“大救星”重试装饰器!助你解决各种“莫名其妙”的麻烦事

全文共1871字,预计学习时长8分钟


“大救星”重试装饰器!助你解决各种“莫名其妙”的麻烦事

图源:unsplash


“胶水语言”,顾名思义,意味着这种语言有助于将连接系统,确保数据能以所需结构和格式,由A转到B。Python就是知名度最高的一种胶水语言。笔者曾用Python构建了无数的ETL脚本(数据提取、转换和加载)。


这些脚本基本上都按照相同规则运行。从某处提取数据,数据转化,然后运行最后一步——将数据上传到某个地方,但也可能是有条件的删除。


越来越多的传统公司的基础设施正在被上传到云端,越来越多的公司正在向微服务方式转型。这也意味着一种情况出现的可能大大增加:必须从某个地方提取数据,或从非本地计算机处写入数据。


“大救星”重试装饰器!助你解决各种“莫名其妙”的麻烦事

图源:unsplash


小规模的操作很少出问题。如果某些提取或者回写失败,通常会被注意到,且错误能被纠正。


但如果操作规模更大,并存在成百上千的可能事项时,就会出现暂时中断的网络连接、过多的并发写入、暂时没有相应的源系统或者其他莫名其妙的问题,而没人想被这些事情搞砸。而笔者发现了一个非常简单的重试装饰器,它能解决这些问题。


笔者在大多数项目中都会在某个功能模块中使用重试装饰器,可谓是“大救星”。快来看看吧!


“大救星”重试装饰器!助你解决各种“莫名其妙”的麻烦事

装饰器


“大救星”重试装饰器!助你解决各种“莫名其妙”的麻烦事

图源:unsplash


函数是一级对象


在Python中,函数是一级对象。函数和其他任何对象一样。这一事实意味着函数可以动态创建、传递给函数本身,甚至可以更改。下面这个示例尽管很傻但确是事实:


<code>

def

my_function

(x)

:

print(x)IN: my_function(

2

) OUT:

2

IN: my_function.yolo =

'you live only once'

print(my_function.yolo) OUT:

'you live only once'

/<code>


修饰语法


为了方便开发人员,Python提供了一种特殊的语法。还可以按如下方式操作:


<code> 

def

first_func

(x)

:

return

x**

2

/<code>


以上的语法等同于:


<code>

def

first_func

(x)

:

return

x**

2

first_func =convert_to_numeric(first_func)/<code>


这种语法使实际运行情况更清楚,尤其是在使用多个装饰器的情况下。


修饰函数


可以用另一个函数包装一个函数来满足特定需求,这一点很有用。例如,可以确保函数在被调用时向某个日志端点报告,打印出参数,实现类型检查、预处理或后处理,以列出一些可能性。以下为一个简单示例:


<code>

def

first_func

(x)

:

return

x**

2

def

second_func

(x)

:

return

x -

2

/<code>


当用字符串'2'调用这两个函数时,其都会无法工作。可在其中使用一个类型转换函数,并且用其修饰first_func 和 second_func 。


<code>

def

convert_to_numeric

(func)

:

def

new_func

(x)

:

return

func(float(x))

return

new_func/<code>


这个convert_to_numeric 包装函数需要用一个函数作为参数,并返回另一个函数。现在,虽然先前无法工作,但是如果包装了函数,然后用一个字符串号调用,所有的函数都能按设置的使用。


<code>IN:
new_fist_func = convert_to_numeric(first_func) 
convert_to_numeric returns  

this

function: def new_func(x):

return

first_func(float(x)) OUT:

4.0

IN: convert_to_numeric(second_func)(

'2'

) OUT:

0

/<code>


这是为什么?


convert_to_numeric接受一个函数(A)作为参数并返回一个新函数(B)。新函数(B)在被调用时调用函数(A),但它不是用传递的参数x调用函数(A),而是用float(x)调用函数(A),从而解决了以前的TypeError 问题。


“大救星”重试装饰器!助你解决各种“莫名其妙”的麻烦事

图源:unsplash


“大救星”重试装饰器!助你解决各种“莫名其妙”的麻烦事

重试!


基本知识已经介绍完了,现在讲一讲笔者最喜欢的retry装饰器:


“大救星”重试装饰器!助你解决各种“莫名其妙”的麻烦事

包装已经包装的函数。是一些初始阶段的内容,这并不复杂!


来一步一步了解代码:


1.最外层函数retry:这将参数化修饰器,即要处理的异常是什么,尝试的频率是多少,两次尝试之间等待的时间是多长,以及指数退避因子是什么(即每次失败时,用什么数字乘以等待时间)。


2.retry_decorator:这是参数化的装饰器,由retry函数返回。用@wraps来修饰retry_decorator中的函数。严格来讲,这在功能上是不必要的。这个包装器更新了包装函数的 __name__ 和 __doc__ (如果不这样做,函数的 __name__ 始终是 func_with_retries)。


“大救星”重试装饰器!助你解决各种“莫名其妙”的麻烦事

图源:unsplash


3.func_with_retries应用重试逻辑。此函数包装了try-except语句块中的函数调用,并实现指数退避等待和一些日志记录。


用途:


“大救星”重试装饰器!助你解决各种“莫名其妙”的麻烦事

函数用重试装饰器修饰,在异常上最多重试四次。或者更具体一点:


“大救星”重试装饰器!助你解决各种“莫名其妙”的麻烦事

用在TimeoutError上的重试装饰的函数最多重试两次。


结果:


调用修饰函数,并遇到错误,将会导致如下结果:


“大救星”重试装饰器!助你解决各种“莫名其妙”的麻烦事

调用的函数在ConnectionRefusedError上失败两次,在ConnectionResetError上失败一次,在第四次尝试时成功。这里有良好的日志记录,打印了args和kwargs以及函数名,使调试和修复问题变得轻而易举(如果重试次数用完错误依然存在)。


“大救星”重试装饰器!助你解决各种“莫名其妙”的麻烦事

图源:unsplash


了解装饰器如何在Python中工作,以及如何用一个简单的重试装饰器来修饰关键任务函数,这非常有必要的。它是面对不确定情况时的“大救星”!


“大救星”重试装饰器!助你解决各种“莫名其妙”的麻烦事

留言点赞关注

我们一起分享AI学习与发展的干货

如转载,请后台留言,遵守转载规范


分享到:


相關文章: