2017-03-22 24 views
-2

これは "Effective Python"の例です。私は自分自身を説得するためにいくつかのプリントを追加しましたが、私はまだ不明です。pythonのプライベート属性名mangling継承

継承されたプライベート変数にアクセスしようとすると、子のインスタンス辞書で名前が変更されているため、失敗します(最後の行はa.__valueにアクセスしようとすると正しく失敗します。 mangled版_ApiClass__value)。

ここで、私が問題になっているのは、継承されたメソッドgetにこの問題がない理由です。 getへの呼び出しでself.__dict__を印刷すると、子から直接ドット区切りのアクセスを使用しようとしたときと同じChildインスタンスdictを使用していることがわかります。このメソッド内からのドット付き属性アクセスは、何らかの形で適切に変換された名前に変換され、プライベート変数を取得します。

アトリビュートアクセスについての私の理解は、カバーの下で、基本的に起こっていることは(簡略化していますが)a.__valueは基本的にa.__dict__['__value']です。これは理にかなっており、継承されたプライベート変数に直接アクセスしようとすると証明されます。しかし、継承されたメソッドgetは、同じインスタンスでChildから操作されていますが、ドット付きのアクセスで動作するため、明らかにa.__dict__['__value']ではなく、a.__dict__['_ApiClass__value']です。

getメソッド内のプライベート属性へのアクセスで、子からの同様の属性アクセスではなく、変更された名前が認識されるのは何ですか?

class ApiClass(): 
    def __init__(self): 
     self.__value = 5 

    def get(self): 
     print(self.__dict__['_ApiClass__value']) #succeeds 
     print(self.__dict['__value']) #fails bc name mangle 
     return self.__value #how is this translated to '_ApiClass_value' 
          #but a similar instance lookup fails? 

class Child(ApiClass): 
    def __init__(self): 
     super().__init__() 
     self._value = 'hello' 

a = Child() 
print(a.__dict__) 
print(a.get()) #works, but instance dict has no '__value' key? 
print(a.__value) #fail bc name mangled to '_ApiClass_value' 
+0

'ApiClass.get()'の2行目のコメントは、その行がうまくいかないことを示唆していますが、それは問題ありません。 'a .__ value'にアクセスしようとする最後の行を除いて、エラーなしでこのコードを実行できます。 – Craig

+0

manglingルールについては、ドキュメントで明確に説明しています。https://docs.python.org/2/tutorial/classes.html#private-variables-and-class-local-references – Craig

+0

@Craig - 2番目の誤字を修正しましたライン、それについて申し訳ありません。それは、私にドキュメントを紹介することは特に役に立たないと言いました。 – Solaxun

答えて

1

名前の符号化は、関数は、それが貫通と呼ばれていたものではなく、定義された場所に依存するように、名前の符号化は、バイトコードのコンパイル時に行われます。 Childにはgetメソッドがありません。ApiClassを使用しています。ApiClassgetは、ApiClassで動作するようにマングルされています。

これは意図的です。ここでの目標は、クラスXで定義されているメソッドが、どのように到達してもXのためにマングルすることです。そうでない場合、親と子の両方が同じ名前のプライベート変数を定義した場合、その親はその変数の独自のバージョンへのプライベートアクセスを持たず、子と共有します(変数の意味はそれぞれのケースで完全に異なる可能性があります)。対話的に検査

class Parent: 
    def x(self): 
     return self.__x 

class Child(Parent): 
    pass 

その後:

>>> import dis 
>>> dis.dis(Parent.x) 
    3   0 LOAD_FAST    0 (self) 
       3 LOAD_ATTR    0 (_Parent__x) 
       6 RETURN_VALUE 
>>> dis.dis(Child.x): 
    3   0 LOAD_FAST    0 (self) 
       3 LOAD_ATTR    0 (_Parent__x) 
       6 RETURN_VALUE 

注意をLOAD_ATTR値は、_Parent__xはバイトコードにハードコードされていることを

disモジュールは、マングリングがコンパイル時にあるという事実を証明することができます。

あなたはまた、(クラスの一部として定義された方法とは反対に)特別な行動はプレーンな機能に関与されていない方法を示すことができます:

LOAD_ATTRは単なる __x名前をロードしようとしている
>>> def foo(bar): return bar.__x 
>>> dis.dis(foo) 
    1   0 LOAD_FAST    0 (bar) 
       3 LOAD_ATTR    0 (__x) 
       6 RETURN_VALUE 

、マングルドバージョンではありません。 barがクラスのインスタンスであった場合、これはmangling protectionという名前のおかげでうまくいくことはほとんどありません。