web-dev-qa-db-ja.com

クラスに追加されたメソッドの「__」(二重アンダースコア)変数にアクセスする方法

バックグラウンド

元のクラスに基づいてヘルパーメソッドを追加するために、メタクラスを使用したいと思います。追加したいメソッドがself.__attributeNameを使用する場合、AttributeErrorを取得します(名前マングリングのため)が、既存の同一のメソッドの場合、これは問題ではありません。

コード例

これは簡単な例です

# Function to be added as a method of Test
def newfunction2(self):
    """Function identical to newfunction"""
    print self.mouse
    print self._dog
    print self.__cat

class MetaTest(type):
    """Metaclass to process the original class and
    add new methods based on the original class
    """
    def __new__(meta, name, base, dct):
        newclass = super(MetaTest, meta).__new__(
                meta, name, base, dct
                )

        # Condition for adding newfunction2
        if "newfunction" in dct:
            print "Found newfunction!"
            print "Add newfunction2!"
            setattr(newclass, "newfunction2", newfunction2)

        return newclass

# Class to be modified by MetaTest
class Test(object):
    __metaclass__ = MetaTest

    def __init__(self):
        self.__cat = "cat"
        self._dog = "dog"
        self.mouse = "mouse"

    def newfunction(self):
        """Function identical to newfunction2"""
        print self.mouse
        print self._dog
        print self.__cat

T = Test()
T.newfunction()
T.newfunction2() # AttributeError: 'Test' object has no attribute '__cat'

質問

newfunction2を使用できるself.__catを追加する方法はありますか?

self.__catの名前をself._catに変更せずに。)

そして、おそらくもっと基本的なことですが、self.__catTestの一部になっているのに、なぜnewfunction2が両方のケースで同じように扱われないのでしょうか。

9
Haydon

名前のマングリングは、クラスのメソッドがコンパイルされるときに発生します。 __fooのような属性名は_ClassName__fooに変換されます。ここで、ClassNameは、メソッドが定義されているクラスの名前です。他のオブジェクトの属性には名前マングリングを使用できることに注意してください。

コードでは、newfunction2の名前マングリングは機能しません。これは、関数がコンパイルされるときに、それがクラスの一部ではないためです。したがって、__catのルックアップは、__Test_catで行ったようにTest.__init__に変換されません。必要に応じて、属性名のマングルバージョンを明示的に検索できますが、newfunction2を汎用にし、複数のクラスに追加できるようにする必要があるようです。残念ながら、それは名前マングリングでは機能しません。

実際、クラスで定義されていないコードが属性にアクセスできないようにすることが、名前マングリングを使用する理由です。通常、プロキシまたはミックスインタイプを作成していて、内部使用属性をプロキシまたはミックスインしているクラスの属性と衝突させたくない場合にのみ、気にする価値があります(これはわかりません)。あらかじめ)。

4
Blckknght

両方の質問に答えるには:

  1. 名前マングリング規則のおかげで、_self.__cat_から_newfunction2_に呼び出す必要がある場合は、_self._Test__cat_を変更する必要があります。

  1. Pythonドキュメント:

このマングリングは、クラスの定義内で発生する限り、識別子の構文上の位置に関係なく実行されます。

名前が壊れた名前に遭遇したとき、通訳がどこを読んでいるかは問題ではないと言っています。 名前がマングルされるのはifクラスの定義で発生する場合のみです。 、 そうではありません。クラス定義の直接「下」ではないため。したがって、_self.__cat_を読み取ると、_self.__cat_に保持され、notテキストで_self._Test__cat_に置き換えられます。 Testクラス内で定義されていないためです。

1
abccd

<Test instance>._Test__catを使用して、Testクラスから__cat属性にアクセスできます。 (ここで、<Test instance>selfまたはTestクラスの他のインスタンスに置き換えられます)

詳細については、 Python doc をご覧ください。

0
Tryph