使用装饰器将所有函数包装成 "if func returned false, return false"
Use decorators to wrap all functions with "if func returned false, return false"
我正在编写一个非常基本的 Python 脚本,该脚本基于一个 main
函数,该函数顺序调用其他函数。
我想做的是将所有从 main
调用的函数包装成如下内容:
result = func(*some_args):
if (result != True):
return result
例如,对于此代码:
def func1(arg1 , arg2):
if (some_cond):
return False #Or some err_val
return True
def func2(arg1):
if (some_cond):
return False
return True
def main():
func1(val1 , val2)
func2(val3)
return True
if __name__ == "__main__":
import sys
result = main()
if (result == err_val1):
# Do something. Maybe print. Maybe call some function.
sys.exit(1)
我希望如果其中一个函数失败 main
会中断并且 return 它会出错。我可以使用装饰器来做到这一点吗?
我想,您真正想要的是一个通用的异常捕获器,它可以捕获并 return 任何包装函数的异常。你可以很容易地这样做。
def return_exception(func):
def wrapper(*args, **kwargs):
try:
return func(*args, **kwargs)
except Exception as e:
return e
return wrapper
例子
In [3]: @return_exception
...: def div(a, b):
...: return a / b
...:
In [4]: div(1, 0)
Out[4]: ZeroDivisionError('division by zero')
这样您就可以按照您想要的方式处理 return 异常对象,尽管很难说出您为什么需要它。
Update 正如其他人指出的那样,通常只捕获特定的异常是好的。您可以稍微修改装饰器。
def return_exception(*exception_types):
def build_wrapper(func):
def wrapper(*args, **kwargs):
try:
return func(*args, **kwargs)
except exception_types as e:
return e
return wrapper
return build_wrapper
示例:
In [6]: @return_exception(ZeroDivisionError)
...: def div(a, b):
...: return a / b
...:
In [7]: div(0, 1)
Out[7]: 0.0
In [8]: div(1, 0)
Out[8]: ZeroDivisionError('division by zero')
In [9]: div(1, "a")
---------------------------------------------------------------------------
TypeError Traceback (most recent call last)
...
TypeError: unsupported operand type(s) for /: 'int' and 'str'
In [10]: @return_exception(ZeroDivisionError, TypeError)
....: def div(a, b):
....: return a / b
....:
In [11]: div(1, 0)
Out[11]: ZeroDivisionError('division by zero')
In [12]: div(1, "a")
Out[12]: TypeError("unsupported operand type(s) for /: 'int' and 'str'")
如您所见,您只捕获指定的异常(不过您仍然可以指定通用 Exception
class)。
我认为最好的解决方案是使用异常。如果这绝对不是你想要的,你可以做一些短路:
return func1() and func2()
要将其扩展到更多功能而无需大量 and
s:
from functools import partial
def main():
funcs = (partial(func1, arg1, arg2),
partial(func2, arg1))
if any(!f() for f in funcs):
return False
虽然这不是 return "its error"(函数失败的错误),但它只是 returns False
。如果您想更多地区分不同类型的错误...好吧,您又回到了异常。
这正是 Python.
中构建异常的原因
# imports belong at the *top* of the file
import sys
class SomeDescriptiveError(Exception): pass
class SomeOtherSpecialError(Exception): pass
def func1(arg1 , arg2):
if (some_cond):
raise SomeDescriptiveError('Cannot frobnosticate the fizzbuzz')
return arg1 + arg2
# or skip the return statement altogether
def func2(arg1):
if (some_cond):
raise SomeOtherSpecialError('The frontobulator is no longer cromulent')
return ''.join(reversed(arg1))
def main():
print(func1(val1 , val2))
print(func2(val3))
if __name__ == "__main__":
try:
result = main()
except SomeDescriptiveError as e:
print('Oh dear')
sys.exit(e.args[0])
except SomeOtherSpecialError as e:
print('Oh no')
sys.exit(e.args[0])
else:
print('All systems are fully operational')
finally:
print('I really should clean up all these bits.')
由于您确实实际上希望程序在发生这些错误之一时终止,您不妨提出SystemExit
。这是使用装饰器实现的方法。
flag = 2
def die_on_not_True(func):
def wrapper(*args):
rc = func(*args)
if rc is not True:
fmt = 'Function {} failed with return value {!r}'
print(fmt.format(func.__name__, rc))
raise SystemExit(1)
return True
return wrapper
@die_on_not_True
def func1(arg1 , arg2):
if arg1 == flag:
return 'error 1'
return True
@die_on_not_True
def func2(arg1):
if arg1 == flag:
return 'error 2'
return True
def main():
val1, val2, val3 = 1, 2, 3
print(func1(val1, val2))
print('one')
print(func2(val3))
print('two')
if __name__ == '__main__':
main()
输出
True
one
True
two
如果我们设置flag = 1
,输出变为
Function func1 failed with return value 'error 1'
如果我们设置flag = 3
,输出变为
True
one
Function func2 failed with return value 'error 2'
当flag
等于2时,返回0的退出状态为shell,当flag
等于1或3时,返回1的退出状态。
如果您想在打印错误消息后进行进一步处理,则引发自定义异常而不是 SystemExit
并通过将 main
调用包装在 try...except
中来捕获它。
我正在编写一个非常基本的 Python 脚本,该脚本基于一个 main
函数,该函数顺序调用其他函数。
我想做的是将所有从 main
调用的函数包装成如下内容:
result = func(*some_args):
if (result != True):
return result
例如,对于此代码:
def func1(arg1 , arg2):
if (some_cond):
return False #Or some err_val
return True
def func2(arg1):
if (some_cond):
return False
return True
def main():
func1(val1 , val2)
func2(val3)
return True
if __name__ == "__main__":
import sys
result = main()
if (result == err_val1):
# Do something. Maybe print. Maybe call some function.
sys.exit(1)
我希望如果其中一个函数失败 main
会中断并且 return 它会出错。我可以使用装饰器来做到这一点吗?
我想,您真正想要的是一个通用的异常捕获器,它可以捕获并 return 任何包装函数的异常。你可以很容易地这样做。
def return_exception(func):
def wrapper(*args, **kwargs):
try:
return func(*args, **kwargs)
except Exception as e:
return e
return wrapper
例子
In [3]: @return_exception
...: def div(a, b):
...: return a / b
...:
In [4]: div(1, 0)
Out[4]: ZeroDivisionError('division by zero')
这样您就可以按照您想要的方式处理 return 异常对象,尽管很难说出您为什么需要它。
Update 正如其他人指出的那样,通常只捕获特定的异常是好的。您可以稍微修改装饰器。
def return_exception(*exception_types):
def build_wrapper(func):
def wrapper(*args, **kwargs):
try:
return func(*args, **kwargs)
except exception_types as e:
return e
return wrapper
return build_wrapper
示例:
In [6]: @return_exception(ZeroDivisionError)
...: def div(a, b):
...: return a / b
...:
In [7]: div(0, 1)
Out[7]: 0.0
In [8]: div(1, 0)
Out[8]: ZeroDivisionError('division by zero')
In [9]: div(1, "a")
---------------------------------------------------------------------------
TypeError Traceback (most recent call last)
...
TypeError: unsupported operand type(s) for /: 'int' and 'str'
In [10]: @return_exception(ZeroDivisionError, TypeError)
....: def div(a, b):
....: return a / b
....:
In [11]: div(1, 0)
Out[11]: ZeroDivisionError('division by zero')
In [12]: div(1, "a")
Out[12]: TypeError("unsupported operand type(s) for /: 'int' and 'str'")
如您所见,您只捕获指定的异常(不过您仍然可以指定通用 Exception
class)。
我认为最好的解决方案是使用异常。如果这绝对不是你想要的,你可以做一些短路:
return func1() and func2()
要将其扩展到更多功能而无需大量 and
s:
from functools import partial
def main():
funcs = (partial(func1, arg1, arg2),
partial(func2, arg1))
if any(!f() for f in funcs):
return False
虽然这不是 return "its error"(函数失败的错误),但它只是 returns False
。如果您想更多地区分不同类型的错误...好吧,您又回到了异常。
这正是 Python.
中构建异常的原因# imports belong at the *top* of the file
import sys
class SomeDescriptiveError(Exception): pass
class SomeOtherSpecialError(Exception): pass
def func1(arg1 , arg2):
if (some_cond):
raise SomeDescriptiveError('Cannot frobnosticate the fizzbuzz')
return arg1 + arg2
# or skip the return statement altogether
def func2(arg1):
if (some_cond):
raise SomeOtherSpecialError('The frontobulator is no longer cromulent')
return ''.join(reversed(arg1))
def main():
print(func1(val1 , val2))
print(func2(val3))
if __name__ == "__main__":
try:
result = main()
except SomeDescriptiveError as e:
print('Oh dear')
sys.exit(e.args[0])
except SomeOtherSpecialError as e:
print('Oh no')
sys.exit(e.args[0])
else:
print('All systems are fully operational')
finally:
print('I really should clean up all these bits.')
由于您确实实际上希望程序在发生这些错误之一时终止,您不妨提出SystemExit
。这是使用装饰器实现的方法。
flag = 2
def die_on_not_True(func):
def wrapper(*args):
rc = func(*args)
if rc is not True:
fmt = 'Function {} failed with return value {!r}'
print(fmt.format(func.__name__, rc))
raise SystemExit(1)
return True
return wrapper
@die_on_not_True
def func1(arg1 , arg2):
if arg1 == flag:
return 'error 1'
return True
@die_on_not_True
def func2(arg1):
if arg1 == flag:
return 'error 2'
return True
def main():
val1, val2, val3 = 1, 2, 3
print(func1(val1, val2))
print('one')
print(func2(val3))
print('two')
if __name__ == '__main__':
main()
输出
True
one
True
two
如果我们设置flag = 1
,输出变为
Function func1 failed with return value 'error 1'
如果我们设置flag = 3
,输出变为
True
one
Function func2 failed with return value 'error 2'
当flag
等于2时,返回0的退出状态为shell,当flag
等于1或3时,返回1的退出状态。
如果您想在打印错误消息后进行进一步处理,则引发自定义异常而不是 SystemExit
并通过将 main
调用包装在 try...except
中来捕获它。