如何正确使用 python 中的 class 装饰器链?
How properly work with chain of class decorators in python?
我目前正在学习装饰器并了解,装饰器可以通过两种方式创建:作为函数和作为 class。作为 class 我从 this answer 指导
阅读 pep318 中的示例,我决定重写示例 4,检查函数的属性和 return 值类型。下面是一些代码:
from inspect import signature
def accepts(*types):
def check_types(f):
f_args = list(signature(f).parameters)
assert len(types) == len(f_args)
def inner_f(*args, **kwargs):
for (a, t) in zip(args, types):
assert isinstance(a, t), f"arg {a} doesn't match {t}"
return f(*args, **kwargs)
inner_f.__name__ = f.__name__
return inner_f
return check_types
def returns(rtype):
def check_returns(f):
def new_f(*args, **kwargs):
result = f(*args, **kwargs)
assert isinstance(result, rtype), f"return value {result} doesn't match {rtype}"
new_f.__name__ = f.__name__
return new_f
return check_returns
class CheckTypes(object):
def __init__(self, func, *types):
self._func = func
self._types = types
f_args = list(signature(self._func).parameters)
assert len(types) == len(f_args)
def __call__(self, *args, **kwargs):
for number, (a, t) in enumerate(zip(args, self._types)):
assert isinstance(a, t), f"{number} arg {a} with type {type(a)} doesn't match {t}"
class ExternalWrapperCheckTypes(object):
def __init__(self, *types):
self._types = types
def __call__(self, func):
return CheckTypes(func, *self._types)
class CheckReturns(object):
def __init__(self, func, *types):
self._func = func
self._types = types
def __call__(self, *args, **kwargs):
result = self._func(*args, **kwargs)
assert isinstance(result, self._types), f"return value {result} doesn't match {self._types}"
class ExternalWrapperCheckReturns(object):
def __init__(self, *types):
self._types = types
def __call__(self, func):
return CheckReturns(func, *self._types)
@accepts(int, (int, float))
@returns((int,))
def decorated_by_functions(arg1, arg2):
return "Incorrect output"
@ExternalWrapperCheckTypes(int, (int, float))
@ExternalWrapperCheckReturns((int,))
def decorated_by_classes(arg1, arg2):
return "Incorrect output"
def main():
res1 = decorated_by_functions (42, 42.42) # AssertionError: return value s doesn't match (<class 'int'>,)
res2 = decorated_by_classes(42, 42.42) # Ignore assertion
那么,在什么问题上? decorated_by_functions
会按预期导致断言错误,但 decorated_by_classes
会忽略断言。在我看来 - 两种方法中重载函数 __call__
的问题,我可能会 return class 或其他东西的实例,但是当我 return 它时 - 行为不'改变了。
更新
实际上,我的直觉是正确的 - 我需要 return 正确的对象,感谢 pythontips 这是我们的函数,用参数调用:self._func(*args, **kwargs)
。感谢大家的关注和您的宝贵时间!
最终解决方案有这样的看法:
from inspect import signature
class CheckTypes(object):
def __init__(self, func, *types):
self._func = func
self._types = types
f_args = list(signature(self._func).parameters)
assert len(types) == len(f_args)
def __call__(self, *args, **kwargs):
for number, (a, t) in enumerate(zip(args, self._types)):
assert isinstance(a, t), f"{number} arg {a} with type {type(a)} doesn't match {t}"
return self._func(*args, **kwargs)
class ExternalWrapperCheckTypes(object):
def __init__(self, *types):
self._types = types
def __call__(self, func, *args, **kwargs):
return CheckTypes(func, *self._types)
class CheckReturns(object):
def __init__(self, func, *types):
self._func = func
self._types = types
def __call__(self, *args, **kwargs):
result = self._func(*args, **kwargs)
assert isinstance(result, self._types), f"return value {result} doesn't match {self._types}"
return self._func(*args, **kwargs)
class ExternalWrapperCheckReturns(object):
def __init__(self, *types):
self._types = types
def __call__(self, func, *args, **kwargs):
return CheckReturns(func, *self._types)
@ExternalWrapperCheckTypes(int, (int, float))
@ExternalWrapperCheckReturns((int, ))
def decorated_by_classes(arg1, arg2):
return "Incorrect output"
def main():
ans = decorated_by_classes(42, 42.42) # AssertionError: return value s doesn't match (<class 'int'>,)
print(ans)
我目前正在学习装饰器并了解,装饰器可以通过两种方式创建:作为函数和作为 class。作为 class 我从 this answer 指导 阅读 pep318 中的示例,我决定重写示例 4,检查函数的属性和 return 值类型。下面是一些代码:
from inspect import signature
def accepts(*types):
def check_types(f):
f_args = list(signature(f).parameters)
assert len(types) == len(f_args)
def inner_f(*args, **kwargs):
for (a, t) in zip(args, types):
assert isinstance(a, t), f"arg {a} doesn't match {t}"
return f(*args, **kwargs)
inner_f.__name__ = f.__name__
return inner_f
return check_types
def returns(rtype):
def check_returns(f):
def new_f(*args, **kwargs):
result = f(*args, **kwargs)
assert isinstance(result, rtype), f"return value {result} doesn't match {rtype}"
new_f.__name__ = f.__name__
return new_f
return check_returns
class CheckTypes(object):
def __init__(self, func, *types):
self._func = func
self._types = types
f_args = list(signature(self._func).parameters)
assert len(types) == len(f_args)
def __call__(self, *args, **kwargs):
for number, (a, t) in enumerate(zip(args, self._types)):
assert isinstance(a, t), f"{number} arg {a} with type {type(a)} doesn't match {t}"
class ExternalWrapperCheckTypes(object):
def __init__(self, *types):
self._types = types
def __call__(self, func):
return CheckTypes(func, *self._types)
class CheckReturns(object):
def __init__(self, func, *types):
self._func = func
self._types = types
def __call__(self, *args, **kwargs):
result = self._func(*args, **kwargs)
assert isinstance(result, self._types), f"return value {result} doesn't match {self._types}"
class ExternalWrapperCheckReturns(object):
def __init__(self, *types):
self._types = types
def __call__(self, func):
return CheckReturns(func, *self._types)
@accepts(int, (int, float))
@returns((int,))
def decorated_by_functions(arg1, arg2):
return "Incorrect output"
@ExternalWrapperCheckTypes(int, (int, float))
@ExternalWrapperCheckReturns((int,))
def decorated_by_classes(arg1, arg2):
return "Incorrect output"
def main():
res1 = decorated_by_functions (42, 42.42) # AssertionError: return value s doesn't match (<class 'int'>,)
res2 = decorated_by_classes(42, 42.42) # Ignore assertion
那么,在什么问题上? decorated_by_functions
会按预期导致断言错误,但 decorated_by_classes
会忽略断言。在我看来 - 两种方法中重载函数 __call__
的问题,我可能会 return class 或其他东西的实例,但是当我 return 它时 - 行为不'改变了。
更新
实际上,我的直觉是正确的 - 我需要 return 正确的对象,感谢 pythontips 这是我们的函数,用参数调用:self._func(*args, **kwargs)
。感谢大家的关注和您的宝贵时间!
最终解决方案有这样的看法:
from inspect import signature
class CheckTypes(object):
def __init__(self, func, *types):
self._func = func
self._types = types
f_args = list(signature(self._func).parameters)
assert len(types) == len(f_args)
def __call__(self, *args, **kwargs):
for number, (a, t) in enumerate(zip(args, self._types)):
assert isinstance(a, t), f"{number} arg {a} with type {type(a)} doesn't match {t}"
return self._func(*args, **kwargs)
class ExternalWrapperCheckTypes(object):
def __init__(self, *types):
self._types = types
def __call__(self, func, *args, **kwargs):
return CheckTypes(func, *self._types)
class CheckReturns(object):
def __init__(self, func, *types):
self._func = func
self._types = types
def __call__(self, *args, **kwargs):
result = self._func(*args, **kwargs)
assert isinstance(result, self._types), f"return value {result} doesn't match {self._types}"
return self._func(*args, **kwargs)
class ExternalWrapperCheckReturns(object):
def __init__(self, *types):
self._types = types
def __call__(self, func, *args, **kwargs):
return CheckReturns(func, *self._types)
@ExternalWrapperCheckTypes(int, (int, float))
@ExternalWrapperCheckReturns((int, ))
def decorated_by_classes(arg1, arg2):
return "Incorrect output"
def main():
ans = decorated_by_classes(42, 42.42) # AssertionError: return value s doesn't match (<class 'int'>,)
print(ans)