获取有关参数的信息
获取关于参数的信息
HTTP微框架Bobo有个使用内省函数的例子
# Bobo直到hello需要person参数,并且从HTTP请求中获取它
import bobo
@bobo.query('/')
def hello(person):
return 'Hello %s!' % person
bobo.query装饰器把一个普通的函数(如hello)与框架的请求处理机制集成起来了。Bobo会内省hello函数,发现它需要一个名为person的参数,然后从请求中获取那个名称对应的参数,将其传递给hello函数,因此程序员根本不用触碰请求对象
Bobo是怎么知道函数需要哪个参数的呢?它又是怎么知道参数没有默认值呢?
函数对象有个__defaults__属性,它的值是一个元组,里面保存着定位参数和关键字参数的默认值。仅限关键字参数的默认值在__kwdefaults__属性中。然后参数的名称在__code__属性中。它的值是一个code对象引用,自身也有很多属性
# 在指定长度附近截断字符串的函数
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 end is None:
end = len(text)
return text[:end].rstrip()
# 提取关于函数参数的信息
clip.__defaults__
(80,)
clip.__code__
clip.__code__.co_varnames
('text', 'max_len', 'end', 'space_before', 'space_after')
clip.__code__.co_argcount
2
参数名称在__code__.co_varnames中,不过里面还有函数定义体中创建的的局部变量。因此,参数名称是前N个字符串,N的值由__code__.co_argcount确定。这里不包含前缀为*或**的变长参数。参数的默认值只能通过它们在__defaults__元组中的位置确定,因此要从后向前扫描才能把参数和默认值对应起来。在这个示例中,clip函数有2个参数,text和max_len,其中有一个有默认值,即80,因此它必然属于最后一个参数,即max_len
# 提取函数的签名
from inspect import signature
sig = signature(clip)
sig
str(sig)
'(text, max_len=80)'
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
仅限关键字参数
- POSITIONAL_ONLY
仅限定位参数;目前,Python声明的句法不支持,但是有些使用C语言实现且不接受关键字参数的函数(如divmod)支持
除了name、default和kind,inspect.Parameter对象还有一个annotation(注解)属性,它的值通常是inspect._empty,但是可能包含Python 3新的注解语法提供的函数签名元数据
inspect.Signature对象有个bind方法,它可以把任意个参数绑定到签名的形参上,所用的规则与实参到形参的匹配方式一样。框架可以使用这个方法在真正调用函数前验证参数