代码整洁之道笔记-把函数视作对象

参考:《代码整洁之道》

一等对象

  • 在运行时创建

  • 能赋值给变量或数据结构中的元素

  • 能作为参数传给函数

  • 能作为函数的返回结果

高阶函数

接受函数为参数,或者把函数作为结果返回的函数是高阶函数

map 函数就是一例
在这里插入图片描述

在这里插入图片描述

filter

filter(lambda n: n % 2, range(6))
#<filter object at 0x000001FE71685A58>
list(filter(lambda n: n % 2, range(6)))
#  1 3 5

reduce

此外,内置函 数 sorted 也是:可选的 key 参数用于提供一个函数,它会应用到各个 元素上进行排序
在这里插入图片描述
根据反向拼写给一个单词列表排序
在这里插入图片描述

如果想使用不定量的参 数调用函数,可以编写 fn(*args, **keywords).

由于引入了列表推导和生成器表达式,map、filter 和 reduce 三个高阶函数变得没那么重要了
在这里插入图片描述
map 和 filter 返回生成器

使用 reduce 和 sum 计算 0~99 之和

from functools import reduce
from operator import add
# 4950
 reduce(add, range(100))
sum(range(100))

sum 和 reduce 的通用思想是把某个操作连续应用到序列的元素上,累 计之前的结果,把一系列值归约成一个值。

匿名函数

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

在参数列表中最适合使用匿名函数。

使用 lambda 表达式反转拼写,然后依此给单词列表排 序
在这里插入图片描述
除了作为参数传给高阶函数之外,Python 很少使用匿名函数。lambda 句法只是语法糖:与 def 语句一样,lambda 表达式会创建函 数对象。

可调用对象

除了用户定义的函数,调用运算符(即 ())还可以应用到其他对象 上。如果想判断对象能否调用,可以使用内置的 callable() 函数。
在这里插入图片描述

Python 数据模型文档列出了 7 种可调用对象。

  1. 用户定义的函数 使用 def 语句或 lambda 表达式创建。
  2. 内置函数 使用 C 语言(CPython)实现的函数,如 len 或 time.strftime。
  3. 内置方法 使用 C 语言实现的方法,如 dict.get。
  4. 方法 在类的定义体中定义的函数
  5. 类 调用类时会运行类的 new 方法创建一个实例,然后运行 init 方法,初始化实例,最后把实例返回给调用方。因为 Python 没有 new 运算符,所以调用类相当于调用函数。(通常,调用类会创建 那个类的实例,不过覆盖 new 方法的话,也可能出现其他行为。 19.1.3 节会见到一个例子。)
  6. 类的实例 如果类定义了 call 方法,那么它的实例可以作为函数调用
  7. 生成器函数
    使用 yield 关键字的函数或方法。调用生成器函数返回的是生成 器对象

用户定义的可调用类型

在这里插入图片描述
bingo 实例可以作为函 数调用,而且内置的 callable(…) 函数判定它是可调用的对象

在这里插入图片描述
实现 call 方法的类是创建函数类对象的简便方式,此时必须在内 部维护一个状态,让它在调用之间可用,例如 BingoCage 中的剩余元 素。装饰器就是这样。装饰器必须是函数,而且有时要在多次调用之 间“记住”某些事 [ 例如备忘(memoization),即缓存消耗大的计算结 果,供后面使用 ]。 创建保有内部状态的函数,还有一种截然不同的方式——使用闭包。

函数内省

除了 doc,函数对象还有很多属性。使用 dir 函数可以探知 factorial 具有下述属性:
在这里插入图片描述

本节讨论与把函数视作对象相关 的几个属性,先从 dict 开始。

与用户定义的常规类一样,函数使用 dict 属性存储赋予它的用户 属性。这相当于一种基本形式的注解。

列出常规对象没有而函数有的属性
在这里插入图片描述

用户定义的函数的属性

在这里插入图片描述

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

Python 最好的特性之一是提供了极为灵活的参数处理机制,而且 Python 3 进一步提供了仅限关键字参数(keyword-only argument)。与之密切相 关的是,调用函数时使用 * 和 **“展开”可迭代对象,映射到单个参 数。

定义函数时若想指定仅限关键字参数,要把它们放到前面有 * 的参数后面。如果不 想支持数量不定的定位参数,但是想支持仅限关键字参数,在签名中放 一个 *,如下所示:
在这里插入图片描述
tag 函数用于生成 HTML标签;使用名为 cls 的关键 字参数传入“class”属性,这是一种变通方法,因为“ class”是 Python 的关键字
·
在这里插入图片描述

获取关于参数的信息

Bobo 知道 hello 需要 person 参数,并且从 HTTP 请 求中获取它
在这里插入图片描述
bobo.query 装饰器把一个普通的函数(如 hello)与框架的请求处理 机制集成起来了。这 里的关键是,Bobo 会内省 hello 函数,发现它需要一个名为 person 的参数,然后从请求中获取那个名称对应的参数,将其传给 hello 函 数,因此程序员根本不用触碰请求对象。

安装 Bobo,然后启动开发服务器,执行的脚本(例 如,bobo -f hello.py)。访问 http://localhost:8080/ 看到的 消息是“Missing form variable person”,HTTP 状态码是 403。这是因为, Bobo 知道调用 hello 函数必须传入 person 参数,但是在请求中找不 到同名参数。

如果请求中缺少函数的参数,Bobo 返回 403 forbidden 响应;curl -i 的作用是把首部转储到标准输出
在这里插入图片描述
如果访问 http://localhost:8080/?person=Jim,响应会变 成字符串 ‘Hello Jim!’
在这里插入图片描述
Bobo 是怎么知道函数需要哪个参数的呢?它又是怎么知道参数有没有 默认值呢?

函数对象有个 defaults 属性,它的值是一个元组,里面保存着定 位参数和关键字参数的默认值。仅限关键字参数的默认值在 kwdefaults 属性中。然而,参数的名称在 code 属性中,它 的值是一个 code 对象引用,自身也有很多属性。有更好的方式——使用 inspect 模块。

提取函数的签名

在这里插入图片描述

这样就好多了。inspect.signature 函数返回一个 inspect.Signature 对象,它有一个 parameters 属性,这是一个有 序映射,把参数名和 inspect.Parameter 对象对应起来。各个 Parameter 属性也有自己的属性,例如 name、default 和 kind。特殊 的 inspect._empty 值表示没有默认值,考虑到 None 是有效的默认值 (也经常这么做),而且这么做是合理的。

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

POSITIONAL_OR_KEYWORD 可以通过定位参数和关键字参数传入的形参(多数 Python 函数的参 数属于此类)。

VAR_POSITIONAL 定位参数元组。

VAR_KEYWORD 关键字参数字典。

KEYWORD_ONLY 仅限关键字参数(Python 3 新增)。

POSITIONAL_ONLY 仅限定位参数;目前,Python 声明函数的句法不支持,但是有些使 用 C 语言实现且不接受关键字参数的函数(如 divmod)支持。

inspect.Signature 对象有个 bind 方法,它可以把任意个参数绑定 到签名中的形参上,所用的规则与实参到形参的匹配方式一样。框架可 以使用这个方法在真正调用函数前验证参数
在这里插入图片描述
在这里插入图片描述

这个示例在 inspect 模块的帮助下,展示了 Python 数据模型把实参绑 定给函数调用中的形参的机制,这与解释器使用的机制相同。

函数注解

Python 3 提供了一种句法,用于为函数声明中的参数和返回值附加元数 据。示例 5-19 是示例 5-15 添加注解后的版本,二者唯一的区别在第一 行

在这里插入图片描述

函数声明中的各个参数可以在 : 之后增加注解表达式。如果参数有默认 值,注解放在参数名和 = 号之间。如果想注解返回值,在 ) 和函数声明 末尾的 : 之间添加 -> 和一个表达式。那个表达式可以是任何类型。注 解中最常用的类型是类(如 str 或 int)和字符串(如 ‘int > 0’)。在示例 5-19 中,max_len 参数的注解用的是字符串。

注解不会做任何处理,只是存储在函数的 annotations 属性(一 个字典)中

在这里插入图片描述
‘return’ 键保存的是返回值注解,即示例 5-19 中函数声明里以 -> 标 记的部分。 Python 对注解所做的唯一的事情是,把它们存储在函数的 annotations 属性里。仅此而已,Python 不做检查、不做强制、 不做验证,什么操作都不做。换句话说,注解对 Python 解释器没有任何 意义。注解只是元数据,可以供 IDE、框架和装饰器等工具使用。写作 本书时,标准库中还没有什么会用到这些元数据,唯有 inspect.signature() 函数知道怎么提取注解

在这里插入图片描述
在这里插入图片描述

支持函数式编程的包

operator模块

在函数式编程中,经常需要把算术运算符当作函数使用。例如,不使用 递归计算阶乘。求和可以使用 sum 函数,但是求积则没有这样的函数。 我们可以使用 reduce 函数(5.2.1 节是这么做的),但是需要一个函数 计算序列中两个元素之积。示例 5-21 展示如何使用 lambda 表达式解决 这个问题

operator 模块中还有一类函数,能替代从序列中取出元素或读取对象 属性的 lambda 表达式:因此,itemgetter 和 attrgetter 其实会自
行构建函数。

示例 5-23 展示了 itemgetter 的常见用途:根据元组的某个字段给元 组列表排序。在这个示例中,按照国家代码(第 2 个字段)的顺序打印 各个城市的信息。其实,itemgetter(1) 的作用与 lambda fields: fields[1] 一样:创建一个接受集合的函数,返回索引位 1 上的元素。

itemgetter 使用 [] 运算符,因此它不仅支持序列,还支持映射和任
何实现 getitem 方法的类。 attrgetter 与 itemgetter 作用类似,它创建的函数根据名称提取对 象的属性。如果把多个属性名传给 attrgetter,它也会返回提取的值 构成的元组。此外,如果参数名中包含 .(点号),attrgetter 会深 入嵌套对象,获取指定的属性。这些行为如示例 5-24 所示。这个控制 台会话不短,因为我们要构建一个嵌套结构,这样才能展示 attrgetter 如何处理包含点号的属性名。

使用functools.partial冻结参数

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

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值