如何在Python函数声明过程中为函数对象添加自定义属性
补充说明:按照Python命名规范,双下划线开头的属性会触发解释器的名称改写(Name Mangling),所以使用单下划线开头的
_notes作为自定义属性名确实更合理,以下所有方案都统一使用_notes命名。
实现方案
Python函数体内部的代码只有在函数被调用时才会执行,如果你希望定义函数时就自动生成_notes属性,优先选择装饰器方案;如果可以接受第一次调用函数时生成属性,可以选择内部自引用方案。
方案1:带参数的装饰器(最推荐)
这个方案不需要改动函数内部逻辑,定义时直接传入notes配置,符合Python惯用写法,也不会影响函数本身的执行逻辑。
def add_notes(notes: dict): def decorator(func): func._notes = notes return func return decorator # 定义函数时直接加装饰器配置notes @add_notes({'Dependencies': ['x', 'y']}) def foo(x:int, y:int)->int: '''This is a doc string''' return x**2 + y**2 # 直接就能读取属性 print(foo._notes) # 输出:{'Dependencies': ['x', 'y']}
如果希望把notes写在函数体内部和业务逻辑放在一起,也可以用装饰器自动解析函数源码提取配置:
import inspect import ast def extract_notes(func): # 解析函数体的抽象语法树,提取内部定义的_notes变量 tree = ast.parse(inspect.getsource(func)) for node in ast.walk(tree): if isinstance(node, ast.Assign): for target in node.targets: if isinstance(target, ast.Name) and target.id == '_notes': func._notes = ast.literal_eval(node.value) return func func._notes = {} return func @extract_notes def foo(x:int, y:int)->int: '''This is a doc string''' _notes = {'Dependencies': ['x', 'y']} return x**2 + y**2 print(foo._notes) # 输出:{'Dependencies': ['x', 'y']}
方案2:函数内部自引用设置(适合动态生成notes场景)
如果你的notes需要根据函数运行时的参数或者状态动态调整,可以在函数内部拿到自身的引用然后设置属性,第一次调用之后就会生成_notes属性:
import inspect def get_current_func(): # 通用方法获取当前正在执行的函数对象 frame = inspect.currentframe().f_back func_name = frame.f_code.co_name return frame.f_globals[func_name] def foo(x:int, y:int)->int: '''This is a doc string''' current_func = get_current_func() # 仅第一次调用时初始化notes,避免重复赋值 if not hasattr(current_func, '_notes'): current_func._notes = {'Dependencies': ['x', 'y']} return x**2 + y**2 # 调用前没有_notes属性 print(hasattr(foo, '_notes')) # 输出False # 调用后生成属性 foo(1,2) print(foo._notes) # 输出{'Dependencies': ['x', 'y']}
方案3:实现可调用类(适合复杂标注场景)
如果你的标注逻辑非常复杂,除了_notes之外还有很多其他自定义属性和方法,可以直接实现一个带__call__方法的类,实例本身可以像函数一样调用,同时可以直接在类定义里声明_notes属性:
class FuncWithNotes: _notes = {'Dependencies': ['x', 'y']} def __call__(self, x:int, y:int) -> int: '''This is a doc string''' return x**2 + y**2 foo = FuncWithNotes() # 像普通函数一样调用 print(foo(1,2)) # 输出5 # 直接读取_notes属性 print(foo._notes) # 输出{'Dependencies': ['x', 'y']}
内容的提问来源于stack exchange,提问作者MYK
相关产品推荐
相关产品推荐

