プライベート変数は外部からアクセスできないと認識しております。
確かに、ob.__num = 321 で値を代入し、ob.print()で確認すると、値は変更できていない
ことを確認しましたが、print(ob.__num)では値が変更されている、つまりアクセスできいるのですが
どういうことでしょうか。
class MyObj: def __init__(self): self.__num = 123 def print(self): print(self.__num) ob = MyObj() ob.__num = 321 ob.print() print(ob.__num)
気になる質問をクリップする
クリップした質問は、後からいつでもMYページで確認できます。
またクリップした質問に回答があった際、通知やメールを受け取ることができます。
バッドをするには、ログインかつ
こちらの条件を満たす必要があります。
回答5件
0
プライベート変数
~~
そういう言葉はありません。少なくともpythonの世界では。
~~
~~ドキュメントにはプライベートな名前と書いてあるみたいですね。
~~
チュートリアルではプライベート変数という言葉も使っているようです。
とりあえず厳密な意味での(絶対に外部から参照できない)「プライベート変数」はpythonにはない、そういう名前で呼ぶことも(原則的には)ない、としておきます。
アンダーバー2つの属性はマングリングによって処理されます。
プライベートな名前のマングリング: クラス定義内に書かれた識別子で、2つ以上のアンダースコアから始まり、末尾が2つ以上のアンダースコアで終わっていないものは、そのクラスの プライベートな名前 とみなされます。プライベートな名前は、コードが生成される前により長い形式に変換されます。この変換によって、クラス名の先頭にアンダースコアがあれば除去し、先頭にアンダースコアを1つ付加し、名前の前に挿入されます。例えば、クラス名 Ham の中の識別子 __spam は、_Ham__spam に変換されます。変換は識別子が使用されている構文のコンテキストからは独立しています。変換された名前が非常に長い (255文字を超える) 場合、実装によっては名前の切り詰めが行われるかもしれません。クラス名がアンダースコアのみから成る場合は変換は行われません。
__num
は_MyObj__num
に変換されるので、外部から参照できない「ように見える」だけです。書き換える場合はこちらに対して代入する必要があります。
python
1ob = MyObj() 2ob._MyObj__num = 321 3ob.print() # 321
ではob.__num
に対して代入すると? というと、前提としてpythonのオブジェクトは代入によって新しい属性を作ることができます。
python
1>>> class A: 2... pass 3... 4>>> a = A() 5>>> a.hoge 6Traceback (most recent call last): 7 File "<stdin>", line 1, in <module> 8AttributeError: 'A' object has no attribute 'hoge' 9>>> a.hoge = 42 10>>> a.hoge 1142 12
ob.__num = 321
という文はクラス定義の外にあるので、マングリングは働かずそのまま__num
という属性が新たに作られます。
投稿2019/05/14 05:21
編集2019/05/14 05:52総合スコア30933
バッドをするには、ログインかつ
こちらの条件を満たす必要があります。
退会済みユーザー
2019/05/14 05:47 編集
0
一応補足として。
- マングリングが実施されるのは
class
文を実行した時です。 - マングリングの対象になるのは変数ではありません。クラス定義内にある識別子です。
python
1class Hoge: 2 def __init__(__self): 3 __hoge = 1 4 print(locals())
と書いてHoge()
を実行すると
{'_Hoge__hoge': 1, '_Hoge__self': <__main__.Hoge object at 0x10c009390>}
となるのが確認できます。
クラス変数なのかローカル変数なのか引数なのかメソッド名なのかというような、「その識別子がなんであるか?」という区別なしに、マングリング対象になる識別子はすべて書き換えられているわけです。
上のソースは
python
1class Hoge: 2 def __init__(_Hoge__self): 3 _Hoge__hoge = 1 4 print(locals())
とかわりありません。
難号化の規則は主に不慮の事故を防ぐためのものだということに注意してください
とチュートリアルにある通り、この機能は名前をプライベートにするためのものではありません。
投稿2019/05/14 05:44
総合スコア11029
バッドをするには、ログインかつ
こちらの条件を満たす必要があります。
0
Pythonの名前の衝突を避ける仕様が働いています。
Python
1print(ob.__num) # -> 321と出力 2print(ob._MyObj__num) # -> 123 と出力
クラスのプライベートメンバについて適切なユースケース(特にサブクラスで定義された名前との衝突を避ける場合)があるので、マングリング(name mangling) と呼ばれる、限定されたサポート機構があります。 __spam (先頭に二個以上の下線文字、末尾に一個以下の下線文字) という形式の識別子は、 _classname__spam へとテキスト置換されるようになりました。ここで classname は、現在のクラス名から先頭の下線文字をはぎとった名前になります。このような難号化 (mangle) は、識別子の文法的な位置にかかわらず行われるので、クラス定義内に現れた識別子全てに対して実行されます。
投稿2019/05/14 05:20
編集2019/05/14 05:21総合スコア306
バッドをするには、ログインかつ
こちらの条件を満たす必要があります。
2019/05/14 05:26
2019/05/14 05:39
退会済みユーザー
2019/05/14 05:48
0
Pythonのプライベート変数は変数名への変更になっているためです。
python
1class A: 2 def __init__(self): 3 self.__private = 1 4 def print(self): 5 print(self.__private) 6a = A() 7a.__private = 2 8a.print() 9print(a.__private) 10 11print(vars(a))
1 2 {'_A__private': 1, '__private': 2}
投稿2019/05/14 05:21
総合スコア8560
バッドをするには、ログインかつ
こちらの条件を満たす必要があります。
0
アンダースコアが二つ付いた変数識別子はマングリングされます。
ただし外部から新たに属性を加える場合はそのままです。
Python
1>>> class MyClass: 2... def __init__(self): 3... self.__attr = 1 4... 5>>> inst = MyClass() 6>>> inst.__attr = 2 7>>> 8>>> dir(inst) 9['_MyClass__attr', '__attr', '__class__', '__delattr__', '__dict__', '__dir__', '__doc__', '__eq__', '__format__', '__ge__', '__getattribute__', '__gt__', '__hash__', '__init__', '__init_subclass__', '__le__', '__lt__', '__module__', '__ne__', '__new__', '__reduce__', '__reduce_ex__', '__repr__', '__setattr__', '__sizeof__', '__str__', '__subclasshook__', '__weakref__'] 10>>> 11>>> inst._MyClass__attr 121 13>>> inst.__attr 142
別のオブジェクトとして扱われているのが分かります。
コメントを受けて
この両者(
self.__attr
とinst.__attr
)は違うのでしょうか
hayataka2049さんが引用されている文章の中に、次のような記述があります。
クラス定義内に書かれた識別子で ...後略
単にどこに書かれているかが問題なわけです。
次のコードでもそれを確認することができます。
Python
1class MyClassA: 2 def __init__(self, inst): 3 inst.__attr = 42 4 5 6class MyClassB: 7 def __init__(self): 8 pass 9 10 11b = MyClassB() 12a = MyClassA(b) 13 14print(vars(b))
実行結果 Wandbox
{'_MyClassA__attr': 42}
MyClassBの属性なのに、MyClassAの名前を冠されているのはなかなか面白い挙動ですね。
投稿2019/05/14 05:24
編集2019/05/14 06:52総合スコア35658
バッドをするには、ログインかつ
こちらの条件を満たす必要があります。
2019/05/14 05:52
2019/05/14 08:20
あなたの回答
tips
太字
斜体
打ち消し線
見出し
引用テキストの挿入
コードの挿入
リンクの挿入
リストの挿入
番号リストの挿入
表の挿入
水平線の挿入
プレビュー
質問の解決につながる回答をしましょう。 サンプルコードなど、より具体的な説明があると質問者の理解の助けになります。 また、読む側のことを考えた、分かりやすい文章を心がけましょう。