Skip to main content
 首页 » 编程设计

python : Set method attribute from within method

2025年05月04日14arxive

我正在尝试制作一个 python 装饰器,它将属性添加到类的方法中,以便我可以从方法本身内部访问和修改这些属性。装饰器代码是

from types import MethodType 
class attribute(object): 
 
    def __init__(self, **attributes): 
        self.attributes = attributes 
 
    def __call__(self, function): 
 
        class override(object): 
 
            def __init__(self, function, attributes): 
                self.__function = function 
                for att in attributes: 
                    setattr(self, att, attributes[att]) 
 
            def __call__(self, *args, **kwargs): 
                return self.__function(*args, **kwargs) 
 
            def __get__(self, instance, owner): 
                return MethodType(self, instance, owner)  
 
        retval = override(function, self.attributes) 
        return retval 

我在下面的玩具示例中尝试了这个装饰器。

    class bar(object): 
 
        @attribute(a=2) 
        def foo(self): 
            print self.foo.a 
            self.foo.a = 1 

虽然我可以从 foo() 中访问属性“a”的值,但我无法将它设置为另一个值。 确实,当我调用 bar().foo() 时,我得到了以下 AttributeError。

AttributeError: 'instancemethod' object has no attribute 'a' 

这是为什么?更重要的是我怎样才能实现我的目标?


编辑

更具体地说,我试图找到一种简单的方法来实现位于类方法中的静态变量。继续上面的例子,我想实例化 b = bar(),同时调用 foo() 和 doo() 方法,然后访问b.foo.a 和 b.doo.a 稍后。

    class bar(object): 
 
        @attribute(a=2) 
        def foo(self): 
            self.foo.a = 1 
 
        @attribute(a=4) 
        def doo(self): 
            self.foo.a = 3 

请您参考如下方法:

最好的方法是根本不做。

首先,不需要属性装饰器;你可以自己分配它:

class bar(object): 
    def foo(self): 
        print self.foo.a 
        self.foo.a = 1 
    foo.a = 2 

但是,这样还是会遇到同样的错误。你需要做的:

self.foo.__dict__['a'] = 1 

您可以改为使用元类...但这很快就会变得困惑。

另一方面,还有更清洁的替代品。

您可以使用默认值:

def foo(self, a): 
    print a[0] 
    a[0] = 2 
foo.func_defaults = foo.func_defaults[:-1] + ([2],) 

当然,我的首选方法是完全避免这种情况并使用可调用类(C++ 中的“仿函数”):

class bar(object): 
    def __init__(self): 
        self.foo = self.foo_method(self) 
    class foo_method(object): 
        def __init__(self, bar): 
            self.bar = bar 
            self.a = 2 
        def __call__(self): 
            print self.a 
            self.a = 1 

或者只使用经典的类属性:

class bar(object): 
    def __init__(self): 
        self.a = 1 
    def foo(self): 
        print self.a 
        self.a = 2 

如果你想从派生类中隐藏a,请使用 Python 术语中称为的任何私有(private)属性:

class bar(object): 
    def __init__(self): 
        self.__a = 1 # this will be implicitly mangled as __bar__a or similar 
    def foo(self): 
        print self.__a 
        self.__a = 2 

编辑:您想要静态属性?

class bar(object): 
    a = 1 
    def foo(self): 
        print self.a 
        self.a = 2 

编辑 2: 如果您希望静态属性仅对当前函数可见,您可以使用 PyExt的modify_function:

import pyext 
 
def wrap_mod(*args, **kw): 
    def inner(f): 
        return pyext.modify_function(f, *args, **kw) 
    return inner 
 
class bar(object): 
    @wrap_mod(globals={'a': [1]}) 
    def foo(self): 
        print a[0] 
        a[0] = 2 

它有点丑陋和骇人听闻。但它有效。

我的建议是只使用双下划线:

class bar(object): 
    __a = 1 
    def foo(self): 
        print self.__a 
        self.__a = 2 

尽管此对其他函数可见,但对其他任何函数都不可见(实际上,它就在那里,但被破坏了)。

最终编辑:使用:

import pyext 
 
def wrap_mod(*args, **kw): 
    def inner(f): 
        return pyext.modify_function(f, *args, **kw) 
    return inner 
 
class bar(object): 
    @wrap_mod(globals={'a': [1]}) 
    def foo(self): 
        print a[0] 
        a[0] = 2 
    foo.a = foo.func_globals['a'] 
 
b = bar() 
b.foo() # prints 1 
b.foo() # prints 2 
# external access 
b.foo.a[0] = 77 
b.foo() # prints 77