摘自《流畅的Python》
函数内省:
除了__doc__属性外,函数对象还有很多属性。使用dir
函数可以探知之前的函数对象factorial具有如下属性:
>>> dir(factorial)['__annotations__', '__call__', '__class__','__closure__', '__code__', '__defaults__', '__delattr__', '__dict__', '__dir__', '__doc__', '__eq__', '__format__', '__ge__', '__get__', '__getattribute__', '__globals__', '__gt__', '__hash__', '__init__','__kwdefault__', '__le__', '__lt__', '__module__', '__name__', '__ne__', '__new__', '__qualname__', '__reduce__', '__reduce_ex__','__repr__', '__setattr__', '__sizeof__', '__str__','__subclasshook__']>>>复制代码
其中大多数属性是Python对象共有的,本节讨论与把函数视作对象相关的几个属性,先从__dict__开始。
与用户定义的常规类一样,函数使用__dict__属性存储于它的用户属性。这相当于一种基本的注解。一般来说,为函数随意赋予属性不是很常见的做法,但是Django
框架这么做了。在Django
文档中举了下述示例,把short_description
属性赋予一个方法,Django
管理后台使用这个方法时,在记录列表中会出现指定的描述文本:
def upper_case_name(obj): return ("%s %s" % (obj.first_name, obj.last_name)).upper()upper_case_name.short_descrption = 'Customer name'复制代码
下面重点说明函数专有而用户定义的一般对象没有的属性。计算两个属性集合的差集便能得到函数专有属相列表:
>>> class C:pass # 创建一个空的用户定义的类>>> obj = C() # 创建一个实例>>> def func():pass # 创建一个空函数>>> # 计算差集然后排序,得到类的实例没有而函数有的属性列表>>> sorted(set(dir(func)), set(dir(obj))) ['__annotation__', '__call__', '__closure__', '__code__', '__defaults__', '__get__','__globals__', '__kwdefaults__', '__name__', '__qualname__']复制代码
下表为用户定义的函数的属性:
名称 | 类型 | 说明 |
---|---|---|
__annotations__ | dict | 参数和返回值的注解 |
__call__ | method-wrapper | 实现**()**运算符,即可调用对象协议 |
__closure__ | tuple | 函数闭包,即自由变量的绑定(通常是None) |
__code__ | code | 编译成字节码的函数元数据和函数定义体 |
__defaults__ | tuple | 形式参数的默认值 |
__get__ | method-wrapper | 实现只读描述符协议 |
__globals__ | dict | 函数所在模块的全局变量 |
__kwdefaults__ | dict | 仅限关键字形式参数的默认值 |
__name__ | str | 函数名称 |
__qualname__ | str | 函数的限定名称,如Random.choice |
之后会讨论__defaults__、__code__和__annotation__属性,IDE和框架使用它们提取关于函数签名的信息。但是,为了深入了解这些属性,我们要先讨论Python为声明函数形参和传入实参所提供的强大句法!