这章主要讲一些基础的函数式编程。
在一门语言中,如果函数可以满足下面几个条件,那么它就是这个语言中的一等公民(first-class object)。"first-class function" 和 "functions as first-class objects" 表达的是同样的意思。
- Created at runtime
- Assigned to a variable or element in a data structure
- Passed as an argument to a function
- Returned as the result of a function
这章的重点是如何获取函数签名信息,并给了 bobo
库作为一个漂亮的例子讲解。
Treating a Function Like an Object
展示了函数被赋值给对象,函数的 __doc__
属性等,以表达函数是个对象。
Higher-Order Functions
即使用函数作为参数的函数。比如内置的 sort
:
>>> fruits = ['strawberry', 'fig', 'apple', 'cherry', 'raspberry', 'banana']
>>> sorted(fruits, key=len)
['fig', 'apple', 'cherry', 'banana', 'raspberry', 'strawberry']
同类函数有 map
, filter
,但这两个函数在灵活性和扩展性上不如 list comprehension,Python 社区已经不推荐。
Anonymous Functions
匿名函数在语法上很受限制,它除了跟高阶函数(Higher-Order Functions)一起搭配使用外,很少有合适的场景:
>>> fruits = ['strawberry', 'fig', 'apple', 'cherry', 'raspberry', 'banana']
>>> sorted(fruits, key=lambda word: word[::-1])
['banana', 'apple', 'fig', 'raspberry', 'strawberry', 'cherry']
The Seven Flavors of Callable Objects
__builtin__.callable()
Checks.
7 Callable types:
- User-defined functions (
def
,lambda
) - Built-in functions (C functions:
len
,time.strftime
) - Built-in methods (
dict.get
) - Methods (functions defined in a class)
- Classes (
point = Point3D()
) - Class instances (a class defines a
__call__
method) - Generator functions (we'll talk about this later)
User-Defined Callable Types
在类中定义一个 __call__
类型,可以使类的实例可以当作函数来调用。书中给了一个很实用的使用场景,可以用作做装饰器(decorator),以保存多次调用间的中间状态。比如可以用来缓存一些复杂运算的结果。
Function Introspection
函数有一些特殊的内部属性,可以被用来观察函数内部。
其中 __defaults__
, __code__
, __annotations__
经常被 IDE 或者一些框架用来提取关于函数签名的信息。
From Positional to Keyword-Only Parameters
Python 3 提供了一个新特性,可以定义一个函数参数,只能作为命名参数被调用。比如:
>>> def f(a, *args, b):
... return a, b
...
>>> f(1, b=2)
(1, 2)
这个例子中,调用时需要给上 b
这个命名参数,不然会报错。如果你不想要使用 a
之后的位置参数,你也可以把 args
省掉:def f(a, *, b):
。
Retrieving Information About Parameters
这是本章的重点。
函数的签名信息存在它的 __defaults__
, __kwdefaults__
, __code__
属性中,但是你不需要自己去解析他们,标准库 inspect
做了这个事情。boto
Web 框架是一个绝佳例子,它利用了参数信息做 HTTP 请求的参数校验:
import bobo
@bobo.query('/')
def hello(person):
return 'Hello %s!' % person
如果你访问 http://localhost:8080/
时不带 person
参数,bobo
会返回 HTTP 403 表示缺少参数。
Function Annotations
Python 3 提供了一套机制,用来给函数参数和返回值增加元信息:
def clip(text:str, max_len:'int > 0'=80) -> str:
# ...
这些信息会存放在函数对象的 __annotations__
中。但是 Python 不会使用这些数据做任何事情,比如参数校验等。这些信息主要被 IDE、静态代码检查器使用。同时 Python 3.5 引入了 Type Hints,给出一个数据规范让 IDE / 其他工具更好地进行代码分析。
inspect
模块可以处理他们。像 bobo
这些 Web 框架,也可以利用这些信息做参数校验。
Packages for Functional Programming
The operator Module
内置的 operator
模块提供了几类函数:
- 表示操作符的函数,比如加法
add_(a, b)
函数 itemgetter
,attrgetter
methodcaller
可以动态调用一个对象的方法,参数也可以预先指定好
>>> from operator import methodcaller
>>> s = 'The time has come'
>>> upcase = methodcaller('upper')
>>> upcase(s)
'THE TIME HAS COME'
>>> hiphenate = methodcaller('replace', ' ', '-')
>>> hiphenate(s)
'The-time-has-come'
Freezing Arguments with functools.partial
functools.partial
的作用跟 methodcaller
类似,区别在于一个对函数动手,一个对方法(类里面的函数)动手。它非常实用:
>>> import unicodedata, functools
>>> nfc = functools.partial(unicodedata.normalize, 'NFC')
>>> s1 = 'café'
>>> s2 = 'cafe\u0301'
>>> s1, s2
('café', 'café')
>>> s1 == s2
False
>>> nfc(s1) == nfc(s2)
True
functools.partialmethod
的作用类似,但是用在类内部方法中。
Furture Reading
Python 之父在设计之初并没有想把 Python 做成一门函数式编程语言,只是他从其他语言借鉴了相应的部分,让他有了一些函数式的特性而已。同时 Python 没有做尾递归优化,可能限制了它在函数式编程上的一些使用。这块内容我还看不懂,后面更深入了解了再回来看看。