Python学习之函数式编程

Python学习目录python

  1. 在Mac下使用Python3
  2. Python学习之数据类型
  3. Python学习之函数
  4. Python学习之高级特性
  5. Python学习之函数式编程
  6. Python学习之模块
  7. Python学习之面向对象编程
  8. Python学习之面向对象高级编程
  9. Python学习之错误调试和测试
  10. Python学习之IO编程
  11. Python学习之进程和线程
  12. Python学习之正则
  13. Python学习之经常使用模块
  14. Python学习之网络编程

练习代码git

​函数式编程就是一种抽象程度很高的编程范式,纯粹的函数式编程语言编写的函数没有变量,所以,任意一个函数,只要输入是肯定的,输出就是肯定的,这种纯函数咱们称之为没有反作用。而容许使用变量的程序设计语言,因为函数内部的变量状态不肯定,一样的输入,可能获得不一样的输出,所以,这种函数是有反作用的。函数式编程的一个特色就是,容许把函数自己做为参数传入另外一个函数,还容许返回一个函数!github

Python对函数式编程提供部分支持。因为Python容许使用变量,所以,Python不是纯函数式编程语言。编程

函数是Python内建支持的一种封装,咱们经过把大段代码拆成函数,经过一层一层的函数调用,就能够把复杂任务分解成简单的任务,这种分解能够称之为面向过程的程序设计。函数就是面向过程的程序设计的基本单元。网络

​而函数式编程(请注意多了一个“式”字)——Functional Programming,虽然也能够归结到面向过程的程序设计,但其思想更接近数学计算。闭包

咱们首先要搞明白计算机(Computer)和计算(Compute)的概念。在计算机的层次上,CPU执行的是加减乘除的指令代码,以及各类条件判断和跳转指令,因此,汇编语言是最贴近计算机的语言。而计算则指数学意义上的计算,越是抽象的计算,离计算机硬件越远。对应到编程语言,就是越低级的语言,越贴近计算机,抽象程度低,执行效率高,好比C语言;越高级的语言,越贴近计算,抽象程度高,执行效率低,好比Lisp语言。app

高阶函数

在python中函数名是指向函数的变量,当函数的参数也是函数的时候,这种函数咱们称之为高阶函数。编程语言

def add(x, y, f):
    return f(x) + f(y)
add(1, -6, abs)
复制代码

map/reduce

map()函数接收两个参数,一个是函数,一个是Iterable,map将传入的函数依次做用到序列的每一个元素,并把结果做为新的Iterator返回。函数式编程

def f(x):
    return x * x
r = map(f, [1, 2, 3, 4, 5, 6, 7, 8, 9])
print(list(r))

>>> [1, 4, 9, 16, 25, 36, 49, 64, 81]
复制代码

reduce把一个函数做用在一个序列[x1, x2, x3, ...]上,这个函数必须接收两个参数,reduce把结果继续和序列的下一个元素作累积计算,效果以下:函数

reduce(f, [x1, x2, x3, x4]) = f(f(f(x1, x2), x3), x4)
复制代码

结合map和reduce能够整理出一个str2int的函数:

from functools import reduce

DIGITS = {'0': 0, '1': 1, '2': 2, '3': 3, '4': 4, '5': 5, '6': 6, '7': 7, '8': 8, '9': 9}

def char2num(s):
    return DIGITS[s]

def str2int(s):
    return reduce(lambda x, y: x * 10 + y, map(char2num, s))
复制代码

filter

Python内建的filter()函数用于过滤序列。

map()相似,filter()也接收一个函数和一个序列。和map()不一样的是,filter()把传入的函数依次做用于每一个元素,而后根据返回值是True仍是False决定保留仍是丢弃该元素。

例如,在一个list中,删掉偶数,只保留奇数,能够这么写:

def is_odd(n):
    return n % 2 == 1

list(filter(is_odd, [1, 2, 4, 5, 6, 9, 10, 15]))
# 结果: [1, 5, 9, 15]
复制代码

sorted

对list进行排序:

>>> sorted([36, 5, -12, 9, -21])
[-21, -12, 5, 9, 36]
复制代码

此外,sorted()函数也是一个高阶函数,它还能够接收一个key函数来实现自定义的排序,例如按绝对值大小排序:

>>> sorted([36, 5, -12, 9, -21], key=abs)
[5, 9, -12, -21, 36]
复制代码

字符串排序:

>>> sorted(['bob', 'about', 'Zoo', 'Credit'])
['Credit', 'Zoo', 'about', 'bob']
复制代码

忽略大小写:

>>> sorted(['bob', 'about', 'Zoo', 'Credit'], key=str.lower)
['about', 'bob', 'Credit', 'Zoo']
复制代码

要进行反向排序,没必要改动key函数,能够传入第三个参数reverse=True

>>> sorted(['bob', 'about', 'Zoo', 'Credit'], key=str.lower, reverse=True)
['Zoo', 'Credit', 'bob', 'about']
复制代码

返回函数

定义

高阶函数除了能够接受函数做为参数外,还能够把函数做为结果值返回。

咱们来实现一个可变参数的求和。一般状况下,求和的函数是这样定义的:

def calc_sum(*args):
    ax = 0
    for n in args:
        ax = ax + n
    return ax
复制代码

可是,若是不须要马上求和,而是在后面的代码中,根据须要再计算怎么办?能够不返回求和的结果,而是返回求和的函数:

def lazy_sum(*args):
    def sum():
        ax = 0
        for n in args:
            ax = ax + n
        return ax
    return sum
复制代码

当咱们调用lazy_sum()时,返回的并非求和结果,而是求和函数:

>>> f = lazy_sum(1, 3, 5, 7, 9)
>>> f
<function lazy_sum.<locals>.sum at 0x101c6ed90>
复制代码

调用函数f时,才真正计算求和的结果

>>> f()
25
复制代码

闭包

如上所示的例子中,咱们在函数lazy_sum中又定义了函数sum,而且,内部函数sum能够引用外部函数lazy_sum的参数和局部变量,当lazy_sum返回函数sum时,相关参数和变量都保存在返回的函数中,这种称为“闭包(Closure)”。

注意点:返回函数不要引用任何循环变量,或者后续会发生变化的变量。

以下所示:

def count():
    fs = []
    for i in range(1, 4):
        def f():
             return i*i
        fs.append(f)
    return fs

f1, f2, f3 = count()
复制代码

返回结果以下:

>>> f1()
9
>>> f2()
9
>>> f3()
9
复制代码

匿名函数

关键字lambda表示匿名函数,冒号前面的x表示函数参数。匿名函数有个限制,就是只能有一个表达式,不用写return,返回值就是该表达式的结果。

>>> list(map(lambda x: x * x, [1, 2, 3, 4, 5, 6, 7, 8, 9]))
[1, 4, 9, 16, 25, 36, 49, 64, 81]
复制代码

装饰器

代码运行期间动态增长功能的方式,称之为“装饰器”(Decorator)。本质上,decorator就是一个返回函数的高阶函数。

定义一个能打印日志的decorator,能够定义以下:

def log(func):
    def wrapper(*args, **kw):
        print('call %s():' % func.__name__)
        return func(*args, **kw)
    return wrapper
复制代码

观察上面的log,由于它是一个decorator,因此接受一个函数做为参数,并返回一个函数。咱们要借助Python的@语法,把decorator置于函数的定义处:

@log
def now():
    print('2015-3-25')

>>> now()
call now():
2015-3-25
复制代码

@log放到now()函数的定义处,至关于执行了语句:now = log(now),now变量指向了新的函数。

若是decorator自己须要传入参数,那就须要编写一个返回decorator的高阶函数,写出来会更复杂。好比,要自定义log的文本:

def log(text):
    def decorator(func):
        def wrapper(*args, **kw):
            print('%s %s():' % (text, func.__name__))
            return func(*args, **kw)
        return wrapper
    return decorator
复制代码

这个3层嵌套的decorator用法以下:

@log('execute')
def now():
    print('2015-3-25')
    
>>> now()
execute now():
2015-3-25
复制代码

和两层嵌套的decorator相比,3层嵌套的效果是这样的:>>> now = log('execute')(now)

有个问题,通过decorator装饰以后的函数,它们的__name__已经从原来的'now'变成了'wrapper'

>>> now.__name__
'wrapper'
复制代码

Python内置的functools.wraps用来复制函数_name_属性的:

import functools

def log(func):
 @functools.wraps(func)
    def wrapper(*args, **kw):
        print('call %s():' % func.__name__)
        return func(*args, **kw)
    return wrapper
复制代码

或者针对带参数的装饰器:

import functools

def log(text):
    def decorator(func):
 @functools.wraps(func)
        def wrapper(*args, **kw):
            print('%s %s():' % (text, func.__name__))
            return func(*args, **kw)
        return wrapper
    return decorator
复制代码

偏函数

Python的functools模块提供了不少有用的功能,其中一个就是偏函数(Partial function)。把一个函数的某些参数给固定住(也就是设置默认值),返回一个新的函数,调用这个新函数会更简单。

建立偏函数时,实际上能够接收函数对象、*args**kw这3个参数。

好比要使用int()函数去转换二进制字符串,本来的写法是:int(x, base=2),若是量大的话,很麻烦,因此咱们能够这么写:

>>> import functools
>>> int2 = functools.partial(int, base=2)
>>> int2('1000000')
64
>>> int2('1010101')
85
复制代码

上面的in2 = functools.partial(int, base=2)至关于:

kw = { 'base': 2 }
int('10010', **kw)
复制代码

下一篇:Python学习之模块

相关文章
相关标签/搜索