当前位置: 代码迷 >> 综合 >> Fluent Python - Part5 一等函数
  详细解决方案

Fluent Python - Part5 一等函数

热度:61   发布时间:2023-10-21 05:26:05.0

在 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 = Noneif len(text) > max_len:space_before = text.rfind(' ', 0, max_len)if space_before >= 0:end = space_beforeelse:space_after = text.rfind(' ', max_len)if space_after >= 0:end = space_afterif 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 = <class 'inspect._empty'> 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 = Noneif len(text) > max_len:space_before = text.rfind(' ', 0, max_len)if space_before >= 0:end = space_beforeelse:space_after = text.rfind(' ', max_len)if space_after >= 0:end = space_afterif 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: <BoundArguments (text='abcd df', max_len=3)> text = abcd df max_len = 3 Traceback (most recent call last):File "E:/code/python/py_study/test.py", line 28, in <module>bound_args = sig.bind(**my_params)File "C:\Users\Administrator\AppData\Local\Programs\Python\Python37\lib\inspect.py", line 3015, in bindreturn args[0]._bind(args[1:], kwargs)File "C:\Users\Administrator\AppData\Local\Programs\Python\Python37\lib\inspect.py", line 2930, in _bindraise TypeError(msg) from None TypeError: missing a required argument: 'text'<BoundArguments (text='abcd df', max_len=3)> 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]

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