5.8 5.8 5.8 获取关于参数的信息
bobo库中有一个使用函数内省的示例:
import bobo
@bobo.query('/')
def hello(person):
return 'Hello %s' % person
bobo装饰器将一个普通函数与框架的请求处理机制给囊括在一起了。bobo会内省hello这个函数,会去判断所需的参数。然后自动从请求中过去获取对应名称的参数,传给hello参数。
如果你没有传示例中的person参数时,如下图所示:
请求时,加入参数信息person,才能得到想要的结果:
那么bobo是怎么知道该函数需要什么样的参数呢?原来函数对象有个__defaults__属性,他的值是一个元组,里面保存着定位参数和关键词参数的默认值。仅限关键字参数的默认值在__kwdefaults__的属性中。参数的名称在__code__属性中,它的值是一个code对象引用,
它也有很多自身属性。
def clip(text, max_len=80):
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 space_after >= 0:
end = space_after
if end is None:
end = len(text)
return text[:end].rstrip()
print(clip.__defaults__)
print(clip.__code__.co_varnames)
print(clip.__code__)
# res
(80,)
('text', 'max_len', 'end', 'space_before', 'space_after')
<code object clip at 0x1064b9230, file "/Users/xxx/PycharmProjects/flu_python/test.py", line 7>
参数和在 函数定义体中创建的局部变量都在__code__.co_varnames中。但是,这里面不包含前缀为*或着**的变长参数。
在该实例中,clip函数有text和max_len两个参数,其中一个有默认值80,__defaults__得到的max_len为后面一个参数的值,这不合逻辑。
因此有了一个更好的inspect模块。他有一个inspect.signature的方法,同时返回一个signature对象,它有一个paramaters的属性,是一个有序的映射。将参数名和inspect.parameter对象对应起来。
>>> import inspect
>>> from meishi.test import books
>>> sig = inspect.signature(books)
>>> sig
<Signature (name, cls=None, *content, **attrs)>
>>> my_tag = {'name': "img", "title": 'SunSet', "src": 'sunset.jpg', "cls": 'framed'}
>>> bound_args = sig.bind(**my_tag)
>>> bound_args
<BoundArguments (name='img', cls='framed', attrs={'title': 'SunSet', 'src': 'sunset.jpg'})>
>>>for name, value in bound_args.arguments.items():
... print(name, '=', value)
name = img
cls = framed
attrs = {'title': 'SunSet', 'src': 'sunset.jpg'}
>>> del my_tag['name']
>>> bound_args = sig.bind(**my_tag)
Traceback (most recent call last):
File "<input>", line 1, in <module>
File "/usr/local/var/pyenv/versions/3.6.6/lib/python3.6/inspect.py", line 2984, in bind
return args[0]._bind(args[1:], kwargs)
File "/usr/local/var/pyenv/versions/3.6.6/lib/python3.6/inspect.py", line 2899, in _bind
raise TypeError(msg) from None
TypeError: missing a required argument: 'name'
inspect.signature对象通过.bind()方法和一个字典绑定。从而得到一个inspect.BoundArguments对象。该模块向我们展示了python数据模型把实参绑定给函数调用中的形参的过程。
5.9
5.9
5.9 函数注解
python3中为我们提供了一些句法,用于函数声明中的参数和返回值附加元数据。
def my_clip(text: str, max_len: 'int > 0' = 80) -> str:
'''
在max_len前面或后面的第一个空格处截断文本
:param text:
:param max_len:
:return:
'''
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 space_after >= 0:
end = space_after
if end is None:
end = len(text)
return text[:end].rstrip()
如上代码,是有注解的函数声明。可以在“:”之后增加注解表达式。
如果参数有默认值,注解放在参数名和=之间。如果想要注解返回式,在函数声明的末尾和“:”之间添加一个->和一个表达式。
做完注解后,观察函数的__annotations__属性:
my_clip.__annotations__
{'text': <class 'str'>, 'max_len': 'int > 0', 'return': <class 'str'>}
它们只是被放在了__annotations__属性中而已,并没有做什么其他的检查操作。换句话说,注解对python解释器没有任何意义。注解只是元数据,可以供IDE、框架和装饰器等工具使用。