函数式编程
Python 对函数式编程提供部分支持。由于 Python 允许使用变量,因此,Python 不是纯函数式编程语言。
以函数为单元把复杂任务分解,这种分解称之为面向过程编程。
函数式编程(Functional Programming)虽然也可以归结到面向过程的程序设计,但其思想更接近数学计算。它是一种抽象程度很高的编程范式,允许把函数本身作为参数传入另一个函数,还允许返回一个函数。
纯粹的函数式编程语言编写的函数没有变量,因此,任意一个函数,只要输入是确定的,输出就是确定的,这种纯函数我们称之为没有副作用。
而允许使用变量的程序设计语言,由于函数内部的变量状态不确定,同样的输入,可能得到不同的输出,因此,这种函数是有副作用的。
高阶函数
一个函数就可以接收另一个函数作为参数,这种函数就称之为高阶函数(Higher-order function)。函数式编程就是指这种高度抽象的编程范式。
map
函数原型:map(func, *iterables) --> map object
map()函数接收两个参数,一个是函数func,另一个是可变参数(iterables是一个tuple),可以是1个或多个Iterable对象(数目对应func的形参数)。
map 将序列的每个元素依次传入到函数,并生成一个map对象(是一个Iterator对象,一个惰性序列)。 计算等价于:
map(f, [x1,x2,...,xn], [y1,y2,...,yn], ...)
=>
[f(x1,y1,...), f(x2,y2,...), ..., f(xn,yn,...)]
举例:
>>> def f(x):
... return x * x
...
>>> r = map(f, [1, 2, 3, 4, 5, 6, 7, 8, 9])
>>> list(r)
[1, 4, 9, 16, 25, 36, 49, 64, 81]
模拟map的实现:
def mymap(func, *iterables):
res = []
for args in zip(*iterables):
res.append(func(*args))
return res
验证:
def sum(x,y):
return x+y
print(mymap(sum, [1,2,3], [4,5,6])) #输出: [5,7,9]
分析:
1)打包参数,构成元组iterables为([1,2,3], [4,5,6])
2)*iterables
解包参数传入zip([1,2,3], [4,5,6])
3)遍历会得到一系列的tunple并交给args,即(1,2) (2,5) (3,6)
4)*args
解包元组并传入func,即func(1,2) func(2,5) func(3,6)
reduce
需要导入:from functools import reduce
函数原型:reduce(func, sequence) --> value
reduce()函数接收两个参数,一个是函数func,另一个是序列。返回结果为函数计算值。
reduce 把结果继续和序列的下一个元素做累积计算.
计算等价于:
reduce(f, [x1, x2, x3, x4]) = f(f(f(x1, x2), x3), x4)
filter
函数原型:filter(function, iterable) --> filter object
filter把传入的函数依次作用于每个元素,然后根据返回值是 True 还是 False 决定保留还是丢弃该元素。
结果返回一个filter对象(是一个Iterator对象,一个惰性序列)。
举例:
def is_odd(n):
return n % 2 == 1
list(filter(is_odd, [1, 2, 4, 5, 6, 9, 10, 15])) # 结果: [1, 5, 9, 15]
注意到 filter()
函数返回的是一个 Iterator,也就是一个惰性序列,所以要强迫 filter()
完成计算结果,需要用 list()
函数获得所有结果并返回 list。
[小结] filter()的作用是从一个序列中筛出符合条件的元素。由于 filter()使用了惰性计算,所以只有在取 filter()结果的时候,才会真正筛选并每次返回下一个筛出的元素。
sorted
函数原型:sorted(iterable, key=None, reverse=False) --> new sorted list
可以对一个iterable对象排序,并返回一个list对象。
key 指定的函数将作用于 iterable 的每一个元素上,并根据 key 函数返回的结果进行排序(排序遵循一般的比较原则,数字直接比较,字母比较ASCII值等等)。
reverse表示是否需要将排序结果倒过来。
>>> sorted(['bob', 'about', 'Zoo', 'Credit'], key=str.lower,reverse=True)
['Zoo', 'Credit', 'bob', 'about']
这里,key指向的函数将元素变成小写返回,然后sorted对小写的结果进行排序。
闭包
一个函数和它的环境变量合在一起,就构成了一个闭包。在Python中,所谓闭包是一个包含有环境变量取值的函数对象。 换句话说,闭包是内层函数引用了外层函数的变量(包括它的参数),然后返回内层函数的情况。
当一个函数A内部定义了函数B,函数B引用了A的局部变量,当A将函数B作为返回值时,相关参数和变量都保存在返回的函数中,这种称为“闭包”。
def A(x):
def B(y):
xx = x + y
return xx
return B #return a function object
当我们调用 A()时,返回的是一个函数对象,调用函数f时,才真正计算结果:
>>> f = A(1)
>>> f
<function A.<locals>.B at 0x0000000000B57268>
>>> f(2)
3
当我们调用A时,每次调用都会返回一个新的函数对象,即使传入相同的参数:
>>> f1 = A(1)
>>> f2 = A(1)
>>> f1==f2 #f1()和 f2()的调用结果互不影响
False
[注意] 返回一个函数时,牢记该函数并未立刻执行,而是直到调用时才执行;返回函数中不要引用任何可能会变化的变量(任何循环变量, 或者后续会发生变化的变量。)。
装饰器
假设我们要增强 now()函数的功能,比如,在函数调用前后自动打印日志,但又不希望修改 now()函数的定义,这种在代码运行期间动态增加功能的方式,称之为“装饰器”( Decorator)。
本质上, decorator 就是一个返回函数的高阶函数。
举例:
def log(func):
def wrapper(*args, **kw):
print('begin call')
ret = func(*args, **kw)
print('end call')
return ret
return wrapper
#分析:now = log(now) = wrapper
@log
def now():
print('hello...')
now()
print(now.__name__) #输出wrapper
分析:
把@log 放到 now()函数的定义处,相当于执行了语句:
now = log(now)
由于 log()是一个 decorator,返回一个函数,所以,原来的 now()函数仍然存在,只是现在同名的 now 变量指向了新的函数(同名的变量优先被使用),于是调用 now()将执行新函数,即在 log()函数中返回的 wrapper()函数。
如果 decorator 本身需要传入参数,那就需要编写一个返回 decorator 的高阶函数(嵌套一层decorator)。
举例:
def log(text):
def decorator(func):
def wrapper(*args, **kw):
print(text)
print('begin call')
ret = func(*args, **kw)
print('end call')
return ret
return wrapper
return decorator
#分析:now = log('erick')(now) = decorator(now) = wrapper
@log('erick')
def now():
print('hello...')
now()
print(now.__name__) #输出wrapper
以上两种 decorator 的定义都没有问题,但还差最后一步。因为我们讲了函数也是对象,它有__name__
等属性,但你去看经过 decorator 装饰之后的函数,它们的__name__
已经从原来的'now'变成了'wrapper'。所以,需要把原始函数的__name__
等属性复制到 wrapper()函数中,否则,有些依赖函数签名的代码执行就会出错。
不需要编写 wrapper.__name__ = func.__name__
这样的代码, Python 内置的 functools.wraps
就是干这个事的,所以,一个完整的 decorator 的写法如下:
import functools
def log(func):
@functools.wraps(func) #增加这一行
def wrapper(*args, **kw):
print('begin call')
ret = func(*args, **kw)
print('end call')
return ret
return wrapper
偏函数
需要导入:from functools import partial
偏函数是某一个函数带有固定参数的实现。
partial 的作用就是,把一个函数的某些参数给固定住(也就是设置默认值),返回一个新的函数,调用这个新函数会更简单。
参数位置问题:对于无关键字参数,位置在最左边;对于关键字参数,调用时会给出形参名,所以位置不用管。
例1:
def sum(x,y,z):
print(x,y,z)
return x+y+z
newsum = partial(sum, 1,2) #newsum(z)等价于sum(1,2,z)
print(newsum(3))
例2:
def mymod(x, y=2):
return x % y
newmymod = partial(mymod, y=8) #newmymod(x)等价于mymod(x, y=8)
print(newmymod(9))
模拟实现:
def mypartial(func, *args, **keywords):
def newfunc(*fargs, **fkeywords):
newkeywords = keywords.copy()
newkeywords.update(fkeywords) #合并字典
return func(*(args + fargs), **newkeywords) #参数合并后传给func,注意args在最左边
return newfunc #返回一个闭包
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论