1

ショート バージョン: インスタンスにバインドされた外部メソッドは、 を介してプライベート変数に直接アクセスできませんself.__privatevarname。これは機能ですか、それともバグですか?

拡張版 (説明と例付き) :

Python : バインドされていないメソッドをバインドしますか? で、Alex Martelli が関数をインスタンスにバインドする簡単な方法を説明しています。

このメソッドを使用すると、外部関数を使用してクラスにインスタンス メソッドを設定できます (図 2 __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の名前マングリングにより、に格納されている属性の実際の名前t_TestClass__obj_val(でわかるようにdir(t))です。クラスで定義されていない外部関数は、を探しているだけです__obj_val

マングルされた名前は関数のコードオブジェクト(例t.add_extern.func_code.co_names)に格納され、読み取り専用であるため、簡単に更新する方法はありません。

したがって、その手法を使用しない理由があります...少なくとも名前が壊れている場合。

于 2012-06-06T01:37:42.000 に答える