无法理解这个python装饰器

rit*_*att 5 python decorator

我是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)

我的疑惑:

  1. 当我们将装饰器添加到函数前面时,这是否意味着我们正在创建装饰器类的对象?在我们的例子中,当它说:

    @countcalls def f(): print 'f called'

@countcalls等效于创建一个countcalls对象,并通过以下功能,它的__init__方法是什么?

  1. __call__是三个论点.self只要回答上面的问题就可以了.另外两个论点*args, **kwargs到底是什么:他们取得了什么成果?

  2. 怎样才能让装饰师变得更好?

Mik*_*ham 5

这段代码似乎有些奇怪.我们来谈谈稍微简单的代码

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),你应该能够找到大多数装饰器,给予足够的时间.像所有事情一样,练习将帮助您更快更轻松地完成这项工作.