我想写一个装饰将限制次数可以执行的功能,以及语法如下一些:这些类型的python装饰器是如何编写的?
@max_execs(5)
def my_method(*a,**k):
# do something here
pass
我认为这是可能写这种类型的装饰,但我不不知道如何。我认为一个函数不会是这个装饰器的第一个参数,对吧?我想要一个“普通的装饰器”的实现,而不是一些有调用方法的类。
原因是要了解它们是如何书写的。请解释语法,以及装饰器的工作原理。
我想写一个装饰将限制次数可以执行的功能,以及语法如下一些:这些类型的python装饰器是如何编写的?
@max_execs(5)
def my_method(*a,**k):
# do something here
pass
我认为这是可能写这种类型的装饰,但我不不知道如何。我认为一个函数不会是这个装饰器的第一个参数,对吧?我想要一个“普通的装饰器”的实现,而不是一些有调用方法的类。
原因是要了解它们是如何书写的。请解释语法,以及装饰器的工作原理。
这是我鞭打。它不使用类,但它确实使用功能属性:
def max_execs(n=5):
def decorator(fn):
fn.max = n
fn.called = 0
def wrapped(*args, **kwargs):
fn.called += 1
if fn.called <= fn.max:
return fn(*args, **kwargs)
else:
# Replace with your own exception, or something
# else that you want to happen when the limit
# is reached
raise RuntimeError("max executions exceeded")
return wrapped
return decorator
max_execs
返回运作称为decorator
,这反过来又返回wrapped
。decoration
在两个函数属性中存储最大执行数和当前exec数,然后在wrapped
中进行检查。
翻译:当使用像这样的装饰:
@max_execs(5)
def f():
print "hi!"
你基本上做这样的事情:
f = max_execs(5)(f)
我知道你说过你不想上课,但不幸的是,这是我能想到如何从头顶开始做的唯一方法。
class mymethodwrapper:
def __init__(self):
self.maxcalls = 0
def mymethod(self):
self.maxcalls += 1
if self.maxcalls > 5:
return
#rest of your code
print "Code fired!"
火起来像这样
a = mymethodwrapper
for x in range(1000):
a.mymethod()
输出将是:
>>> Code fired!
>>> Code fired!
>>> Code fired!
>>> Code fired!
>>> Code fired!
这不能用于任何方法,并使用可调用的对象。我想要一个普通的装饰者。 – Geo 2009-07-09 20:28:36
装饰仅仅是一种把函数转换成别的东西调用。在你的情况下,max_execs(5)
必须是一个可调用函数,它将函数转换为另一个可调用的对象,该对象将计算和转发调用。
class helper:
def __init__(self, i, fn):
self.i = i
self.fn = fn
def __call__(self, *args, **kwargs):
if self.i > 0:
self.i = self.i - 1
return self.fn(*args, **kwargs)
class max_execs:
def __init__(self, i):
self.i = i
def __call__(self, fn):
return helper(self.i, fn)
我不明白你为什么会想限制自己的功能(而不是类)。但如果你真的想...
def max_execs(n):
return lambda fn, i=n: return helper(i, fn)
有两种方法可以做到这一点。面向对象的方法是让一个类:
class max_execs:
def __init__(self, max_executions):
self.max_executions = max_executions
self.executions = 0
def __call__(self, func):
@wraps(func)
def maybe(*args, **kwargs):
if self.executions < self.max_executions:
self.executions += 1
return func(*args, **kwargs)
else:
print "fail"
return maybe
为wraps
的说明,请参见this question。
我更喜欢上面这种装饰器的OOP方法,因为你基本上有一个跟踪执行次数的私有计数变量。但是,另一种方法是使用闭包,如
def max_execs(max_executions):
executions = [0]
def actual_decorator(func):
@wraps(func)
def maybe(*args, **kwargs):
if executions[0] < max_executions:
executions[0] += 1
return func(*args, **kwargs)
else:
print "fail"
return maybe
return actual_decorator
这涉及三个功能。 max_execs
函数被赋予一个执行次数的参数,并返回一个装饰器,它将限制你进行那么多的调用。该功能actual_decorator
与OOP示例中的__call__
方法具有相同的功能。唯一不可思议的是,由于我们没有私有变量的类,因此我们需要修改位于闭包外部范围内的executions
变量。 Python 3.0支持nonlocal
声明,但在Python 2.6或更早版本中,我们需要将我们的执行计数包含在列表中,以便它可以进行变异。
如果例如具有@logged属性的方法表示method = logged(method),那么具有@max_execs(5)“方法”的方法是如何“翻译”的? – Geo 2009-07-09 20:31:05
这和应用这个装饰器时说的max_execs(5)(f) – 2009-07-09 20:37:41
不依靠这样的状态,一类,你必须保存功能本身的状态(计数):
def max_execs(count):
def new_meth(meth):
meth.count = count
def new(*a,**k):
meth.count -= 1
print meth.count
if meth.count>=0:
return meth(*a,**k)
return new
return new_meth
@max_execs(5)
def f():
print "invoked"
[f() for _ in range(10)]
它给出:
5
invoked
4
invoked
3
invoked
2
invoked
1
invoked
0
-1
-2
-3
-4
此方法不会修改函数内部,而是将其包装到可调用对象中。
与使用修补功能相比,使用class减慢了执行20%!
def max_execs(n=1):
class limit_wrapper:
def __init__(self, fn, max):
self.calls_left = max
self.fn = fn
def __call__(self,*a,**kw):
if self.calls_left > 0:
self.calls_left -= 1
return self.fn(*a,**kw)
raise Exception("max num of calls is %d" % self.i)
def decorator(fn):
return limit_wrapper(fn,n)
return decorator
@max_execs(2)
def fun():
print "called"
一样,python代码是如何“翻译”的?例如,如果我的方法被称为blabla,并且我应用了max_execs属性,那么Python将如何看待它? blabla = max_execs(5)(blabla)? – Geo 2009-07-09 20:35:05