1

流畅的python--第七章

把函数视为对象

在python中,函数是一等对象。编程语言研究人员把“一等对象”定义为满足以下条件的程序实体:

  • 在运行时创建;
  • 能赋值给变量或数据结构中的元素;
  • 能作为参数传给函数;
  • 能作为函数的返回结果。
    示例7-1 创建并测试一个函数,读取函数的__doc__属性,再检查函数的类型

🚩 __doc__属性用于生成对象的帮助文本.

示例7-2 通过其他名称使用factorial函数,再把factorial函数作为参数传递

🚩 示例7-2展示了函数对象的“一等”本性。可以把factorial函数赋值给变量fact,然后通过变量名调用。还可以把factorial函数作为参数传给map函数。map(function, iterable)调用会返回一个可迭代对象,所含的项是把第一个参数(一个函数)应用到第二个参数(一个可迭代对象,这里是range(11))中各个元素上得到的结果。

高阶函数

接受函数为参数或者把函数作为结果返回的函数是高阶函数(higher-oder function)。示例7-2中的map函数就是一例。此外,内置函数sorted也是:通过可选的key参数提供一个函数,应用到每一项上进行排序。

示例7-3 根据单词长度排序一个列表

🚩 任何单函数都可以作为key参数的值。例如,为了创建押韵字典,可以把各个单词反过来拼写,然后排序。

示例7-4 根据反向拼写排序一个单词列表

🚩 在函数式编程范式中,最为人熟知的高阶函数有map、filter、reduce和apply(python3已经废弃),可以编写fn(*args, **kwargs).

mapfilterreduce的现代替代品。函数式语言通常会提供mapfilterreduce这3个高阶函数,在python3中,map和filter还是内置函数,但是由于引入了列表推导式和生成器表达式,因此二者就变得没那么重要了。列表推导式或生成器表达式兼具mapfilter这两个函数的功能,而且代码可读性更高。
示例7-5 计算阶乘列表:mapfilter与列表推导式对比

示例7-6 使用reducesum计算0——99的整数之和

🚩 sum和reduce的整体运作方式是一样的,即把某个操作连续应用到序列中的项上,累计前一个结果,把一系列值规约成一个值

内置的规约函数还有allany

  • all(iterable) iterable中没有表示假值的元素时返回Trueall([])返回True
  • any(iterable) 只要iterable中有元素是真值就返回Trueany([])返回False

匿名函数

lambda关键字使用python表达式创建匿名函数。然而,受python简单的句法限制,lambda函数的主体只能是纯粹的表达式,不能有whiletry等其他语句。赋值语句=也不能出现在lambda函数的主体中,但是可以有海象运算符:=,但是这种语句太过复杂,可读性差,因此建议重构。
示例7-7使用lambda表达式反转拼写,然后依次给单词列表排序。

9种可调用对象

除了函数,调用运算符(())还可以应用到其他对象上,如果想判断对象能否调用,可以使用内置的callable()函数。

  • 用户定义的函数
    使用def语句或lambda表达式创建的函数
  • 内置函数
    使用C语言(Cpython)实现的函数,例如lentime.strftime
  • 内置方法
    使用C语言实现的方法,例如 dict.get
  • 方法
    在类主体中定义的函数

  • 调用类时运行类的 __new__ 方法创建一个实例,然后运行
    __init__ 方法,初始化实例,最后再把实例返回给调用方。Python
    没有 new 运算符,调用类就相当于调用函数
  • 类的实例
    如果类定义了 __call__ 方法,那么它的实例可以作为函数调用。
  • 生成器函数
    主体中有 yield 关键字的函数或方法。调用生成器函数返回一个
    生成器对象
  • 原生协程函数
    使用 async def 定义的函数或方法。调用原生协程函数返回一个
    协程对象。
  • 异步生成器函数
    使用 async def定义,而且主体中有 yield 关键字的函数或方
    法。调用异步生成器函数返回一个异步生成器,供 async for 使用。

用户定义的可调用类型

不仅 Python 函数是真正的对象,而且任何 Python 对象都可以表现得像
函数。为此,只需实现实例方法 __call__
示例7-8 bingocall.py:调用BingoCage实例,从打乱顺序的列表中取出一个元素

🚩 bingo实例可以作为函数调用,而且内置函数callable()判定它是可调用对象。

从位置参数到仅限关键字参数

Python 函数最好的功能之一是提供了极为灵活的参数处理机制。与之密
切相关的是,调用函数时可以使用 * ** 拆包可迭代对象,映射各个
参数。
示例7-9 tag函数用于生成HTML标签。可以使用名为class_的仅限关键字参数传入“class”属性,这是一种变通方法,因为“class”python中的关键字。

在示例7-9中,class_参数只能通过关键字参数指定,它一定不会捕获无名位置参数。定义函数时,如果想指定仅限关键字参数,就要把它们放到前面有的参数后面。如果不想支持数量不定的位置参数,但是想支持仅限关键字参数,则可以在签名中放一个

注意,仅限关键字参数不一定要有默认值,可以像上例中的 b 那样,强
制要求传入实参。

仅限位置参数

python3.8开始,用户定义的函数签名可以指定仅限位置参数。内置函数都是如此。例如
divmod(a, b)只能使用位置参数调用,不能写成divmod(a=10, b=4)。如果想定义只接受位置参数的函数,则可以在参数列表中使用/

def divmod(a, b, /):
  return (a//b, a%b)

/左边均是仅限位置参数。在'/'后面,可以指定其他参数,处理方式一同往常。

支持函数式编程的包

Python 的目标不是变成函数式编程语言,但是得
益于一等函数、模式匹配,以及 operatorfunctools 等包的支
持,其对函数式编程风格也可以“信手拈来”。

  • operator模块
    在函数式编程中,经常需要把算术运算符当作函数使用。例如,不使用
    递归计算阶乘。求和可以使用 sum 函数,求积则没有这样的函数。可以
    使用 reduce 函数(参见“mapfilterreduce 的现代替代品”一
    节),但是需要一个函数来计算序列中两项之积。
    示例 7-13 使用itemgetter排序一个元组列表

    示例7-14使用attrgetter处理前文定义的具名元组

  • 使用functools.partial冻结参数
    functools 模块提供了一系列高阶函数,比如 7.3 节mapfilter
    reduce 的现代替代品”中用过的 reduce。另外一个值得关注的函数是
    partial,它可以根据提供的可调用对象产生一个新可调用对象,为原
    可调用对象的某些参数绑定预定的值。使用这个函数可以把接受一个或
    多个参数的函数改造成需要更少参数的回调的 API
    示例7-16 使用partial 把一个双参数函数改造成只需要一个参
    数的可调用对象

    示例7-17 使用partial构建一个便利的Unicode规范化函数

posted @ 2024-06-05 17:45  Bonne_chance  阅读(6)  评论(0编辑  收藏  举报
1