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

Python:从方法中设置方法属性,python,python-decorators,Python,Python Decorators,我正在尝试制作一个python装饰器,它向类的方法添加属性,这样我就可以从方法本身中访问和修改这些属性。装饰代码是 from types import MethodType class attribute(object): def __init__(self, **attributes): self.attributes = attributes def __call__(self, function): class override(obj

我正在尝试制作一个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: '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

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

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

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

instance方法是函数的一个相当透明的代理-您可以通过它检索函数的属性,但不能设置它们-这就是为什么在
self.foo.\uu dict\uu
中设置一个项是有效的


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

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

instance方法是函数的一个相当透明的代理-您可以通过它检索函数的属性,但不能设置它们-这就是为什么在
self.foo.\uu dict\uu
中设置一个项是有效的


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

最好的方法是根本不这样做

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

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.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:如果希望静态属性仅对当前函数可见,可以使用的“修改”函数:

它有点难看和粗俗。但它是有效的

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

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

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

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

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.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:如果希望静态属性仅对当前函数可见,可以使用的“修改”函数:

它有点难看和粗俗。但它是有效的

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

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

根据其他贡献者的回答,我提出了以下解决方法。首先,在类中包装一个词汇表,将不存在的属性解析为包装的词汇表,如下面的代码

class DictWrapper(object):
    def __init__(self, d):
        self.d = d
    def __getattr__(self, key):
        return self.d[key]
此代码的学分为

然后使用将存储静态属性的
statics
属性实现一个
addstatic
装饰器

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
decorator的示例

   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

使用这个statics术语的原因遵循jsbueno的回答,这表明我需要重载foo函数的点运算符和实例方法,我不确定这是否可行。当然,该方法的属性可以在self.foo.\uuu dict\uuuu中设置,但由于不推荐(正如brainovergrow所建议的那样),我提出了这个解决方法。我也不确定这是否会被推荐,我想应该由评论来决定。

根据其他撰稿人的回答,我