Fluent Python - Part5 一等函数

在 Python 中,函数是一等对象。那什么是“一等对象”呢?“一等对象”的定义如下:

  • 在运行时创建
  • 能赋值给变量或数据结构中的元素
  • 能作为参数传给函数
  • 能作为函数的返回结果

高阶函数

能接受函数为参数,或者把函数作为结果返回的函数是高阶函数(higher-order function).

匿名函数

lambda 关键字在 Python 表达式内创建匿名函数。

一个例子

>>> fruits = ['strawberry', 'fig', 'apple', 'cherry', 'raspberry', 'banana']
>>> sorted(fruits, key=lambda word: word[::-1])
['banana', 'apple', 'fig', 'raspberry', 'strawberry', 'cherry']

用户定义的可调用类型

只要实现对象的 __call__ 方法,该对象就可以表现得像函数。

一个例子

class MyDemo:
    def __call__(self):
        return "hello world"

a = MyDemo()
print(a())

获取关于参数的信息

使用 imspect 模块来提取函数签名

def clip(text, max_len=80):
    """在max_len前面或后面的第一个空格处截断文本    """
    end = None
    if len(text) > max_len:
        space_before = text.rfind(' ', 0, max_len)
    if space_before >= 0:
        end = space_before
    else:
        space_after = text.rfind(' ', max_len)
        if space_after >= 0:
            end = space_after
        if end is None:  # 没找到空格
            end = len(text)
    return text[:end].rstrip()

from inspect import signature
sig = signature(clip)
print(sig)
print(str(sig))
for name, param in sig.parameters.items():
    print(param.kind, ':', name, '=', param.default)

"""
output:
(text, max_len=80)
(text, max_len=80)
POSITIONAL_OR_KEYWORD : text = 
POSITIONAL_OR_KEYWORD : max_len = 80
"""
  • inspect.signature 函数返回一个 inspect.Signature 对象,它有一个 parameters 属性,这是一个有序映射,把参数名和inspect.Parameter 对象对应起来。各个 Parameter 属性也有自己的属性,例如 name , defaultkind。特殊的inspect._empty值表示没有默认值,考虑到 None 是有效的默认值,这么做是合理的。

  • kind 属性的值是 _ParameterKind 类中的5个值之一,例举如下。

    • POSITIONAL_OR_KEYWORD
      • 可以通过定位参数和关键字参数传入的形参(多数Python函数的参数属于此类)
    • VAR_POSITIONAL
      • 定位参数元组。
    • VAR_KEYWORD
      • 关键字参数字典。
    • KEYWORD_ONLY
      • 仅限关键字参数(Python3 新增)。
    • POSITIONAL_ONLY
      • 仅限定位参数;目前,Python声明函数的句法不支持,但是有些使用C语言实现且不接受关键字参数的函数(如divmod)支持。
  • inspect.Signature 对象有个 bind 方法,它可以把任意个参数绑定到签名中的形参上,所用的规则与实参到形参的匹配方式一样。框架可以使用这个方法在真正调用函数前验证参数。

def clip(text, max_len=80):
    """在max_len前面或后面的第一个空格处截断文本    """
    end = None
    if len(text) > max_len:
        space_before = text.rfind(' ', 0, max_len)
    if space_before >= 0:
        end = space_before
    else:
        space_after = text.rfind(' ', max_len)
        if space_after >= 0:
            end = space_after
        if end is None:  # 没找到空格
            end = len(text)
    return text[:end].rstrip()

from inspect import signature
sig = signature(clip)
my_params = {
     'text': "abcd df", 'max_len': 3}
bound_args = sig.bind(**my_params)
print(bound_args)

for name, value in bound_args.arguments.items():
    print(name, "=", value)

del my_params['text']
bound_args = sig.bind(**my_params)
"""
output:

text = abcd df
max_len = 3
Traceback (most recent call last):
  File "E:/code/python/py_study/test.py", line 28, in 
    bound_args = sig.bind(**my_params)
  File "C:\Users\Administrator\AppData\Local\Programs\Python\Python37\lib\inspect.py", line 3015, in bind
    return args[0]._bind(args[1:], kwargs)
  File "C:\Users\Administrator\AppData\Local\Programs\Python\Python37\lib\inspect.py", line 2930, in _bind
    raise TypeError(msg) from None
TypeError: missing a required argument: 'text'


text = abcd df
max_len = 3
"""

支持函数式编程的包

使用 functools.partial 冻结参数

functools.partial 这个高阶函数用于部分应用一个函数。部分应用是指,基于一个函数创建一个新的可调用对象,把原函数的某些参数固定。使用这个函数可以把接受一个或多个参数的函数改编成需要回调的API,这样参数更少。

一个例子

>>> from operator import mul
>>> from functools import partial
>>> triple = partial(mul, 3)
>>> triple(7)
21
>>> list(map(triple, range(1, 10)))
[3, 6, 9, 12, 15, 18, 21, 24, 27]

又写完一章啦,真希望快点结束这个专题。下一章我们将介绍如何使用一等函数实现设计模式

你可能感兴趣的:(python,python)