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

这篇博客主要是阅读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
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值