函数进阶
Python 是面向对象的语言,在 Python中一切皆对象。函数自然也不例外。
它有属于对象的属性,除此之外,函数还可以自定义属性。注意,属性是和对象相关的,和作用域无关
注意:这里说的是函数(
function
类型)的特殊属性,而非方法(method
类型)的特殊属性。
自定义属性
函数自定义属性的方式很简单,假设函数名为 myfunc
# 设置属性
myfunc.version = 'V1.0.1'
# 访问属性
print(myfunc.version)
这个属性 version
可以像全局变量一样被访问,修改。但它并不是全局变量
查看函数对象属性
# 查看函数全部属性
dir(myfunc)
['__annotations__', '__call__', '__class__', '__closure__', '__code__', '__defaults__', '__delattr__', '__dict__', '__dir__', '__doc__', '__eq__', '__format__', '__ge__', '__get__', '__getattribute__', '__globals__', '__gt__', '__hash__', '__init__', '__init_subclass__', '__kwdefaults__', '__le__', '__lt__', '__module__', '__name__', '__ne__', '__new__', '__qualname__', '__reduce__', '__reduce_ex__', '__repr__', '__setattr__', '__sizeof__', '__str__', '__subclasshook__']
__doc__
:用于获取函数的说明文档,没有则返回 None__name__
:获取函数的名称__defaults__
:以元组形式返回函数的默认参数,没有则返回 None__dict__
:以字典的形式返回函数中自定义的属性__code__
:返回已编译的函数对象
_code_
print(myfunc.__code__)
# <code object test at 0x00000000021491E0, file "demo.py", line 3>
上面的输出结果已经指明了 __code__
也是对象,在这个函数对象里,我们可以看到字节码,元数(也就是参数数量)和一些与函数相关的其他东西。
print(dir(myfunc.__code__))
# [... , 'co_argcount', 'co_cellvars', 'co_code', 'co_consts', 'co_filename', 'co_firstlineno', 'co_flags', 'co_freevars', 'co_kwonlyargcount', 'co_lnotab', 'co_name', 'co_names', 'co_nlocals', 'co_stacksize', 'co_varnames']
现在就可以看到函数对象相关的属性,其中有一类属性都以 co_
开头,表示字节码的意思。实际上,并非只有函数具有这些属性,所有的代码块(code block)都有这些属性
def myfunc(a, b=2):
a = 'hello'
b = 'world'
# 打印这些以 co 开头的属性的值
for attr in dir(myfunc.__code__):
if attr.startswith('co_'):
print(f'{attr}: {getattr(test.__code__, attr)}')
# co_argcount: 2
# co_cellvars: ()
# co_code: b'd\x01}\x00d\x02}\x01d\x00S\x00'
# co_consts: (None, 'hello', 'world')
# co_filename: C:/Users/Administrator/Desktop/demo.py
# co_firstlineno: 3
# co_flags: 67
# co_freevars: ()
# co_kwonlyargcount: 0
# co_lnotab: b'\x00\x01\x04\x01'
# co_name: myfunc
# co_names: ()
# co_nlocals: 2
# co_stacksize: 1
# co_varnames: ('a', 'b')
# 使用 dis 模块的 show_code() 函数来输出这些信息的整理
import dis
dis.show_code(myfunc)
# Name: myfunc
# Filename: C:/Users/Administrator/Desktop/demo.py
# Argument count: 2
# Kw-only arguments: 0
# Number of locals: 2
# Stack size: 1
# Flags: OPTIMIZED, NEWLOCALS, NOFREE
# Constants:
# 0: None
# 1: 'hello'
# 2: 'world'
# Variable names:
# 0: a
# 1: b
研究 __code__
是了解 Python 函数如何运行的最好办法。例如,函数 foo 的元数是由 foo.__code__.co_argcount
获得,而字码节存在于 foo.__code__.co_code
中
__code__
对象的属性是只读,但是 __code__
属性本身不是!我们可以使用下面的代码整蛊一下朋友
def cat():
print('I am a cat!')
def dog():
print('I am a dog!')
cat.__code__ = dog.__code__
现在,当我们运行 cat()
时,实际上运行的是 dog 的代码,于是我们会得到:
I am a dog!