TL; DR
Используйте атрибут __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