Python : 从方法内部设置方法属性
Python : Set method attribute from within method
我正在尝试制作一个 python 装饰器,将属性添加到 class 的方法中,以便我可以从方法本身内部访问和修改这些属性。装饰器代码是
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'
这是为什么?更重要的是我怎样才能实现我的目标?
编辑
更具体地说,我试图找到一种简单的方法来实现位于 class 方法中的静态变量。继续上面的示例,我想实例化 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.__dict__['a'] = 1
来实现您的目标,但通常不推荐这样做。
如果您正在使用 Python2 -(而不是 Python3)- 每当您从实例中检索方法时,都会创建一个新的 instance method
对象,它是class 主体中定义的原始函数。
实例方法是函数的一个相当透明的代理 - 您可以通过它检索函数的属性,但不能设置它们 - 这就是为什么在 self.foo.__dict__
中设置项目有效。
或者,您可以使用以下方法访问函数对象本身: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
您可以改用元数据class...但这很快就会变得混乱。
另一方面,还有更清洁的替代品。
您可以使用默认值:
def foo(self, a):
print a[0]
a[0] = 2
foo.func_defaults = foo.func_defaults[:-1] + ([2],)
当然,我的首选方法是完全避免这种情况并使用可调用的 class(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
或者只使用classic class属性:
class bar(object):
def __init__(self):
self.a = 1
def foo(self):
print self.a
self.a = 2
如果您想从派生的 classes 中隐藏 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
它有点丑陋和粗糙。但它有效。
我的建议是只使用双下划线:
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 中,将不存在的属性解析为包装的字典,例如以下代码。
class DictWrapper(object):
def __init__(self, d):
self.d = d
def __getattr__(self, key):
return self.d[key]
此代码归功于 Lucas Jones。
然后使用 statics
属性实现 addstatic
装饰器,该装饰器将存储 static 属性。
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
class 的实例会产生:
>>> 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 所建议的),我想出了这个解决方法。我不确定这是否会被推荐,我想它正在征求意见。
我正在尝试制作一个 python 装饰器,将属性添加到 class 的方法中,以便我可以从方法本身内部访问和修改这些属性。装饰器代码是
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'
这是为什么?更重要的是我怎样才能实现我的目标?
编辑
更具体地说,我试图找到一种简单的方法来实现位于 class 方法中的静态变量。继续上面的示例,我想实例化 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.__dict__['a'] = 1
来实现您的目标,但通常不推荐这样做。
如果您正在使用 Python2 -(而不是 Python3)- 每当您从实例中检索方法时,都会创建一个新的 instance method
对象,它是class 主体中定义的原始函数。
实例方法是函数的一个相当透明的代理 - 您可以通过它检索函数的属性,但不能设置它们 - 这就是为什么在 self.foo.__dict__
中设置项目有效。
或者,您可以使用以下方法访问函数对象本身: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
您可以改用元数据class...但这很快就会变得混乱。
另一方面,还有更清洁的替代品。
您可以使用默认值:
def foo(self, a):
print a[0]
a[0] = 2
foo.func_defaults = foo.func_defaults[:-1] + ([2],)
当然,我的首选方法是完全避免这种情况并使用可调用的 class(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
或者只使用classic class属性:
class bar(object):
def __init__(self):
self.a = 1
def foo(self):
print self.a
self.a = 2
如果您想从派生的 classes 中隐藏 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
它有点丑陋和粗糙。但它有效。
我的建议是只使用双下划线:
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 中,将不存在的属性解析为包装的字典,例如以下代码。
class DictWrapper(object):
def __init__(self, d):
self.d = d
def __getattr__(self, key):
return self.d[key]
此代码归功于 Lucas Jones。
然后使用 statics
属性实现 addstatic
装饰器,该装饰器将存储 static 属性。
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
class 的实例会产生:
>>> 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 所建议的),我想出了这个解决方法。我不确定这是否会被推荐,我想它正在征求意见。