我是python decorators的新手.我借助简单的例子理解了基本概念.但是当我试图阅读这个更实用的装饰时,我感到迷茫.以下是我的问题所遵循的代码:
class countcalls(object):
"Decorator that keeps track of the number of times a function is called."
__instances = {}
def __init__(self, f):
self.__f = f
self.__numcalls = 0
countcalls.__instances[f] = self
def __call__(self, *args, **kwargs):
self.__numcalls += 1
return self.__f(*args, **kwargs)
def count(self):
"Return the number of times the function f was called."
return countcalls.__instances[self.__f].__numcalls
@countcalls
def f():
print 'f called'
f()
f()
f()
print f.count() # prints 3
Run Code Online (Sandbox Code Playgroud)
我的疑惑:
当我们将装饰器添加到函数前面时,这是否意味着我们正在创建装饰器类的对象?在我们的例子中,当它说:
@countcalls
def f():
print 'f called'
是@countcalls等效于创建一个countcalls对象,并通过以下功能,它的__init__方法是什么?
这__call__是三个论点.self只要回答上面的问题就可以了.另外两个论点*args, **kwargs到底是什么:他们取得了什么成果?
怎样才能让装饰师变得更好?
这段代码似乎有些奇怪.我们来谈谈稍微简单的代码
class countcalls(object):
def __init__(self, f):
self._f = f
self._numcalls = 0
def __call__(self, *args, **kwargs):
self._numcalls += 1
return self._f(*args, **kwargs)
def count(self):
return self._numcalls
@countcalls
def f():
print 'f called'
f()
f()
f()
print f.count()
# output:
# f called
# f called
# f called
# 3
Run Code Online (Sandbox Code Playgroud)
记得
@countcalls
def f():
print 'f called'
Run Code Online (Sandbox Code Playgroud)
是一样的
def f():
print 'f called'
f = countcalls(f)
Run Code Online (Sandbox Code Playgroud)
因此,当我们使用装饰器时,使用该_f属性存储该函数.
所以f是一个countcalls实例.当你这样做时,f(...)你会调用f.__call__(...)- 这就是你()为自己的实例实现语法的方式.所以当你打电话时f,会发生什么?
def __call__(self, *args, **kwargs):
self._numcalls += 1
return self._f(*args, **kwargs)
Run Code Online (Sandbox Code Playgroud)
首先,你使用*args和**kwargs,在定义中将所有位置和关键字参数压缩成元组和字典,然后在调用中将序列和字典扩展为参数(有关更多信息,请参阅官方教程中的4.7.4).这是一个局部的例子
>>> def f(*args): print args
...
>>> f(1, 2)
(1, 2)
>>> f()
()
>>> def add(a, b): return a + b
...
>>> add(*[4, 3])
7
>>> add(**{'b': 5, 'a': 9})
14
Run Code Online (Sandbox Code Playgroud)
所以def f(*args, **kwargs): return g(*args, **kwargs)只是在所有论点上做一个直通.
除此之外,你只是跟踪你__call__为这个实例多少次(你打过多少次f).
只要记住它@dec def f(...): ...是相同的def f(...): ... f = dec(f),你应该能够找到大多数装饰器,给予足够的时间.像所有事情一样,练习将帮助您更快更轻松地完成这项工作.