SHORT VERSION :绑定到实例的外部方法无法直接通过self.__privatevarname
访问私有变量。这是一个功能还是一个错误?
扩展版本(附有说明和示例):
在Python: Bind an Unbound Method?中,Alex Martelli解释了一种将函数绑定到实例的简单方法。
使用此方法,可以使用外部函数在类中设置实例方法(在__init__
中)。
但是,当绑定到实例的函数需要访问私有变量时,这会中断。这是因为名称重整发生在_Py_Mangle
的{{3}}步骤中,因此函数永远无法调用__getattribute__('_classname__privatevarname')
。
例如,如果我们定义一个访问私有实例变量__obj_val
的简单外部附加函数:
def add_extern(self, value):
return self.__obj_val + value
并将其绑定到__init__
中的每个实例,同时在类定义中定义类似的实例方法add_intern
class TestClass(object):
def __init__(self, object_value):
self.__obj_val = object_value
self.add_extern = add_extern.__get__(self, TestClass)
def add_intern(self, value):
return self.__obj_val + value
然后内部方法将起作用,但外部绑定方法将引发异常:
>>> t = TestClass(0)
>>> t.add_intern(1)
1
>>> t.add_extern(1)
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
File "<stdin>", line 2, in add_extern
AttributeError: 'TestClass' object has no attribute '__obj_val'
POSTSCRIPT :您可以通过覆盖__getattribute__
为您进行修改来克服这个缺点:
class TestClass(object):
...
def __getattribute__(self, name):
try:
return super(TestClass, self).__getattribute__(name)
except AttributeError:
# mimic behavior of _Py_Mangle
if not name.startswith('__'): # only private attrs
raise
if name.endswith('__'): # don't mangle dunder attrs
raise
if '.' in name: # don't mangle for modules
raise
if not name.lstrip('_'): # don't mangle if just underscores
raise
mangled_name = '_{cls_name}{attr_name}'.format(
cls_name=self.__class__.__name__, attr_name=name)
return super(TestClass, self).__getattribute__(mangled_name)
但是,这并不会使变量对外部调用者保密,这是我们不想要的。
答案 0 :(得分:2)
由于Python name-mangling,t
上存储的属性的实际名称为_TestClass__obj_val
(正如您在dir(t)
中所见)。未在类上定义的外部函数只是在寻找__obj_val
。
受损的名称存储在函数的代码对象中(例如t.add_extern.func_code.co_names
)并且是只读的,因此没有简单的方法来更新它。
所以有理由不使用这种技术......至少是有错误的名字。