装饰器带参数

装饰器携带参数,可以实现更加强大的装饰器,本篇教程以http请求重试场景来讲解如何编写带参数的装饰器。

1. 重试场景

假设这样一个场景,你写了一个函数,用来从一个API接口获取数据,那么你必须考虑网络问题,也许网关解析会出错,也许API接口压力太大,短时间内响应超时,面对这种情况,你的函数应该具备重试功能。

请求失败以后,暂停一定时间,再次发起请求,重试的次数必须是可设置的。你可以把这个功能在函数中实现,但可以肯定,这不是一个好方法,原因在于每一个这样的函数你都需要实现一遍,而且不同的函数访问不同的API,暂停的时间以及重试的次数都有可能不同。

这种请求,就非常适合用装饰器来解决

2. 带参数装饰器

from functools import wraps


def retry(retry_count=5, sleep_time=1):
    def wrapper(func):
        @wraps(func)
        def inner(*args, **kwargs):
            for i in range(retry_count):
                try:
                    res = func(*args, **kwargs)
                    return res
                except:
                    time.sleep(sleep_time)
                    continue
            return None
        return inner
    return wrapper

这段代码一共嵌套着定义了3个函数,在讲解作用域的时候强调过,嵌套作用域是一个相对概念,在函数inner里可以访问更上层的局部作用域里的数据,retry_count是函数retry的一个参数,在inner里根据retry_count来决定重试的次数,只要发生了异常,就会重试。

3. 如何使用

写爬虫时,你不得不考虑网络因素,一次请求可能无法得到所需要的响应数据,这时就需要重试

class HttpCodeException(Exception):
    pass
    
@retry()
def get_html(url):
    headers = {
        'User-Agent': 'Mozilla/5.0 (Linux; Android 6.0; Nexus 5 Build/MRA58N) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/73.0.3683.87 Mobile Safari/537.36',
        'Host': 'movie.douban.com',
        'Accept': 'text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3',
        'Referer': 'https://movie.douban.com/top250?start=25&filter='
    }

    res = requests.get(url, headers=headers)
    if res.status_code != 200:
        raise HttpCodeException

    return res.text

这段代码是我编写过的一个爬虫的一小部分,在其他场景下,只要涉及到重试,都可以使用我写的这个装饰器。

扫描关注, 与我技术互动

QQ交流群: 211426309

加入知识星球, 每天收获更多精彩内容

分享日常研究的python技术和遇到的问题及解决方案