如何避免python decorators函数中的名称冲突

2024-09-27 21:23:12 发布

您现在位置:Python中文网/ 问答频道 /正文

我想编写一个python修饰符,这样一个引发异常的函数将再次运行,直到它成功,或者达到放弃之前的最大尝试次数。你知道吗

像这样:

def tryagain(func):
    def retrier(*args,**kwargs,attempts=MAXIMUM):
        try:
            return func(*args,**kwargs)
        except Exception as e:
            if numberofattempts > 0:
                logging.error("Failed. Trying again")
                return retrier(*args,**kwargs,attempts=attempts-1)
            else:
                logging.error("Tried %d times and failed, giving up" % MAXIMUM)
                raise e
    return retrier

我的问题是,我需要一个保证,即无论kwargs包含什么名称,都不能与用于表示尝试次数的名称发生冲突。你知道吗

但是,当函数本身将attempts作为关键字参数时,这不起作用

@tryagain
def other(a,b,attempts=c):
    ...
    raise Exception

other(x,y,attempts=z)

在本例中,如果运行other,它将运行z次,而不是最大运行次数(请注意,要发生此错误,必须在调用中显式使用关键字参数!)。你知道吗


Tags: 函数returnloggingdefexceptionargserror次数
2条回答

从函数属性获取重试次数,而不是参数。你知道吗

def tryagain(func):
    def retrier(*args,**kwargs):
        retries = getattr(func, "attempts", MAXIMUM)
        while retries + 1 > 0:
            try:
                return func(*args, **kwargs)
            except Exception as e:
                logging.error("Failed. Trying again")
                last_exception = e
            retries -= 1
        else:
            logging.error("Tried %d times and failed, giving up", retries)
            raise last_exception

    return retrier

@tryagain
def my_func(...):
    ...

my_func.attempts = 10
my_func()  # Will try it 10 times

要使MAXIMUM成为调用函数时可以指定的内容,请将定义更改为

def tryagain(maximum=10):
    def _(f):
        def retrier(*args, **kwargs):
            retries = getattr(func, "attempts", maximum)
            while retries + 1 > 0:
                try:
                    return func(*args, **kwargs)
                except Exception as e:
                    logging.error("Failed. Trying again")
                    last_exception = e
                retries -= 1
            else:
                logging.error("Tried %d times and failed, giving up", retries)
                raise last_exception
        return retrier
    return _

尽管仍然存在与attempts属性发生名称冲突的风险,但是函数属性很少被使用这一事实使得将tryagain文档化为不使用具有预先存在的attempts属性的函数更为合理。你知道吗

(我把它作为一个练习来修改tryagain以获取一个属性名作为参数:

@tryagain(15, 'max_retries')
def my_func(...):
    ...

以便您可以在装修时选择一个未使用的名称。因此,还可以使用tryagain的参数作为要添加到my_func的关键字参数的名称。)

您可以指定decorator参数,如下所示:

import logging

MAXIMUM = 5

def tryagain(attempts=MAXIMUM):
    def __retrier(func):
        def retrier(*args,**kwargs):
            nonlocal attempts
            while True:
                try:
                    return func(*args,**kwargs)
                except Exception as e:
                    attempts -= 1
                    if attempts > 0:
                        print('Failed, attempts left=', attempts)
                        continue
                    else:
                        print('Giving up')
                        raise
        return retrier
    return __retrier


@tryagain(5)                              # <  this specifies number of attempts
def fun(attempts='This is my parameter'): # <  here the function specifies its own `attempts` parameter, unrelated to decorator
    raise Exception(attempts)

fun()

相关问题 更多 >

    热门问题