Python:从方法内部设置方法属性

Fra*_*cis 1 python python-decorators

我正在尝试制作一个将属性添加到类的方法的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
Run Code Online (Sandbox Code Playgroud)

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

    class bar(object):

        @attribute(a=2)
        def foo(self):
            print self.foo.a
            self.foo.a = 1
Run Code Online (Sandbox Code Playgroud)

尽管我可以从foo()中访问属性'a'的值,但无法将其设置为另一个值。 确实,当我调用时bar().foo(),我得到以下AttributeError。

AttributeError: 'instancemethod' object has no attribute 'a'
Run Code Online (Sandbox Code Playgroud)

为什么是这样?更重要的是,我如何实现自己的目标?


编辑

更具体地说,我试图找到一种简单的方法来实现位于类方法内的静态变量。从上面的示例继续,我想实例化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
Run Code Online (Sandbox Code Playgroud)

kir*_*sos 5

做到这一点的最好方法是根本不做

首先,不需要属性装饰器。您可以自己分配:

class bar(object):
    def foo(self):
        print self.foo.a
        self.foo.a = 1
    foo.a = 2
Run Code Online (Sandbox Code Playgroud)

但是,这仍然遇到相同的错误。您需要做:

self.foo.__dict__['a'] = 1
Run Code Online (Sandbox Code Playgroud)

您可以改用元类...但是很快就会变得混乱。

另一方面,还有更清洁的选择。

您可以使用默认值:

def foo(self, a):
    print a[0]
    a[0] = 2
foo.func_defaults = foo.func_defaults[:-1] + ([2],)
Run Code Online (Sandbox Code Playgroud)

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

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
Run Code Online (Sandbox Code Playgroud)

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

class bar(object):
    def __init__(self):
        self.a = 1
    def foo(self):
        print self.a
        self.a = 2
Run Code Online (Sandbox Code Playgroud)

如果您想a从派生类中隐藏,请使用Python术语中调用的任何私有属性:

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
Run Code Online (Sandbox Code Playgroud)

编辑:您想要静态属性?

class bar(object):
    a = 1
    def foo(self):
        print self.a
        self.a = 2
Run Code Online (Sandbox Code Playgroud)

编辑2:如果您希望静态属性仅对当前函数可见,则可以使用PyExtModify_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
Run Code Online (Sandbox Code Playgroud)

这有点丑陋和朴实。但这有效。

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

class bar(object):
    __a = 1
    def foo(self):
        print self.__a
        self.__a = 2
Run Code Online (Sandbox Code Playgroud)

尽管这对于其他功能可见的,但是对于其他任何功能都是不可见的(实际上,它在那里,但是被扭曲了)。

最终编辑:使用此:

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
Run Code Online (Sandbox Code Playgroud)