Python 函數內省


函數內省(function introspection)

除了__doc__屬性, 函數對象還有很多屬性,對於下面的函數,可以使用dir()查看函數具有的屬性:

def factorial(n):
    return 1 if n == 1 else n*factorial(n - 1)
>>> dir(factorial)
['__annotations__', '__call__', '__class__', '__closure__', '__code__', 
'__defaults__', '__delattr__', '__dict__', '__dir__', '__doc__', '__eq__', 
'__format__', '__ge__', '__get__', '__getattribute__', '__globals__', '__gt__',
 '__hash__', '__init__', '__kwdefaults__', '__le__', '__lt__', '__module__', 
'__name__', '__ne__', '__new__', '__qualname__', '__reduce__', 
'__reduce_ex__', '__repr__', '__setattr__', '__sizeof__', '__str__', 
'__subclasshook__']

其中大多數是Python常規類都有的屬性,下面重點看看常規對象沒有而函數對象有的屬性:

>>> class C:pass
... 
>>> obj = C()
>>> def func():pass
... 
>>> sorted(set(dir(func)) - set(dir(obj))) # 計算差集,然后排序
['__annotations__', '__call__', '__closure__', '__code__', '__defaults__', '__get__', '__globals__', '__kwdefaults__', '__name__', '__qualname__']

對於上面列出的函數特有屬性,說明如下:

接下來我們討論__defaults__, __code__, __annotations__的作用

(一) __defaults__, __code__:

函數對象有個__defaults__屬性,他的值是一個元組,里面保存着定位參數和關鍵字參數的默認值。然而,參數的名稱在__code__屬性中, 它的值是一個code對象引用,自身也有很多屬性

僅限關鍵字參數的默認值在__kwdefaults__屬性中,

我們借用下面的示例說明這些屬性的用途:

def clip(text=None, max_length=80):
    """
    Return text clipped at the last space before or after max_len
    """
    end = None
    if len(text) > max_length:
        space_before = text.rfind(' ', 0, max_length)
        if space_before >= 0:
            end = space_before
        else:
            space_after = text.rfind(' ', max_length)
            if space_after >= 0:
                end = space_after

        if end is None:
            end = len(text)

        return text[:end].rstrip()

提取clip函數參數的信息:

>>> from Example5_15 import clip
>>> clip.__defaults__
(None, 80)
>>> clip.__code__
<code object clip at 0x7f8e50658f60, file "/root/python_demo/Example5_15.py", line 1>
>>> clip.__code__.co_varnames
('text', 'max_length', 'end', 'space_before', 'space_after')
>>> clip.__code__.co_argcount
2

從控制台信息可以看出,__code__.co_varnames包含函數參數,並且還包含函數體中的局部變量,函數參數個數由__code__.co_argcount確定(這里不包含前綴為*或者**的變長參數)

從這些屬性提取參數信息並不是很方便,我們有更好的方式-------使用inspect模塊:

>>> from inspect import signature
>>> sig = signature(clip)
>>> sig
<Signature (text=None, max_length=80)>
>>> for name, param in sig.parameters.items():
...     print(param.kind, ':', name, '=', param.default)
... 
POSITIONAL_OR_KEYWORD : text = None
POSITIONAL_OR_KEYWORD : max_length = 80

這樣就好多了, Inspect.signature 函數返回一個inspect.Signature對象,它有一個parameter屬性,這是一個有序映射,把參數名和inspect.Parameter對象對應起來,各個Parameter屬性也有自己的屬性,比如name, default, kind

如果是非默認值參數,特殊的inspect._empty值表示沒有默認值,考慮到上面例子的None是有效默認值,這樣處理是合理的

(二) __annotations__屬性:

我們把上面的clip函數添加注解,如下所示:

def clip(text: str =None, max_length: 'int > 0'=80) -> str: """
    :return text clipped at the last space before or after max_len
    :param text:
    :param max_length:
    """
    end = None
    if len(text) > max_length:
        space_before = text.rfind(' ', 0, max_length)
        if space_before >= 0:
            end = space_before
        else:
            space_after = text.rfind(' ', max_length)
            if space_after >= 0:
                end = space_after

        if end is None:
            end = len(text)

        return text[:end].rstrip()

注意函數頭部的第一行

函數聲明中的各個參數可以在:之后添加注解,添加注解的方法:

(1)參數有默認值,注解放在參數名和=號之間

(2)返回值,在)和函數聲明尾部的:之間添加->和一個表達式,表達式可以是任何類型

對於注解,Python解釋器不做任何處理,只是存儲在__annotations__屬性(字典形式)中:

>>> from Example5_15 import clip
>>> clip.__annotations__
{'return': <class 'str'>, 'max_length': 'int > 0', 'text': <class 'str'>}

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM