[英]How to use decorators on overridable class methods
我有一个带有多个方法的自定义类,这些方法都返回一个代码。 我想要标准逻辑,根据该方法的可接受代码列表检查返回的代码,并在不符合预期的情况下引发错误。
我认为实现这一目标的好方法是使用装饰器:
from functools import wraps
def expected_codes(codes):
def decorator(f):
@wraps(f)
def wrapper(*args, **kwargs):
code = f(*args, **kwargs)
if code not in codes:
raise Exception(f"{code} not allowed!")
else:
return code
return wrapper
return decorator
然后我有一个这样的课程:
class MyClass:
@expected_codes(["200"])
def return_200_code(self):
return "200"
@expected_codes(["300"])
def return_300_code(self):
return "301" # Exception: 301 not allowed!
这工作正常,但是如果我覆盖基类:
class MyNewClass:
@expected_codes(["300", "301"])
def return_300_code(self):
return super().return_300_code() # Exception: 301 not allowed!
由于被覆盖的装饰器,我本来希望上述覆盖的方法正确返回而不是引发异常。
从我通过阅读收集的信息来看,我想要的方法不起作用,因为装饰器正在类定义中进行评估 - 但是我很惊讶没有办法实现我想要的。 这一切都在 Django 应用程序的上下文中,我认为 Django 的method_decorator
装饰器可能已经为我解决了这个问题,但我认为我对它的工作原理有一个根本的误解。
使用__wrapped__
属性忽略父装饰器:
class MyNewClass(MyClass):
@expected_codes(["300", "301"])
def return_300_code(self):
return super().return_300_code.__wrapped__(self) # No exception raised
@decorator
语法等效于:
def f():
pass
f = decorator(f)
因此,您可以堆叠装饰器:
def decorator(f):
@wraps(f)
def wrapper(*args, **kwargs):
print(f"Calling {f.__name__}")
f(*args, **kwargs)
return wrapper
@decorator
def f():
print("Hi!")
@decorator
def g():
f()
g()
#Calling g
#Calling f
#Hi!
但是如果你想避免堆积, __wrapped__
属性是你的朋友:
@decorator
def g():
f.__wrapped__()
g()
#Calling g
#Hi!
简而言之,如果您在子类的装饰方法中调用装饰的父方法之一,装饰器将堆叠,而不是相互覆盖。
因此,当您调用super().return_300_code()
您正在调用父类的装饰方法,该方法不接受301
作为有效代码,并将引发自己的异常。
如果您想重用原始父级的方法,即直接返回301
而不检查的方法,您可以使用__wrapped__
属性来访问原始函数(在装饰之前):
class MyNewClass(MyClass):
@expected_codes(["300", "301"])
def return_300_code(self):
return super().return_300_code.__wrapped__(self) # No exception raised
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.