Python:从方法中设置方法属性

时间:2015-04-15 19:28:36

标签: 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

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

    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.ab.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

4 个答案:

答案 0 :(得分:4)

执行此操作的最佳方法是根本不执行此操作

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

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术语中调用的任何私有属性:

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

它有点丑陋和hackish。但它确实有效。

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

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

答案 1 :(得分:2)

虽然您可以将self.foo.a = 1替换为self.foo.__dict__['a'] = 1来实现目标,但通常不建议这样做。

答案 2 :(得分:1)

如果您使用的是Python2(而不是Python3) - 每当从实例中检索方法时,都会创建一个新的instance method对象,它是类体中定义的原始函数的包装。

实例方法是函数的一个相当透明的代理 - 您可以通过它检索函数的属性,但不能设置它们 - 这就是为什么在self.foo.__dict__中设置项目的原因。

或者,您可以使用以下方法访问函数对象:self.foo.im_func - 实例方法的im_func属性指向基础函数。

答案 3 :(得分:0)

根据其他贡献者的答案,我提出了以下解决方法。首先,将一个字典中的字典包含在解析包含的字典中的非存在属性中,例如以下代码。

class DictWrapper(object):
    def __init__(self, d):
        self.d = d
    def __getattr__(self, key):
        return self.d[key]

此代码对Lucas Jones的信用。

然后使用addstatic属性实现statics装饰器,该属性将存储静态属性。

class addstatic(object):

    def __init__(self, **statics):
        self.statics = statics

    def __call__(self, function):

        class override(object):

            def __init__(self, function, statics):
                self.__function = function
                self.statics = DictWrapper(statics)

            def __call__(self, *args, **kwargs):
                return self.__function(*args, **kwargs)

            def __get__(self, instance, objtype):
                from types import MethodType
                return MethodType(self, instance)

        retval = override(function, self.statics)
        return retval

以下代码是如何在方法上使用addstatic装饰器的示例。

   class bar(object):

        @attribute(a=2, b=3)
        def foo(self):
            self.foo.statics.a = 1
            self.foo.statics.b = 2

然后,使用bar类的实例产生:

>>> b = bar()
>>> b.foo.statics.a
2
>>> b.foo.statics.b
3
>>> b.foo()
>>> b.foo.statics.a
3
>>> b.foo.statics.b
5

使用这个静态字典的原因遵循jsbueno的答案,这表明我想要的是需要重载包含foo函数的点运算符和实例方法,我不确定是否可能。当然,方法的属性可以在self.foo.__dict__中设置,但由于不推荐(正如brainovergrow所建议的),我想出了这个解决方法。我不确定这是否会被推荐,我想这是为了评论。