这篇博客主要是阅读python之旅 时做的笔记。提取出最主要的知识点,供个人在以后中快速查阅。
高阶函数
在函数式编程中,我们可以将函数当作变量一样自由使用。一个函数接收另一个函数作为参数,这种函数称之为高阶函数(Higher-order Functions)。
def func(g, arr):
return [g(x) for x in arr]
def double(x):
return 2 * x
arr1 = func(double, [1, 2, 3, 4])
map/reduce/filter
map
map(function,sequence)
其实就是对序列的item依次进行function(item),并将结果组成一个List返回。
>>> def square(x):
... return x * x
>>> map(square, [1, 2, 3, 4])
[1, 4, 9, 16]
>>> map(lambda x: x * x, [1, 2, 3, 4]) # 使用 lambda
[1, 4, 9, 16]
python3返回的是迭代器,所以迭代器与list互转用 list()
和iter()
即可.
比如:
# for python3
list(map(str,[1,2]))
reduce
python3加上这个from functools import reduce
reduce(function, sequence[, initial])
解释:先将 sequence 的前两个 item 传给 function,即 function(item1, item2),函数的返回值和 sequence 的下一个 item 再传给 function,即 function(function(item1, item2), item3),如此迭代,直到 sequence 没有元素,如果有 initial,则作为初始值调用。
其实就是迭代。
>>> reduce(lambda x, y: x * y, [1, 2, 3, 4]) # 相当于 ((1 * 2) * 3) * 4
24
filter
filter(function, sequnce)
filter顾名思义就是过滤出符合function的元素,显然会将结果组成一个List/String/Tuple (取决于 sequnce 的类型,python3 统一返回迭代器) 返回。
>>> even_num = list(filter(lambda x: x % 2 == 0, [1, 2, 3, 4, 5, 6]))
>>> even_num
[2, 4, 6]
>>> odd_num = list(filter(lambda x: x % 2, [1, 2, 3, 4, 5, 6]))
>>> odd_num
[1, 3, 5]
>>> filter(lambda x: x < 'g', 'hijack')
'ac' # python2
>>> filter(lambda x: x < 'g', 'hijack')
<filter object at 0x1034b4080> # python3
小结
注意在 python2 和 python3 中,map/reduce/filter 的返回值类型有所不同,python2 返回的是基本数据类型,而 python3 则返回了迭代器;
匿名函数
>>> (lambda x: 2 * x)(8)
16
>>> f = lambda x: 2 * x # 将匿名函数赋给变量 f
>>> f
<function <lambda> at 0x7f835a696578>
>>> f(8)
16
闭包
因为函数也是一个对象,所以定义函数时,再嵌套定义一个函数,并将嵌套的函数返回。而且,该内部函数还引用了外部函数的相关参数和变量。 那么内部函数称为闭包。
梳理一下闭包的定义:
- 函数定义时,内部定义了一个函数
- 返回了该内部函数
- 内部函数还引用了外部函数的相关参数和变量
from math import pow
def make_pow(n):
def inner_func(x): # 嵌套定义了 inner_func
return pow(x, n) # 注意这里引用了外部函数的 n
return inner_func # 返回 inner_func
因为make_pow返回的是函数,因此我们用make_pow构造一个函数。
>>> pow2 = make_pow(2) # pow2 是一个函数,参数 2 是一个自由变量
>>> pow2
<function inner_func at 0x10271faa0>
>>> pow2(6)
36.0
我们还注意到,内部函数 inner_func 引用了外部函数 make_pow 的自由变量 n,这也就意味着,当函数 make_pow 的生命周期结束之后,n 这个变量依然会保存在 inner_func 中,它被 inner_func 所引用。
>>> del make_pow # 删除 make_pow
>>> pow3 = make_pow(3)
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
NameError: name 'make_pow' is not defined
>>> pow2(9) # pow2 仍可正常调用,自由变量 2 仍保存在 pow2 中
81.0
有必要把上面的东西多看几遍,这样就明白了闭包。
闭包的作用
- 闭包的最大特点就是引用了自由变量,即使生成闭包的环境已经释放,闭包仍然存在。
- 闭包在运行时可以有多个实例,即使传入的参数相同。
>>> pow_a = make_pow(2)
>>> pow_b = make_pow(2)
>>> pow_a == pow_b
False
- 用闭包模拟类的实例
from math import sqrt
class Point(object):
def __init__(self, x, y):
self.x, self.y = x, y
def get_distance(self, u, v):
distance = sqrt((self.x - u) ** 2 + (self.y - v) ** 2)
return distance
>>> pt = Point(7, 2) # 创建一个点
>>> pt.get_distance(10, 6) # 求到另一个点的距离
5.0
# 闭包实现
def point(x, y):
def get_distance(u, v):
return sqrt((x - u) ** 2 + (y - v) ** 2)
return get_distance
>>> pt = point(7, 2)
>>> pt(10, 6)
5.0
闭包中要避免引用循环变量,或是后续会发生变化的变量
装饰器
def hello():
return 'hello world'
def makeitalic(func):
def wrapped():
return "<i>" + func() + "</i>"
return wrapped
>>> hello = makeitalic(hello) # 将 hello 函数传给 makeitalic
>>> hello()
'<i>hello world</i>'
此时hello函数的名称不再是hello了。而是makeitalic返回的函数名称wrapped。
像上面的情况,可以动态修改函数(或类)功能的函数就是装饰器。本质上,它是一个高阶函数,以被装饰的函数(比如上面的 hello)为参数,并返回一个包装后的函数(比如上面的 wrapped)给被装饰函数(hello)。
我们可以直接再需要装饰的函数前面加上”@decorate_func”表示该函数被装饰器装饰, 而不用麻烦写
类似hello = makeitalic(hello)的语句。
def makeitalic(func):
def wrapped():
return "<i>" + func() + "</i>"
return wrapped
@makeitalic
def hello():
return 'hello world'
#此时的hello的函数名是wrapped,而且是输出斜体的hello world了。
使用装饰器
@decorator_one
@decorator_two
def func():
pass
#离函数定义近的装饰器首先被调用,其等价于
def func():
pass
func = decorator_one(decorator_two(func))
既然装饰器其实就是高阶函数,那么当然可以带参数。
@decorator(arg1, arg2)
def func():
pass
#等价于:
def func():
pass
func = decorator(arg1, arg2)(func)
修饰的函数带有参数
如果被修饰的函数带有参数,显然,我们要定义一个wrapped函数,其参数也应该与被修饰的函数配套。显然要用到可变参数,当然加上关键字参数就更好了。所以通用的写法:
def makeitalic(func):
def wrapped(*args, **kwargs): #使用"可变参数"和"关键字参数"使得传入的函数不管是什么参数都能满足
ret = func(*args, **kwargs)
return '<i>' + ret + '</i>'
return wrapped
@makeitalic
def hello(name):
return 'hello %s' % name
@makeitalic
def hello2(name1, name2):
return 'hello %s, %s' % (name1, name2)
装饰器带参数
显然,既然装饰器是高阶函数,传入的参数是函数,那么显然还能传入其他参数。
比如要增强函数 hello 的功能,给它的返回加上了标签 …,现在,我们想改用标签 … 或
…
, 把i之类的作为一个参数不就行了。def wrap_in_tag(tag):
def decorator(func):
def wrapped(*args, **kwargs):
ret = func(*args, **kwargs)
return '<' + tag + '>' + ret + '</' + tag + '>'
return wrapped
return decorator #多包了一层
@wrap_in_tag('b')
def hello(name):
return 'hello %s' % name
基于类的装饰器
前面的装饰器是一个函数,也可以将一个装饰器定义成一个类。
无参的类的装饰器
类定义的装饰器有两个方法:
- init(): 它接收一个函数作为参数,也就是被装饰的函数
- call(): 让类对象可以调用。
class Bold(object):
def __init__(self, func):
self.func = func
def __call__(self, *args, **kwargs):
return '<b>' + self.func(*args, **kwargs) + '</b>'
@Bold
def hello(name):
return 'hello %s' % name
>>> hello('world')
'<b>hello world</b>'
带参的装饰器
__init__ 接收参数,而 __call__ 接收 func
class Tag(object):
def __init__(self, tag):
self.tag = tag
def __call__(self, func):
def wrapped(*args, **kwargs):
return "<{tag}>{res}</{tag}>".format(
res=func(*args, **kwargs), tag=self.tag
)
return wrapped
@Tag('b')
def hello(name):
return 'hello %s' % name
解决装饰器的副作用
Python 中的 functools 包提供了一个 wraps 的装饰器:
from functools import wraps
def makeitalic(func):
@wraps(func) # 加上 wraps 装饰器
def wrapped():
return "<i>" + func() + "</i>"
return wrapped
@makeitalic
def hello():
return 'hello world'
>>> hello.__name__
'hello'
partial函数
估计不怎么用得到。。。
partial顾名思义就是部分,作用就是固定函数的部分参数。
from functools import partial
def subtraction(x, y):
return x - y
f = partial(subtraction, 4) # 4 赋给了 x
>>> f(10) # 4 - 10
-6