python 一等函数
一等函数
首先要明确一等对象的概念,一等对象是一个编程语言里的概念,满足以下条件:
- 在运行时创建
- 能赋值给变量或者是数据结构中的元素
- 能作为参数传给函数
- 能作为函数的返回结果
在python中,常常把“函数视作一等对象”称为“一等函数”。
下例是把函数视为对象的一个例子:
def foo(n):
return n
tmp = foo
tmp(1)
高阶函数
接受函数为参数或者把函数作为返回结果的就是高阶函数。sorted是一个例子:
def reverse(word):
return word[::-1]
sorted(tmp, key=reverse)
上例的意思是按照reverse后的字典序给tmp这个列表排序。
在python3中比较常见的高阶函数还有map, filter, reduce,但是在python3中一般存在更好的方式替换这三种函数。
# map
list(map(foo, range(6)))
[foo(n) for n in range(6)]
# filter
list(map(foo, filter(lambda n: n % 2, range(6))))
[foo(n) for n in range(6) if n % 2]
可见,map和filter的直接替代品是生成器表达式。而reduce在python3中不再是内置函数,而是被放到了functools中:
reduce的通用思想是:把某个操作作用到序列的元素上,把一系列的值聚合成一个值。reduce菜鸟教程
类似的内置函数有sum,any,all
- sum 求一个序列的值的和
- any(iterable) 只要iterable中有一个True,返回True
- all(iterable) iterable中的值都是True时,返回True
匿名函数
lambda关键字在python表达式中创建匿名函数。lambda中的定义体不能赋值,也不能使用try,while等语句
python中除非要作为参数传给高阶函数,否则尽可能不使用lambda函数。
可调用对象
除了用户定义的函数,可调用运算符也可以应用到其他对象上。可以使用callable判断是否可以调用。
- 用户定义的函数:使用def或者lambda创建
- 内置函数:使用C语言实现的函数
- 内置方法:dict.get
- 方法:在类的定义体中定义的函数
- 类:调用一个类时__new__->init
- 类的实例:如果类定义了__call__方法,那么它的实例也可以被调用。
接下来说明如何把类的实例变成可调用的对象
用户定义的可调用类型
任何实现了__call__的python对象都可以被调用。
函数内省
dir()
列出常规对象没有而函数有的属性,计算两个属性集合的差集即可
从定位参数到仅限关键字参数
调用函数时使用*和**展开可迭代对象,映射到单个参数。
def tag(name, *content, cls=None, **attrs):
if cls is not None:
attrs['class'] = cls
if attrs:
attr_str = ''.join(" %s=%s"%(attr, value) for attr, value in sorted(attrs.items()))
else:
attr_str = ''
if content:
return '\n'.join("<%s%s>%s</%s>"%(name, attr_str, c, name) for c in content)
else:
return '<%s%s />'% (name, attr_str)
简单理解就是,有名的参数被attr捕获,没有名字的参数被content捕获。
获取参数相关的信息
获取函数的签名,可以使用inspect模块:
from inspect import signature
sig = signature(foo)
其中,__defaults__元组保存定位参数和关键字参数的默认值。code.co_varnames保存参数名称。这样获取参数信息的问题在于这两个元组中的顺序不定,所以使用signature,生成一个有序映射,把参数名称和参数默认值一一对应起来。
而且signature中有一个bind方法,可以在参数传入函数之前进行格式的检查,如果确实必要的参数,会抛出TypeError,提示缺少参数。
函数注解
先来看一个有注解的函数:
def clip(text: str, max_len: 'int > 0' = 80) -> str:
......
参数的注解放在默认值前,冒号之后,返回值的注解在右括号后加一个箭头和返回值类型。注解不会进行任何处理,只会存在__annotations__中。
注意!!不会进行任何处理的意思是python只会把它当做一堆字串保存起来,仅作为参考的metadata使用,不会做类型检查,不做强制验证,不做类型验证。目前,只有signature函数知道如何提取注解。
sig.parameters
parameters是一个字典,建立参数名与参数注解的映射关系。
如果忘记了,直接看dir即可。
实际上,functools是一个非常有用的包,我们会在之后对其进行更深入的学习~