[英]Using decorators for AssertionError exception handling in Python
这是我在这里的第一个问题,请告诉我是否正确询问。 :X
我试图更好地理解 Python AssertionError 异常处理和装饰器,我想知道是否可以通过装饰器替换 function 中的一系列嵌套 try/except/else 块......
例子:
我通常会做的事情:
# CHECK functions:
def is_int(x):
assert isinstance(x,int), f"'{x}' is not an integer"
return True
def is_str(y):
assert isinstance(y,str), f"'{y}' is not a string"
return True
# FUNCTION I want to decorate
def pretty_function(x:int, y:str):
try:
is_int(x)
except AssertionError as exc:
print(exc)
else:
try:
is_str(y)
except AssertionError as exc:
print(exc)
else:
print(f'There is/are {x} {y}')
输入:
pretty_function(2,'cat')
pretty_function(2,3)
pretty_function('2','cat')
Output:
There is/are 2 cat
'3' is not a string
'2' is not an integer
所以这工作正常,但我想改用装饰器......尤其是当我有超过 2 个嵌套的 try/except/else 块时。
我想做的事:
# DECORATOR ---> Don't know how to make it work as I want
def test_assertion(test_function):
def wrapper(*args, **kwargs):
try:
test_function(*args, **kwargs)
except AssertionError as exc:
print(exc)
else:
return test_function(*args, **kwargs)
return wrapper
# CHECK functions --> No idea how to write these correctly
@test_assertion
def is_int(func):
def wrapper(x):
assert isinstance(x,int), f"'{x}' is not an integer"
return True
return wrapper
@test_assertion
def is_str(func):
def wrapper(y):
assert isinstance(y,str), f"'{y}' is not a string"
return True
return wrapper
# FUNCTION I want to decorate
@is_int(x)
@is_str(y)
def pretty_function(x:int, y:str):
print(f'There is/are {x} {y}')
输入:
pretty_function(2,'cat')
pretty_function(2,3)
pretty_function('2','cat')
Output:
# What I get so far:
---------------------------------------------------------------------------
NameError Traceback (most recent call last)
/var/folders/r9/nj5lw_gj0fvfp4bsmcy587ch0000gn/T/ipykernel_2388/1180576500.py in <module>
28
29 # FUNCTION I want to decorate
---> 30 @is_int(x)
31 @is_str(y)
32 def pretty_function(x:int, y:str):
NameError: name 'x' is not defined
所以我的实验代码不起作用……:'(
然而,第一个装饰器似乎工作:
如果我只是输入:
# DECORATOR
def test_assertion(test_function):
def wrapper(*args, **kwargs):
try:
test_function(*args, **kwargs)
except AssertionError as exc:
print(exc)
else:
return test_function(*args, **kwargs)
return wrapper
# CHECK functions
@test_assertion
def is_int(x):
assert isinstance(x,int), f"'{x}' is not an integer"
return True
@test_assertion
def is_str(y):
assert isinstance(y,str), f"'{y}' is not a string"
return True
输入:
print(is_int(2))
print(is_int('2'))
print(is_str(2))
print(is_str('2'))
我得到这个 output:
True
'2' is not an integer
None
'2' is not a string
None
True
问题是,到目前为止,我只发现了 else 语句不存在的情况......以及对于初学者来说太复杂而无法理解的情况。 :/
任何想法?
一个问题是您的装饰者事先不知道x
和y
是什么。 这可以使用'x'
和'y'
来排序,而不是x
和y
。 但是,如果您也修改 function 签名,以强制 arguments 为keyword only
,则会简单得多。 一种方法是:
def assert_type(kwd, _type):
def decorator(func):
def wrapper(**kwargs):
assert isinstance(kwargs[kwd], _type), f"{kwargs[kwd]} not a {_type.__name__}"
return func(**kwargs)
return wrapper
return decorator
@assert_type('x', int)
@assert_type('y', str)
def pretty_function(*, x:int, y:str):
print(f'There is/are {x} {y}')
更一般的情况是:
def assert_types(**mapping):
def decorator(func):
def wrapper(**kwargs):
for kwd, _type in mapping.items():
assert isinstance(kwargs[kwd], _type), f"{kwargs[kwd]} not a {_type.__name__}"
return func(**kwargs)
return wrapper
return decorator
@assert_types(x=int, y=str)
def pretty_function(*, x:int, y:str):
print(f'There is/are {x} {y}')
然后你打电话:
pretty_function(x=2,y='cat')
pretty_function(x=2,y=3)
pretty_function(x='2',y='cat')
要获得所需的程序行为(您仍然必须使用'x'
和'y'
),可以使用__annotations__
的 __annotations__ 属性来获取 args 的名称。
from functools import wraps
def is_int(arg):
def decorator(func):
@wraps(func)
def wrapper(*args, **kwargs):
params = (*args, *kwargs.values())
mapping = dict(zip(func.__annotations__, params))
assert isinstance(mapping[arg], int), f"{mapping[arg]} not an integer"
return func(*args, **kwargs)
return wrapper
return decorator
def is_str(arg):
def decorator(func):
@wraps(func)
def wrapper(*args, **kwargs):
params = (*args, *kwargs.values())
mapping = dict(zip(func.__annotations__, params))
assert isinstance(mapping[arg], str), f"{mapping[arg]} not a string"
return func(*args, **kwargs)
return wrapper
return decorator
@is_int('x')
@is_str('y')
def pretty_function(x:int, y:str):
print(f'There is/are {x} {y}')
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.