1

简短版本:绑定到实例的外部方法不能直接通过self.__privatevarname. 这是功能还是错误?

扩展版(带解释和示例)

Python 中:绑定一个未绑定的方法?, Alex Martelli 解释了一种将函数绑定到实例的简单方法。

使用这种方法,可以使用外部函数在类中设置实例方法(in __init__)。

但是,当绑定到实例的函数需要访问私有变量时,这种方法就会失效。这是因为名称修改发生在编译步骤中_Py_Mangle,因此函数永远没有机会调用__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)

但是,这并没有将变量留给外部调用者私有,这是我们不想要的。

4

1 回答 1

2

由于 Python 的name-mangling,存储的属性的实际名称t_TestClass__obj_val(如您在 中看到的dir(t))。没有在类上定义的外部函数只是在寻找__obj_val.

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

所以有理由不使用这种技术……至少在名称混乱的情况下。

于 2012-06-06T01:37:42.000 回答