2019年7月18日 星期四
教學
https://sites.google.com/site/zsgititit/home/python-cheng-shi-she-ji/python-bian-shu-yu-yun-suan-zi
inspect – 探測 Python 物件內容
inspect – 探測 Python 物件內容
Theinspect
module provides several useful functions to help get information about live objects such as modules, classes, methods, functions, tracebacks, frame objects, and code objects. For example, it can help you examine the contents of a class, retrieve the source code of a method, extract and format the argument list for a function, or get all the information you need to display a detailed traceback.
Source code: Lib/inspect.py
在 Python 界,人人都能朗朗上口的一句話大概就是「Python 裏什麼東西都是物件」,既然如此,有沒有辦法在 runtime 得到這些資訊呢?答案是可以的,透過
inspect
函式庫,我們可以探測幾乎任何的東西 (凡舉模組、類別、函式、traceback、frame 甚至是 code 的資訊!)。inspect
主要提供四大功能:型態檢查、獲取程式碼、探測類別與函式、檢查直譯器堆疊。01. Quickstart Tutorial
首先我們來使用
inspect
檢查物件的型態!
1
2
3
4
5
6
7
8
9
10
11
12
|
>>> import inspect
>>> inspect.ismodule(inspect) # 檢查 inspect 是否為模組
True
>>> inspect.ismethod(inspect) # 檢查 inspect 是否為物件方法
False
>>> inspect.isfunction(len) # 檢查 len 是否為函式
True
>>> inspect.isbuiltin(len) # 檢查 len 是否為內建函式
True
>>> inspect.isawaitable(inspect) # 檢查 inspect 是否可用於 await expression
False
>>>
|
我們也可以透過
inspect
取得物件內的資訊,例如說物件的 docstring、comment 甚至是 source code:
1
2
3
4
5
6
7
|
>>> inspect.getdoc(len) # 取得 len 的 docstring
'Return the number of items in a container.'
>>> inspect.getsource(inspect) # 取得 inspect 的 source code
'"""Get useful information from live Python objects.\n\n
This module encapsulates the interface provided by the
internal special\nattributes (co_*, im_*, tb_*, etc.) in ...'
>>>
|
透過
inspect.getmembers
,我們可以取得一個物件裏面所有的 member:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
|
>>> inspect.getmembers(len)
[('__call__',
<method-wrapper '__call__' of builtin_function_or_method object at 0x7f85af1491c0>),
('__class__', <class 'builtin_function_or_method'>),
('__delattr__',
...,
('__setattr__',
<method-wrapper '__setattr__' of builtin_function_or_method object at 0x7f85af1491c0>),
('__sizeof__',
<built-in method __sizeof__ of builtin_function_or_method object at 0x7f85af1491c0>),
('__str__',
<method-wrapper '__str__' of builtin_function_or_method object at 0x7f85af1491c0>),
('__subclasshook__',
<built-in method __subclasshook__ of type object at 0x899760>),
('__text_signature__', '($module, obj, /)')]
>>> import tabnanny
>>> inspect.getmembers(tabnanny, inspect.isfunction) # get only function
[('check', <function check at 0x7f85acbccbd8>),
('errprint', <function errprint at 0x7f85acbcca68>),
('format_witnesses', <function format_witnesses at 0x7f85acbccf70>),
('main', <function main at 0x7f85acbccb20>),
('process_tokens', <function process_tokens at 0x7f85acb57560>)]
|
不過最有趣以及有用的,還是這個
inspect.signature
,可以獲得 callable 的 Signature 物件:
1
2
3
4
5
6
7
8
9
10
|
>>> 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
mappingproxy(OrderedDict([('name', <Parameter "name">), ('a', <Parameter "a:int">), ('b', <Parameter "b:float">)]))
|
透過
Signature.bind
,我們可以把參數給綁定到 signature 上:
1
2
3
4
5
6
7
8
9
10
11
|
>>> 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
|
02. HOW-TO Guides
透過 signature.bind
以及 annotation 來做型態檢查
前面提到的
signature.bind
看似無用,但是卻可以用在型態檢查上面。在 PEP 484 中,提到了如何在 Python 裏面使用型態提示:
gcd with type hints
Python
1
2
3
4
5
6
7
|
>>> def gcd(a: int, b: int) -> int:
... while b:
... a, b = b, a % b
... return b
...
>>> gcd(27, 36)
9
|
上面的 很廢只有提示功能,我們可以嘗試丟入 float 數值看看:
gcd
函式中,我們為參數 a 以及參數 b 加上型態提示為 int,而函式回傳值的型態提示為 int。只是這個
Using float in gcd function
Python
1
2
|
>>> gcd(2.7, 3.6)
4.440892098500626e-16
|
Python 會很開心的忽略 annotation,回傳一個神奇的 float 數值給你。
要解決這個問題,我們可以手動的使用
assert
來檢查型態:
gcd with assert to check arguments type
Python
1
2
3
4
5
6
7
8
9
10
11
12
|
>>> def gcd(a: int, b: int) -> int:
... assert isinstance(a, int), 'Expected int'
... assert isinstance(b, int), 'Expected int'
... while b:
... a, b = b, a % b
... return a
...
>>> gcd(2.7, 3.6)
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
File "<stdin>", line 2, in gcd
AssertionError: Expected int
|
但是這根本沒有發揮 type hint 的效果啊!
額外加上一個 wrapper 來解決這個問題:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
|
>>> 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 name, val in bound.arguments.items():
... if name in ann:
... assert isinstance(val, ann[name]), \
... f'Expected {ann[name]}'
... return func(*args, **kwargs)
... return wrapper
...
>>> @checked
... def gcd(a: int, b: int) -> int:
... while b:
... a, b = b, a % b
... return a
...
>>> gcd(2.7, 3.6)
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
File "<stdin>", line 9, in wrapper
AssertionError: Expected <class 'int'>
>>> gcd(27, 36)
9
>>>
|
如此一來就能活用 inspect 來達到使用 annotation 檢查型態的功能了!
03. Discussions
暫時沒有。
Lib/inspect.py
Python
1
2
|
__author__ = ('Ka-Ping Yee <ping@lfw.org>',
'Yury Selivanov <yselivanov@sprymix.com>')
|
訂閱:
文章 (Atom)