...

Python3中的函数对象

在Python中一切皆对象,函数也是一种对象,有相关的属性和方法。
对于任意对象,我们可以用dir()函数来获取其内置的属性及方法名,例如:

def add(a: int, b: int=1) -> int:
    """加法函数"""
    return a + b

print(dir(add))

运行结果如下:

['__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__']

函数常见属性

函数常见属性如下:

属性 返回类型 说明
__name__ str 函数名
__qualname__ str 函数限定名,例如,函数在一个类中时,其限定名为 类名.函数名
__doc__ str or None 函数注释(docstring)
__annotations__ dict or None 函数函数参数及返回值类型注释
__module__ str 函数所属模块名
__class__ 类对象 函数所属类(对象)
__code__ code对象 函数代码(对象)
__defaults__ tuple or None 函数参数默认值
__kwdefaults__ tuple or None 函数限定关键字参数默认值
__closure__ tuple of cell or None 闭包函数中引用的cell变量

cell变量指闭包外部函数中,被内部引用的变量(多作用域变量)

函数属性使用示例

示例1-函数基本属性

def add(a: int, b: int=1) -> int:
    """加法函数"""
    return a + b

print('函数名:', add.__name__)
print('函数限定名:', add.__qualname__)
print('函数注释(docstring):', add.__doc__)
print('函数参数及返回值类型注释:', add.__annotations__)
print('函数所属类(对象):', add.__class__)
print('函数所属模块名:', add.__module__)
print('函数参数默认值:', add.__defaults__)

输出结果如下:

函数名: add
函数限定名: add
函数注释(docstring): 加法函数
函数参数及返回值类型注释: {'a': <class 'int'>, 'b': <class 'int'>, 'return': <class 'int'>}
函数所属模块名: __main__
函数所属类(对象): <class 'function'>
函数代码(对象): <code object add at 0x1043d0a80, file "/Users/superhin/Library/Application Support/JetBrains/PyCharm2023.1/scratches/scratch_11.py", line 19>
函数参数默认值: (1,)
函数限定关键值参数默认值: None

示例2-函数名及函数限定名

class Calc:
    def add(self, a, b):
        return a + b
print('函数名:', Calc.add.__name__)
print('函数限定名:', Calc.add.__qualname__)

输出结果如下:

函数名: add
函数限定名: Calc.add

示例3-关键字限定参数默认值

函数中可以使用独立的*参数,其后的参数在使用函数时仅可以按key=value形式传参

def add(*, a, b=1):
    return a + b

# add(1,2)  # ❌报错,不允许按位置参数形式使用
# add(a=1, b=2) # 可用
print('函数限定关键值参数默认值:', add.__kwdefaults__)

输出结果如下:

函数限定关键值参数默认值: {'b': 1}

示例4-闭包函数获取自由变量列表

def calc(method, a, b):
    def add():
        return a + b

    if method == 'add':
        return add
    # ...

add = calc('add', 1, 2)  # 得到闭包函数add
print('回调函数中自由变量列表', add.__closure__)
for cell in add.__closure__:
    print('自由变量值:', cell.cell_contents)

输出结果如下:

回调函数中自由变量列表 (<cell at 0x1113caaf0: int object at 0x10c8ef930>, <cell at 0x1113ca9d0: int object at 0x10c8ef950>)
自由变量值: 1
自由变量值: 2

注:由于闭包函数add中未使用外部函数的method变量,因此其不会出现在add的__closure__中。

函数代码对象常见属性

函数的__code__属性返回一个code(Python字节码)对象,该对象常见属性如下:

属性 返回类型 说明
co_filename str 代码所在文件路径
co_firstlineno int 代码第一行行号
co_argcount int 常规参数数量
co_posonlyargcount int 限定位置参数数量
co_kwonlyargcount int 限定关键字参数数量
co_nlocals int 局部变量数量
co_varnames tuple 局部变量名列表
co_freevars tuple 自由变量名列表
co_cellvars tuple cell变量(多作用域变量)名列表
co_names tuple 引用其他函数名列表
co_consts tuple 所使用常量列表,包含函数的docstring、内部函数等
co_code bytes 编译后的二进制操作码(opcodes)
co_lnotab bytes 地址及代码行号映射编码后的二进制
co_flags int 操作标记值
co_stacksize int 使用的栈大小

示例1-基本属性

def add(a, b):
    """加法函数"""
    s = a + b
    return s

code = add.__code__

print('函数代码所在文件路径:', code.co_filename)
print('函数代码第一行行号:', code.co_firstlineno)

print('参数数量:', code.co_argcount)
print('关键字限定参数数量:', code.co_kwonlyargcount)
print('位置限定参数数量:',code.co_posonlyargcount)
print('局部变量数量:', code.co_nlocals)

print('局部变量名列表:', code.co_varnames)
print('cell变量名列表:', code.co_cellvars)
print('自由变量名列表:',code.co_freevars)
print('所使用常量列表:', code.co_consts)
print('引用名称列表:', code.co_names)

print('编译后的二进制操作码:', code.co_code)
print('地址及代码行号映射编码后的二进制:', code.co_lnotab)
print('操作标记值:', code.co_flags)
print('使用栈大小:', code.co_stacksize)

输出结果如下:

函数代码所在文件路径: /Users/superhin/Library/Application Support/JetBrains/PyCharm2023.1/scratches/scratch_11.py
函数代码第一行行号: 98
参数数量: 2
关键字限定参数数量: 0
位置限定参数数量: 0
局部变量数量: 3
局部变量名列表: ('a', 'b', 's')
cell变量名列表: ()
自由变量名列表: ()
所使用常量列表: ('加法函数',)
引用名称列表: ()
编译后的二进制操作码: b'|\x00|\x01\x17\x00}\x02|\x02S\x00'
地址及代码行号映射编码后的二进制: b'\x00\x02\x08\x01'
操作标记值: 67
使用栈大小: 2

示例2-位置限定参数及关键字限定参数

def sub(a, b, /):  # 限定位置参数
    return a -b

def mul(*, a, b):  # 限定关键字参数
    return a * b

print('sub函数-参数数量:', sub.__code__.co_argcount)
print('sub函数-位置限定参数数量:', sub.__code__.co_posonlyargcount)

print('mul函数-参数数量:',mul.__code__.co_argcount)
print('mul函数-关键字限定参数数量:',mul.__code__.co_kwonlyargcount)

输出结果如下:

sub函数-参数数量: 2
sub函数-位置限定参数数量: 2
mul函数-参数数量: 0
mul函数-关键字限定参数数量: 2

示例3-引用函数名称列表

def add(a, b):
    return a + b

def mul(a, b):
    return a * b


def calc(x, y, z):
    """计算(x+y) * z"""
    return mul(add(x, y), z)  # 引用名称

print('calc函数-引用函数名列表:', calc.__code__.co_names)

输出结果如下:

calc函数-引用函数名列表: ('mul', 'add')

示例4-闭包及外部函数-cell变量及自由变量列表

def calc(method, a, b):
    """延迟计算"""
    def add():
        """计算加法"""
        return a + b

    if method == 'add':
        return add
    # ...

add = calc('add', 1, 2)  # 闭包函数


print('calc函数-cell变量名列表:', calc.__code__.co_cellvars)
print('calc函数-局部变量名列表:', calc.__code__.co_varnames)
print('calc函数-所使用常量列表:', calc.__code__.co_consts)

print('add函数-自由变量名列表:', add.__code__.co_freevars)
print('add函数-局部变量名列表:', add.__code__.co_varnames)
print('add函数-所使用常量列表:', add.__code__.co_consts)

输出结果如下:

calc函数-cell变量名列表: ('a', 'b')
calc函数-局部变量名列表: ('method', 'a', 'b', 'add')
calc函数-所使用常量列表: ('延迟计算', <code object add at 0x11030edf0, file "/Users/superhin/Library/Application Support/JetBrains/PyCharm2023.1/scratches/scratch_11.py", line 124>, 'calc.<locals>.add', 'add', None)
add函数-自由变量名列表: ('a', 'b')
add函数-局部变量名列表: ()
add函数-所使用常量列表: ('计算加法',)

参考:inspect-检查对象
参考:PyCodeObject与Python程序执行

posted @ 2023-06-19 13:18  韩志超  阅读(92)  评论(0编辑  收藏  举报