Class 装饰器没有绑定自己
Class decorator not binding self
我已经定义了一个 class,它将装饰器定义为 class 的方法。
装饰器本身创建第二个 class 的可调用实例来替换装饰方法。
由于装饰方法现在实际上是 class,我可以在其上调用方法。
在我的(虚构的,最小的)示例中,我想使用每个方法的自定义最大回调数注册回调。
class CallbackAcceptor:
def __init__(self, max_num_callbacks, func):
self._func = func
self._max_num_callbacks = max_num_callbacks
self._callbacks = []
def __call__(self, *args, **kwargs):
# This ends up being called when the decorated method is called
for callback in self._callbacks:
print(f"Calling {callback.__name__}({args}, {kwargs})")
callback(*args, **kwargs)
return self._func(*args, **kwargs) # this line is the problem, self is not bound
def register_callback(self, func):
# Here I can register another callback for the decorated function
if len(self._callbacks) < self._max_num_callbacks:
self._callbacks.append(func)
else:
raise RuntimeError(f"Can not register any more callbacks for {self._func.__name__}")
return func
class MethodsWithCallbacksRegistry:
def __init__(self):
self.registry = {} # Keep track of everything that accepts callbacks
def accept_callbacks(self, max_num_callbacks: int = 1):
def _make_accept_callbacks(func):
# Convert func to an CallbackAcceptor instance so we can register callbacks on it
if func.__name__ not in self.registry:
self.registry[func.__name__] = CallbackAcceptor(max_num_callbacks=max_num_callbacks, func=func)
return self.registry[func.__name__]
return _make_accept_callbacks
函数的一切都按预期工作,但是当我修饰一个 class 方法时它会中断,因为 class 实例未绑定到修饰的方法:
registry = MethodsWithCallbacksRegistry()
@registry.accept_callbacks(max_num_callbacks=1)
def bar(i):
return i * 10
@bar.register_callback
def bar_callback(*args, **kwargs):
print("Bar Callback")
print(bar(i=10)) # Works fine, prints "Bar Callback" and then 100
现在如果我定义一个接受回调的方法:
class Test:
@registry.accept_callbacks(max_num_callbacks=1)
def foo(self, i):
return i * 2
@Test.foo.register_callback
def foo_callback(*args, **kwargs):
print("Foo Callback")
如果我显式传递 self,它会起作用,但如果我只是假设实例已绑定,则不会起作用:
t = Test()
# Note that I pass the instance of t explicitly as self
Test.foo(t, i=5) # Works, prints "Foo Callback" and then 10
t.foo(t, i=5) # Works, prints "Foo Callback" and then 10
t.foo(i=5) # Crashes, because self is not passed to foo
这是回溯:
Traceback (most recent call last):
File "/home/veith/.PyCharmCE2019.3/config/scratches/scratch_4.py", line 62, in <module>
t.foo(i=5)
File "/home/veith/.PyCharmCE2019.3/config/scratches/scratch_4.py", line 13, in __call__
return self._func(*args, **kwargs) # this line is the problem, self is not bound
TypeError: foo() missing 1 required positional argument: 'self'
我一直认为 t.foo(i=5)
基本上是 Test.foo(t, i=5)
通过描述符的语法糖,但看来我错了。所以这是我的问题:
- 这没有按预期工作的原因是什么?
- 我需要做什么才能让它发挥作用?
谢谢!
PS:我用的是python3.8
如果您将 CallbackAcceptor
设为 descriptor,它的工作原理如下:
class CallbackAcceptor:
def __init__(self, max_num_callbacks, func):
self._func = func
self._max_num_callbacks = max_num_callbacks
self._callbacks = []
def __call__(self, *args, **kwargs):
# This ends up being called when the decorated method is called
for callback in self._callbacks:
print(f"Calling {callback.__name__}({args}, {kwargs})")
callback(*args, **kwargs)
return self._func(*args, **kwargs)
def register_callback(self, func):
# Here I can register another callback for the decorated function
if len(self._callbacks) < self._max_num_callbacks:
self._callbacks.append(func)
else:
raise RuntimeError(f"Can not register any more callbacks for {self._func.__name__}")
return func
# Implementing __get__ makes this a descriptor
def __get__(self, obj, objtype=None):
if obj is not None:
# the call is made on an instance, we can pass obj as the self of the function that will be called
return functools.partial(self.__call__, obj)
# Called on a class or a raw function, just return self so we can register more callbacks
return self
现在可以正常调用了:
print(bar(i=10))
# Bar Callback
# 100
t = Test()
t.foo(i=5)
# Foo Callback
# 10
t.foo(t, i=5)
# TypeError: foo() got multiple values for argument 'i'
我已经定义了一个 class,它将装饰器定义为 class 的方法。 装饰器本身创建第二个 class 的可调用实例来替换装饰方法。 由于装饰方法现在实际上是 class,我可以在其上调用方法。 在我的(虚构的,最小的)示例中,我想使用每个方法的自定义最大回调数注册回调。
class CallbackAcceptor:
def __init__(self, max_num_callbacks, func):
self._func = func
self._max_num_callbacks = max_num_callbacks
self._callbacks = []
def __call__(self, *args, **kwargs):
# This ends up being called when the decorated method is called
for callback in self._callbacks:
print(f"Calling {callback.__name__}({args}, {kwargs})")
callback(*args, **kwargs)
return self._func(*args, **kwargs) # this line is the problem, self is not bound
def register_callback(self, func):
# Here I can register another callback for the decorated function
if len(self._callbacks) < self._max_num_callbacks:
self._callbacks.append(func)
else:
raise RuntimeError(f"Can not register any more callbacks for {self._func.__name__}")
return func
class MethodsWithCallbacksRegistry:
def __init__(self):
self.registry = {} # Keep track of everything that accepts callbacks
def accept_callbacks(self, max_num_callbacks: int = 1):
def _make_accept_callbacks(func):
# Convert func to an CallbackAcceptor instance so we can register callbacks on it
if func.__name__ not in self.registry:
self.registry[func.__name__] = CallbackAcceptor(max_num_callbacks=max_num_callbacks, func=func)
return self.registry[func.__name__]
return _make_accept_callbacks
函数的一切都按预期工作,但是当我修饰一个 class 方法时它会中断,因为 class 实例未绑定到修饰的方法:
registry = MethodsWithCallbacksRegistry()
@registry.accept_callbacks(max_num_callbacks=1)
def bar(i):
return i * 10
@bar.register_callback
def bar_callback(*args, **kwargs):
print("Bar Callback")
print(bar(i=10)) # Works fine, prints "Bar Callback" and then 100
现在如果我定义一个接受回调的方法:
class Test:
@registry.accept_callbacks(max_num_callbacks=1)
def foo(self, i):
return i * 2
@Test.foo.register_callback
def foo_callback(*args, **kwargs):
print("Foo Callback")
如果我显式传递 self,它会起作用,但如果我只是假设实例已绑定,则不会起作用:
t = Test()
# Note that I pass the instance of t explicitly as self
Test.foo(t, i=5) # Works, prints "Foo Callback" and then 10
t.foo(t, i=5) # Works, prints "Foo Callback" and then 10
t.foo(i=5) # Crashes, because self is not passed to foo
这是回溯:
Traceback (most recent call last):
File "/home/veith/.PyCharmCE2019.3/config/scratches/scratch_4.py", line 62, in <module>
t.foo(i=5)
File "/home/veith/.PyCharmCE2019.3/config/scratches/scratch_4.py", line 13, in __call__
return self._func(*args, **kwargs) # this line is the problem, self is not bound
TypeError: foo() missing 1 required positional argument: 'self'
我一直认为 t.foo(i=5)
基本上是 Test.foo(t, i=5)
通过描述符的语法糖,但看来我错了。所以这是我的问题:
- 这没有按预期工作的原因是什么?
- 我需要做什么才能让它发挥作用?
谢谢!
PS:我用的是python3.8
如果您将 CallbackAcceptor
设为 descriptor,它的工作原理如下:
class CallbackAcceptor:
def __init__(self, max_num_callbacks, func):
self._func = func
self._max_num_callbacks = max_num_callbacks
self._callbacks = []
def __call__(self, *args, **kwargs):
# This ends up being called when the decorated method is called
for callback in self._callbacks:
print(f"Calling {callback.__name__}({args}, {kwargs})")
callback(*args, **kwargs)
return self._func(*args, **kwargs)
def register_callback(self, func):
# Here I can register another callback for the decorated function
if len(self._callbacks) < self._max_num_callbacks:
self._callbacks.append(func)
else:
raise RuntimeError(f"Can not register any more callbacks for {self._func.__name__}")
return func
# Implementing __get__ makes this a descriptor
def __get__(self, obj, objtype=None):
if obj is not None:
# the call is made on an instance, we can pass obj as the self of the function that will be called
return functools.partial(self.__call__, obj)
# Called on a class or a raw function, just return self so we can register more callbacks
return self
现在可以正常调用了:
print(bar(i=10))
# Bar Callback
# 100
t = Test()
t.foo(i=5)
# Foo Callback
# 10
t.foo(t, i=5)
# TypeError: foo() got multiple values for argument 'i'