通过装饰器为class动态添加函数
Dynamically add function to class through decorator
我正在尝试找到一种通过装饰器向 class 动态添加方法的方法。
我的装饰器看起来像:
def deco(target):
def decorator(function):
@wraps(function)
def wrapper(self, *args, **kwargs):
return function(*args, id=self.id, **kwargs)
setattr(target, function.__name__, wrapper)
return function
return decorator
class A:
pass
# in another module
@deco(A)
def compute(id: str):
return do_compute(id)
# in another module
@deco(A)
def compute2(id: str):
return do_compute2(id)
# **in another module**
a = A()
a.compute() # this should work
a.compute2() # this should work
希望装饰者给classA加上compute()
函数,A的任何对象都应该有compute()方法。
但是,在我的测试中,这仅在我将计算明确导入到创建 A 对象的位置时才有效。我想我遗漏了一些明显的东西,但不知道如何解决。感谢任何帮助!
我认为使用作为 class:
实现的装饰器会更简单
class deco:
def __init__(self, cls):
self.cls = cls
def __call__(self, f):
setattr(self.cls, f.__name__, f)
return self.cls
class A:
def __init__(self, val):
self.val = val
@deco(A)
def compute(a_instance):
print(a_instance.val)
A(1).compute()
A(2).compute()
产出
1
2
但是仅仅因为你能做到并不意味着你应该做到。这可能成为调试的噩梦,并且可能会给任何静态代码分析器或 linter 带来困难(PyCharm 例如“抱怨”Unresolved attribute reference 'compute' for class 'A'
)
为什么当我们将它拆分到不同的模块时它不能开箱即用(更具体地说,当 compute
在另一个模块 中定义时)?
假设如下:
a.py
print('importing deco and A')
class deco:
def __init__(self, cls):
self.cls = cls
def __call__(self, f):
setattr(self.cls, f.__name__, f)
return self.cls
class A:
def __init__(self, val):
self.val = val
b.py
print('defining compute')
from a import A, deco
@deco(A)
def compute(a_instance):
print(a_instance.val)
main.py
from a import A
print('running main')
A(1).compute()
A(2).compute()
如果我们执行 main.py
我们得到以下结果:
importing deco and A
running main
Traceback (most recent call last):
A(1).compute()
AttributeError: 'A' object has no attribute 'compute'
有些东西不见了。 defining compute
不输出。更糟糕的是,compute
从未被定义,更不用说绑定到 A
.
为什么?因为没有任何东西触发 b.py
的执行。仅仅因为它放在那里并不意味着它会被执行。
我们可以通过导入来强制执行。感觉有点侮辱我,但它之所以有效,是因为导入一个文件有一个 side-effect:它执行不受 if __name__ == '__main__
保护的每一段代码,就像导入一个模块执行它的 __init__.py
] 文件。
main.py
from a import A
import b
print('running main')
A(1).compute()
A(2).compute()
产出
importing deco and A
defining compute
running main
1
2
我正在尝试找到一种通过装饰器向 class 动态添加方法的方法。 我的装饰器看起来像:
def deco(target):
def decorator(function):
@wraps(function)
def wrapper(self, *args, **kwargs):
return function(*args, id=self.id, **kwargs)
setattr(target, function.__name__, wrapper)
return function
return decorator
class A:
pass
# in another module
@deco(A)
def compute(id: str):
return do_compute(id)
# in another module
@deco(A)
def compute2(id: str):
return do_compute2(id)
# **in another module**
a = A()
a.compute() # this should work
a.compute2() # this should work
希望装饰者给classA加上compute()
函数,A的任何对象都应该有compute()方法。
但是,在我的测试中,这仅在我将计算明确导入到创建 A 对象的位置时才有效。我想我遗漏了一些明显的东西,但不知道如何解决。感谢任何帮助!
我认为使用作为 class:
实现的装饰器会更简单class deco:
def __init__(self, cls):
self.cls = cls
def __call__(self, f):
setattr(self.cls, f.__name__, f)
return self.cls
class A:
def __init__(self, val):
self.val = val
@deco(A)
def compute(a_instance):
print(a_instance.val)
A(1).compute()
A(2).compute()
产出
1
2
但是仅仅因为你能做到并不意味着你应该做到。这可能成为调试的噩梦,并且可能会给任何静态代码分析器或 linter 带来困难(PyCharm 例如“抱怨”Unresolved attribute reference 'compute' for class 'A'
)
为什么当我们将它拆分到不同的模块时它不能开箱即用(更具体地说,当 compute
在另一个模块 中定义时)?
假设如下:
a.py
print('importing deco and A')
class deco:
def __init__(self, cls):
self.cls = cls
def __call__(self, f):
setattr(self.cls, f.__name__, f)
return self.cls
class A:
def __init__(self, val):
self.val = val
b.py
print('defining compute')
from a import A, deco
@deco(A)
def compute(a_instance):
print(a_instance.val)
main.py
from a import A
print('running main')
A(1).compute()
A(2).compute()
如果我们执行 main.py
我们得到以下结果:
importing deco and A
running main
Traceback (most recent call last):
A(1).compute()
AttributeError: 'A' object has no attribute 'compute'
有些东西不见了。 defining compute
不输出。更糟糕的是,compute
从未被定义,更不用说绑定到 A
.
为什么?因为没有任何东西触发 b.py
的执行。仅仅因为它放在那里并不意味着它会被执行。
我们可以通过导入来强制执行。感觉有点侮辱我,但它之所以有效,是因为导入一个文件有一个 side-effect:它执行不受 if __name__ == '__main__
保护的每一段代码,就像导入一个模块执行它的 __init__.py
] 文件。
main.py
from a import A
import b
print('running main')
A(1).compute()
A(2).compute()
产出
importing deco and A
defining compute
running main
1
2