我有一个带有多个方法的自定义类,这些方法都返回一个代码。我希望使用标准逻辑,以针对该方法的可接受代码列表检查返回的代码,并在不期望的情况下引发错误。
我认为实现此目的的一种好方法是使用装饰器:
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
装饰器可能已经为我解决了这个问题,但是我认为我对它的工作原理存在根本的误解。
答案 0 :(得分:1)
使用__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