【问题标题】:Understanding how class decorators work in Python了解类装饰器如何在 Python 中工作
【发布时间】:2017-10-21 03:48:51
【问题描述】:

我在理解 Python 中的类装饰器如何工作时遇到了问题。在这种情况下,我想编写一个装饰器来计算递归函数(搜索最大公约数)被调用的次数。我有一个装饰器:

class TrackCalls(object):

def __init__(self, func):
    self.func = func
    self.calls = 0

def __call__(self,*args,**kwargs):
    self.calls += 1
    return self.func(*args, **kwargs)

def called(self):
    return self.calls

还有一个功能:

@TrackCalls
def NWD(a, b):

    if a > b:
        return NWD(a-b, b)
    elif b > a:
        return NWD(a, b-a)
    else:
        return a

然后我打电话给他们:

print(NWD(60,25)) #5
print(NWD.called()) #6

NWD 功能到底发生了什么?据我所知,装饰器采用一个函数并创建另一个函数,所以在这种情况下,TrackCalls 采用该函数,创建一个 TrackCalls 类的对象,然后通过调用NWD.called() 我基本上调用了一个 TrackCalls 对象的方法?当我跑步时,例如。 NWD(5,25) 在之前的调用之后我得到 11,所以看起来每次我调用 NWD 时我都会使用某种静态变量调用 TrackCalls 对象。如果我用相同的装饰器装饰另一个递归函数,它们会共享calls 变量吗?

【问题讨论】:

  • 试试看吧!
  • 我真的有点惊讶这个作品。我原以为它不会计算递归调用。

标签: python function oop decorator


【解决方案1】:

我认为您的困惑可以追溯到 python 的鸭子类型概念。这是指从外部角度来看,function [让我们称之为func] 和一个对象 (=instance) [让我们称之为obj] 与__call__ 的类之间没有区别方法。两者都是callable,即它们可以在括号内接受参数并分别返回func(args)obj(args) 中的内容。当然区别在于 obj 对象具有状态,即它具有实例变量(也通常称为字段)。

在您的示例中,callsfuncTrackCalls 类的字段。 通过用@TrackCalls 装饰你的NWD 函数,你可以用TrackCallsinstance 包装NWD。也就是名称NWD 基本上被替换为TrackCalls 的实例,相当于调用NWD = TrackCalls(NWD)。新实例是可调用的,因此其行为类似于原始函数,但由于它是一个对象,它也具有状态并且可以计算其__call__ 方法的调用。计算本身被委托给NWD 的原始实现,现在存储在func 字段中。

要回答您的问题,您使用TrackCalls 装饰的每个函数都会生成一个新的TrackCalls 实例每个都有自己的 状态。但是对一个修饰函数的多次调用共享该函数的状态。

【讨论】:

  • 我在这件事情上苦苦挣扎了一段时间,但你说清楚了,非常感谢:)
猜你喜欢
  • 2020-10-15
  • 2014-04-20
  • 1970-01-01
  • 2011-09-17
  • 2018-10-03
  • 2017-09-20
  • 2016-01-06
  • 2021-07-16
  • 2011-11-20
相关资源
最近更新 更多