获取有关参数的信息

获取关于参数的信息

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__
<code object clip at 0x000001EF348585B0, file "C:\Users\reion\AppData\Local\Temp/ipykernel_18940/1245383112.py", line 2>
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
<Signature (text, max_len=80)>
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方法,它可以把任意个参数绑定到签名的形参上,所用的规则与实参到形参的匹配方式一样。框架可以使用这个方法在真正调用函数前验证参数

posted @ 2021-12-31 13:06  里列昂遗失的记事本  阅读(45)  评论(0编辑  收藏  举报