提问人:MYK 提问时间:12/7/2021 最后编辑:MYK 更新时间:12/8/2021 访问量:689
如何在函数声明中向 python 函数对象添加自定义属性?
How can you add custom attributes to a python function object within the function declaration?
问:
我希望创建一个注释丰富的代码库。我记得 Python 中的函数是对象,因此它们允许任意属性。例如:
def foo(x:int, y:int)->int:
'''This is a doc string'''
return x**2 + y**2
foo.__notes__ = {
'Dependencies':['x', 'y']
}
foo.__notes__
>>> {'Dependencies': ['x', 'y']}
我希望从函数定义中设置属性。关于如何实现这一目标的任何想法?__notes__
编辑:正如评论中指出的那样,这个属性是一个糟糕的名称。 更合适。__notes__
_notes
答:
6赞
chepner
12/7/2021
#1
定义函数后,可以使用装饰器附加注释。
def add_note(k, v):
def _(f):
if not hasattr(f, '_notes'):
f._notes = {}
f._notes[k] = v
return f
return _
@add_note('Dependencies', ['x', 'y'])
def foo(x: int, y: int) -> int:
return x**2 + y**2
顺便说一句,dunder 名称 (like ) 是为实现保留的;你不应该发明你自己的。只需使用一个普通的“私人”名称,前缀为一个 .__notes__
_
评论
0赞
MYK
12/7/2021
这看起来很干净,谢谢。你能想出一种方法将函数中声明的对象添加到属性中吗?_notes
0赞
chepner
12/7/2021
我不建议使用任何东西。我假设您希望在第一次执行函数之前添加它们,即使您可以接受每次调用函数时重新添加它们。做到这一点的唯一方法是反省源代码本身,我认为这确实比它的价值更麻烦。
0赞
MYK
12/7/2021
是的,总的来说,我同意。我会继续你的装饰师。
0赞
MYK
12/7/2021
我觉得你对下划线的使用有点奇怪。你能解释一下吗?即。你为什么要做或?def _(f):
return _
0赞
JL Peyret
12/8/2021
#2
我会以不同的方式回答。函数可以在其正文中引用自己的名称,并且可以为其找到的任何内容分配/读取值。
在你的例子中,假设你想自省一个函数的作用,你要做的是从其对象上修饰的内容来驱动函数行为。
请注意,在我下面的示例中 - 它执行您想要的操作并从函数体中进行更新,直到它至少执行一次后,才会从内省工具的 POV 中记录下来。adder3
adder2
另一方面,它被设置为根据其注释调整其行为,并且注释始终对内省工具可见。
adder4
添加以显示此方法的一个限制。引用函数的名称并不能保证您实际上正在访问该函数,只能保证函数名称在全局变量中解析为的任何内容。
def adder1(x:int = 1, y: int = 2, z: int =3 )->int:
'''This is a doc string'''
return x**2 + y**2
def decorate(**kwds):
""" assign kwds as attributes to the function object """
def actual_decorator(func):
for k, v in kwds.items():
setattr(func, k, v)
return func
return actual_decorator
depends = {"x","y"}
@decorate(_note=f"depends:{depends}", depends=depends)
def adder2(x:int = 1, y: int = 2, z: int =3 )->int:
# a function can totally refer to its name
# and use annotations to dynamically adjust its behavior
depends = adder2.depends
#sum up dependencies
li = []
for key in depends:
v = locals()[key] ** 2
li.append(v)
return sum(li)
def adder3(x:int = 1, y: int = 2, z: int =3 )->int:
""" no note until function execution so this won't allow tools to inspect the module"""
_note = getattr(adder3,"_note",None)
if not _note:
adder3._note = "depends x,y"
return x**2 + y**2
def adder4(x:int = 1, y: int = 2, z: int =3 )->int:
"""there is no guarantee that referring by its own name works"""
print("\n\nadder4.name:", adder4.__name__)
return (x**2 + y**2) * 1000
adder4_saved = adder4
#swap name
adder4 = adder2
for func in [adder1,adder2,adder3,adder4_saved]:
print(f"\n{func.__name__} [note(before)={getattr(func,'_note',None)}] -> {func()} [note(after)={getattr(func,'_note',None)}]")
输出:
adder1 [note(before)=None] -> 5 [note(after)=None]
adder2 [note(before)=depends:{'y', 'x'}] -> 5 [note(after)=depends:{'y', 'x'}]
adder3 [note(before)=None] -> 5 [note(after)=depends x,y]
adder4.name: adder2
adder4 [note(before)=None] -> 5000 [note(after)=None]
评论
__doc__
__doc__