绑定需要访问私有变量的外部方法:功能还是错误?

时间:2012-06-06 01:21:51

标签: python cpython

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)

但是,这并不会使变量对外部调用者保密,这是我们不想要的。

1 个答案:

答案 0 :(得分:2)

由于Python name-manglingt上存储的属性的实际名称为_TestClass__obj_val(正如您在dir(t)中所见)。未在类上定义的外部函数只是在寻找__obj_val

受损的名称存储在函数的代码对象中(例如t.add_extern.func_code.co_names)并且是只读的,因此没有简单的方法来更新它。

所以有理由不使用这种技术......至少是有错误的名字。