首页 > 编程 > Python > 正文

Python基础III---函数式编程

2019-11-10 17:10:52
字体:
来源:转载
供稿:网友

这篇博客主要是阅读python之旅 时做的笔记。提取出最主要的知识点,供个人在以后中快速查阅。

高阶函数

在函数式编程中,我们可以将函数当作变量一样自由使用。一个函数接收另一个函数作为参数,这种函数称之为高阶函数(Higher-order Functions)。

def func(g, arr): return [g(x) for x in arr]def double(x): return 2 * xarr1 = 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]

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) * 424

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 powdef 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_bFalse用闭包模拟类的实例from math import sqrtclass 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@makeitalicdef hello(): return 'hello world'#此时的hello的函数名是wrapped,而且是输出斜体的hello world了。

使用装饰器

@decorator_one@decorator_twodef func(): pass#离函数定义近的装饰器首先被调用,其等价于def func(): passfunc = decorator_one(decorator_two(func))

既然装饰器其实就是高阶函数,那么当然可以带参数。

@decorator(arg1, arg2)def func(): pass#等价于:def func(): passfunc = decorator(arg1, arg2)(func)

修饰的函数带有参数

如果被修饰的函数带有参数,显然,我们要定义一个wrapped函数,其参数也应该与被修饰的函数配套。显然要用到可变参数,当然加上关键字参数就更好了。所以通用的写法:

def makeitalic(func): def wrapped(*args, **kwargs): #使用"可变参数"和"关键字参数"使得传入的函数不管是什么参数都能满足 ret = func(*args, **kwargs) return '<i>' + ret + '</i>' return wrapped@makeitalicdef hello(name): return 'hello %s' % name@makeitalicdef 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>'@Bolddef 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 wrapsdef makeitalic(func): @wraps(func) # 加上 wraps 装饰器 def wrapped(): return "<i>" + func() + "</i>" return wrapped@makeitalicdef hello(): return 'hello world'>>> hello.__name__'hello'

partial函数

估计不怎么用得到。。。 partial顾名思义就是部分,作用就是固定函数的部分参数。

from functools import partialdef subtraction(x, y): return x - yf = partial(subtraction, 4) # 4 赋给了 x>>> f(10) # 4 - 10-6
发表评论 共有条评论
用户名: 密码:
验证码: 匿名发表