0%

Fluent Python Chapter 5

I didn’t view Python as a functional programming language. —— Guido van Rossum

开篇这句Python爸爸的话同时也表明了作者后文所描述的一些感觉: 函数式编程在Python中并不是第一首选。

众所周知的是在Python中,一个函数是可以被当作对象传递的。首先我们可以使用type去查看函数的类型。我们也可以直接给函数换个名字。

1
2
3
4
5
6
7
8
>>> def factorial(n):
... return 1 if n < 2 else n * factorial(n-1)
...
>>> type(factorial)
<class 'function'>
>>> fact = factorial
>>> fact(5)
120

高阶函数(High-Order functions)

定义: A function that takes a function as argument or returns a function as result is a higher-order function.

例如在排序的时候,根据对象长度来排序的方法

1
2
3
>>> fruits = ['strawberry', 'fig', 'apple', 'cherry']
>>> sorted(fruits, key=len)
['fig', 'apple', 'cherry', 'strawberry']

这里len就是作为函数传入到sorted中,sorted 就是一个高阶函数。

有时候我们也用functors.partial 去构造一个高阶函数。

使用更现代的方法替换map, filterreduce

在Python中因为有许多方法,所以map, filterreduce 这些标准方法的使用就少了。

例如使用前面几章提到的 列表推导生成器 的方法可以替代许多mapfilter的使用场景。

1
2
3
4
>>> list(map(fact, range(6)))
[1, 1, 2, 6, 24, 120]
>>> [fact(n) for n in range(6)]
[1, 1, 2, 6, 24, 120]

在Python3中 reduce 被放进了functools中。 下面这里例子是用sum代替。

1
2
3
4
5
6
>>> from functools import reduce
>>> from operator import add
>>> reduce(add ,range(100))
4950
>>> sum(range(100))
4950

使用其它方法替代mapreduce可以起到相同作用,但是可读性更强,并且在某些时候一些内置函数经过优化速度会更快。

匿名函数

感觉Python中的lambda 是被吐槽的比较多的地方, 因为只能写一行。 因此lambda通常用来写一些简单的一行的函数传递。

1
2
3
>>> fruits = ['strawberry', 'fig', 'apple', 'cherry']
>>> sorted(fruits, key = lambda word: word[::-1]) # 按照单词反转后字母顺序来排序
['apple', 'fig', 'strawberry', 'cherry']

通常lambda在Python中用的也比较少, Lundh’s lambda refactoring recipe 也提供了简单的步骤如果去掉lambda函数。

函数自省

在计算机编程中,自省是指这种能力:检查某些事物以确定它是什么、它知道什么以及它能做什么。自省向程序员提供了极大的灵活性和控制力。可以参考这篇文章

最常用的就是用dir方法查看函数对象有哪些属性:

1
>>> dir(factorial)

在Python中可以使用inspect模块, 例如使用inspect查看函数签名:

1
2
3
4
5
6
>>> from inspect import signature
>>> sig = signature(clip)
>>> sig
<inspect.Signature object at 0x...>
>>> str(sig)
'text, max_len = 80)'

书中还展示了一如何用inspect.signature 来绑定函数参数的例子。

函数注解(Function annotations)

在Python3 中添加了可以标注函数参数类型和返回值的特性。

1
2
def clip(text: str, max_len:'int >0' = 80) -> str:
...

但这些并不会在语言层面上做什么处理,只是在函数属性的__annotations__中添加对应的字典

1
2
>>> clip.__annotations__
{'text': <class 'str'>, 'max_len': 'int > 0', 'return': <class 'str'>}

这个特性对IDE和linters的用处可能更大一些,可以做静态检查。

几个用于函数式编程的模块

  1. operator 模块: 主要提供了一些运算符的封装,例如add, mul 等等。
  2. functools模块

后续阅读

Python HOW TO 中关于函数编程的文章

结尾的闲谈

So there you have it: Python is, by design, not a functional language — whatever that means. Python just borrows a few good ideas from functional languages.