【问题标题】:Behavioural difference between decorated function and method in PythonPython中装饰函数和方法之间的行为差​​异
【发布时间】:2016-03-21 01:55:08
【问题描述】:

我对“Pythonic 静态变量”使用以下解决方法:

def static_vars(**kwargs):
    """decorator for funciotns that sets static variables"""
    def decorate(func):
        for k, v in kwargs.items():
            setattr(func, k, v)
        return func
    return decorate

@static_vars(var=1)
def global_foo():
    _ = global_foo
    print _.var
    _.var += 1

global_foo()  # >>> 1
global_foo()  # >>> 2

它按预期工作。但是当我在类中移动这样一个装饰函数时,我得到了一个奇怪的变化:

class A(object):
    @static_vars(var=1)
    def foo(self):
        bound = self.foo
        unbound = A.foo
        print bound.var  # OK, print 1 at first call
        bound.var += 1   # AttributeError: 'instancemethod' object has no attribute 'var'

    def check(self):
        bound = self.foo
        unbound = A.foo
        print 'var' in dir(bound)
        print 'var' in dir(unbound)
        print bound.var is unbound.var  # it doesn't make much sense but anyway

a = A()
a.check()  # >>> True
           # >>> True
           # >>> True

a.foo()    # ERROR

我看不出是什么导致了这种行为。在我看来,它与 python 描述符协议有关,所有绑定与未绑定的方法的东西。不知何故,foo.var 属性可访问但不可写。

感谢任何帮助。

附:我知道静态函数变量本质上是类变量,在第二种情况下这个装饰器是不必要的,但问题更多的是理解 Python 的底层而不是获得任何可行的解决方案。

【问题讨论】:

    标签: python python-2.7 python-decorators


    【解决方案1】:

    a.foo 不返回您定义的实际函数;它返回它的一个绑定方法,该方法封装了函数并将self分配给a

    https://docs.python.org/3/howto/descriptor.html#functions-and-methods

    不过,该指南有点过时了,因为未绑定的方法只返回 Python 3 中的函数。

    因此,要访问函数上的属性,您需要通过A.foo(或a.foo.__func__)而不是a.foo。而且这只适用于 Python 3。在 Python 2 中,我认为A.foo.__func__ 会起作用。

    【讨论】:

    • 但是为什么我可以读取这些属性呢? print a.foo.var 有效,print A.foo.var
    • 我猜想instancemethod 类型实现__getattribute__() 以从封闭的未绑定方法/函数中获取未知属性。无论如何,在绑定方法上设置属性并不是一个好主意,因为每次从实例中获取这样的方法时,都会得到一个 不同new 对象! x, y = a.foo, a.foo; print x is y → 打印 False.
    • 我会说@BlackJack 是对的。除了我猜他们实现了 __getattr__() 而不是__getattribute_(),因为那是最后的努力版本。无论哪种方式,他对于不允许在绑定方法上设置属性几乎绝对是正确的,因为它每次都是一个新实例。同样,我建议使用instancemethod__func__ 属性直接进入函数。
    • @JacobZimmerman 我查看了一个实际的instancemethod 实例,它有一个__getattribute__(),但没有__getattr__()
    猜你喜欢
    • 2021-06-22
    • 2023-03-17
    • 2021-09-17
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多