python中一切皆对象,这使得python有着自然的自省特性,即可以运行过程当中获取对象自身由哪些属性和方法组成。在阅读python源码和基于第三方库开发时,查看获取某个对象的具体构成内容颇有必要。python自带的inspect模块便提供了将对象的各个成员总结出来并具备良好可读性的功能。python
不使用模块inspect,仅使用内置函数,也能实现自省和反射的功能。bash
自省意味着获取对象的属性和方法;反射经过字符串映射的方式获取或者修改对象的方法或者属性(好比以某个属性的名字字符串做为参数传入某个函数),是自省的一种具体实现方法。app
python中相关功能的内置函数以下:函数
dir(obj)->将对象的全部属性、方法以列表形式返回
hasattr(obj,name_str)->判断objec是否有name_str这个方法或者属性
getattr(obj,name_str)->获取object对象中与name_str同名的方法或者函数
setattr(obj,name_str,value)->为object对象设置一个以name_str为名的value方法或者属性
delattr(obj,name_str)->删除object对象中的name_str方法或者属性
复制代码
可是使用inspect.getmembers(obj)这个方法可以获取到更详尽的自省信息,且可读性更佳,下面将其和dir内置函数进行比较:ui
import inspect
#示例对象--某个函数
def foo(a: int, b: str) -> int:
return a + int(b)
dir(foo)
-->['__annotations__', '__call__', '__class__',...]
inspect.getmembers(foo)
-->[
('__annotations__', {'a': <class 'int'>, 'b': <class 'str'>, 'return': <class 'int'>}),
('__call__', <method-wrapper '__call__' of function object at 0x032C7B70>),
('__class__', <class 'function'>),
...]
复制代码
能够看到使用dir()仅仅得到一个字符串列表,而使用inspect.getmembers()能够得到每一个属性的类型信息。spa
咱们知道可使用type(),isinstance()等内置函数进行类型检查,经常使用于基本数据类型或者对象实例的class判别,好比:code
type(1)==int #比较数据类型
isinstance(cat,Cat) #比较对象实例是否属于某个类
复制代码
但若是要进行更"元"一点的类型比较呢?好比判断一个对象是否为一个模组,一个内置函数,一个生成器,甚至一个 await 表达式:对象
>>> import inspect
>>> inspect.ismodule(inspect) # 检查 inspect 是否为模组
True
>>> inspect.ismethod(inspect) # 检查 inspect 是否为对象方法
False
>>> inspect.isfunction(len) # 检查 len 是否为函数
True
>>> inspect.isbuiltin(len) # 检查 len 是否为内置函数
True
>>> inspect.isgenerator(inspect) # 检查 inspect 是否为生成器
False
>>> inspect.isawaitable(inspect) # 检查 inspect 是否可用于 await 表达式
False
>>>
复制代码
所以,使用inspect.isXXX 方法能够进行更高级的类型判断。开发
python3中新增了函数注解,提示做用大于约束做用(没有约束):字符串
def foobar(a: int, b: "it's b", c: str = 5) -> tuple:
return a, b, c
复制代码
python原生有__annotations__属性用于获取函数注解:
>>> foobar.__annotations__
{'a': int, 'b': "it's b", 'c': str, 'return': tuple}
复制代码
使用inspect模块一样可用获取到函数(callable)的参数信息。
>>> def foo(name, a: int, b: float):
... pass
...
>>> sig = inspect.signature(foo)
>>> sig
<Signature (name, a:int, b:float)>
>>> str(sig)
'(name, a:int, b:float)'
>>> sig.parameters
OrderedDict([('name', <Parameter "name">), ('a', <Parameter "a:int">), ('b', <Parameter "b:float">)])
复制代码
>>> args = ('foobar', 10)
>>> kwargs = {'b': 23.4}
>>> bound = sig.bind(*args, **kwargs) # bind args to signature parameters
>>> bound
<BoundArguments (name='foobar', a=10, b=23.4)>
>>> bound.arguments['name']
'foobar'
>>> bound.arguments['a']
10
>>> bound.arguments['b']
23.4
复制代码
综上,使用inspect能够将函数形参和实参封装为对象,对进一步操做具备意义。
使用inspect获取到参数对象后,结合函数注解属性__annotations__,能够写一个很优雅的强制类型检查装饰器。
from functools import wraps
def checked(func):
ann=func.__annotations__
sig=inspect.signature(func)
@wraps(func)
def wrapper(*args,**kwargs):
bound=sig.bind(*args,**kwargs)
for k,v in bound.arguments.items():
if k in ann:
assert isinstance(v,ann[k]),f'Type Error Expected {ann[k]}'
return func(*args,**kwargs)
return wrapper
>>> @checked
... def add(a: int, b: int) -> int:
... while b:
... a, b = b, a % b
... return a
>>> add(2.7, 3.6)
Traceback (most recent call last):
AssertionError: Type Error Expected <class 'int'>
>>> add(27, 36)
9
复制代码
综上,没有额外在装饰器参数中指明所须要检查类型,直接利用python自省的特性和inspect获取到函数的参数和类型要求,完成强制类型检查。