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

来源:互联网 发布:mac百度搜索推荐 编辑:程序博客网 时间:2024/05/17 00:50

这篇博客主要是阅读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
0 0