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)
做到这一点的最好方法是根本不做。
首先,不需要属性装饰器。您可以自己分配:
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:如果您希望静态属性仅对当前函数可见,则可以使用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
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)