当前位置: 首页 > 面试题库 >

Python装饰器,自我混淆

万俟震博
2023-03-14
问题内容

我是Python装饰器的新手(哇,很棒的功能!),并且我很难使以下内容起作用,因为self参数混杂在一起。

#this is the decorator
class cacher(object):

    def __init__(self, f):
        self.f = f
        self.cache = {}

    def __call__(self, *args):
        fname = self.f.__name__
        if (fname not in self.cache):
            self.cache[fname] = self.f(self,*args)
        else:
            print "using cache"
        return self.cache[fname]

class Session(p.Session):

    def __init__(self, user, passw):
        self.pl = p.Session(user, passw)

    @cacher
    def get_something(self):
        print "get_something called with self = %s "% self
        return self.pl.get_something()

s = Session(u,p)
s.get_something()

运行此命令时,我得到:

get_something called with self = <__main__.cacher object at 0x020870F0> 
Traceback:
...
AttributeError: 'cacher' object has no attribute 'pl'

我做的那条线 self.cache[fname] = self.f(self,*args)

问题 -显然,问题在于self缓存器对象而不是Session实例,而该实例实际上没有pl属性。但是我找不到解决方法。

我已经考虑过但不能使用的解决方案
-我想到使decorator类返回一个函数而不是一个值(如本文的2.1节),以便self在正确的上下文中进行评估,但这是不可能的因为我的装饰器是作为一个类实现的,并且使用了内置
__call__方法。然后,我认为 不要
为装饰器使用类,这样就不需要__call__方法,但是我不能这样做,因为我需要在装饰器调用之间保持状态(即,跟踪self.cache属性中的内容) 。

问题 -因此,除了使用全局cache字典变量(我没有尝试过,但假定可以使用)之外,还有其他方法可以使装饰器起作用吗?


问题答案:

像这样使用描述符协议

import functools

class cacher(object):

    def __init__(self, f):
        self.f = f
        self.cache = {}

    def __call__(self, *args):
        fname = self.f.__name__
        if (fname not in self.cache):
            self.cache[fname] = self.f(self,*args)
        else:
            print "using cache"
        return self.cache[fname]

    def __get__(self, instance, instancetype):
        """Implement the descriptor protocol to make decorating instance 
        method possible.

        """

        # Return a partial function with the first argument is the instance 
        #   of the class decorated.
        return functools.partial(self.__call__, instance)

编辑:

如何运作?

在装饰器中使用描述符协议将使我们能够以正确的实例作为自身来访问装饰的方法,也许一些代码可以提供更好的帮助:

现在,当我们要做的时候:

class Session(p.Session):
    ...

    @cacher
    def get_something(self):
        print "get_something called with self = %s "% self
        return self.pl.get_something()

相当于:

class Session(p.Session):
    ...

    def get_something(self):
        print "get_something called with self = %s "% self
        return self.pl.get_something()

    get_something = cacher(get_something)

因此,现在get_something是cacher的实例。因此,当我们调用方法get_something时,它将被转换为此(由于描述符协议):

session = Session()
session.get_something  
#  <==> 
session.get_something.__get__(get_something, session, <type ..>)
# N.B: get_something is an instance of cacher class.

并且因为:

session.get_something.__get__(get_something, session, <type ..>)
# return
get_something.__call__(session, ...) # the partial function.

所以

session.get_something(*args)
# <==>
get_something.__call__(session, *args)

希望这会解释它是如何工作的:)



 类似资料:
  • 问题内容: 在unittest的setUp()方法中,我设置了一些 自 变量,稍后将在实际测试中引用它们。我还创建了一个装饰器来进行一些日志记录。有没有一种方法可以从装饰器访问这些 自 变量? 为了简单起见,我将发布此代码: 什么是访问的最好办法 一个 从装饰(()在设置中设定)? 问题答案: 由于您正在装饰一个方法,并且是一个方法参数,因此装饰器可以在运行时访问。显然不是在解析时,因为还没有对象

  • 问题内容: 我正在尝试学习装饰器。我了解它的概念,现在尝试实现它。 这是我编写 的代码,代码不言自明。它只是检查参数是否传入。 抛出错误的说法。我了解它未在下定义,但不知道如何纠正此代码?我要去哪里错了? 问题答案: 您的装饰器应如下所示: 需要注意的几点: 期望将类作为第一个参数(您可以将其替换为简单的try / except TypeError除外)。 包装器应返回一个函数,而不是被调用函数的

  • 看过 angular2 的人基本都知道,它大量使用了装饰器,而装饰器还属于 ecmascript 的征集意愿的第一阶段。 使用装饰器,需要在 tsconfig.json 里面开启支持选项 experimentalDecorators // tsconfig.json "experimentalDecorators": true 装饰器基本可以对所有变量起作用,它的语法是 @装饰器 ,这个装饰器必须

  • 主要内容:带参数的函数装饰器,函数装饰器可以嵌套前面章节中,我们已经讲解了 Python 内置的 3 种函数装饰器,分别是 @staticmethod、@classmethod 和 @property,其中 staticmethod()、classmethod() 和 property() 都是 Python 的内置函数。 那么,函数装饰器的工作原理是怎样的呢?假设用 funA() 函数装饰器去装饰 funB() 函数,如下所示: 实际上,上面

  • 问题内容: 我受够了在函数中不断重复输入相同的重复命令。我想知道我是否可以写一个装饰器为我做这项工作。这是我的问题的一个示例: 有什么方法可以自动将所有传递给函数的参数变成具有相同名称的实例变量?例如: 哪里会自动设置和。我该怎么办? 谢谢! 编辑:我应该提到我使用CPython 2.7。 问题答案: 这是我第一次尝试装饰器: 编辑第二尝试:我添加了处理变量的默认值和检查有效的关键字。 [编辑3:

  • 本文向大家介绍Python装饰器语法糖,包括了Python装饰器语法糖的使用技巧和注意事项,需要的朋友参考一下 Python装饰器语法糖代码示例 总结 以上就是这篇文章的全部内容了,希望本文的内容对大家的学习或者工作具有一定的参考学习价值,谢谢大家对呐喊教程的支持。如果你想了解更多相关内容请查看下面相关链接