返回介绍

函数式编程

发布于 2024-05-30 23:22:17 字数 18989 浏览 0 评论 0 收藏 0

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 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。
列表为空,暂无数据
    我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
    原文