web-dev-qa-db-ja.com

なぜPythonの 'private'メソッドは実際にはプライベートではないのですか?

Pythonでは、__myPrivateMethod()のように、名前の前に二重下線を付けることで、クラス内に「プライベートな」メソッドと変数を作成することができます。それでは、どうやってこれを説明できるでしょうか。

>>> class MyClass:
...     def myPublicMethod(self):
...             print 'public method'
...     def __myPrivateMethod(self):
...             print 'this is private!!'
... 
>>> obj = MyClass()
>>> obj.myPublicMethod()
public method
>>> obj.__myPrivateMethod()
Traceback (most recent call last):
  File "", line 1, in 
AttributeError: MyClass instance has no attribute '__myPrivateMethod'
>>> dir(obj)
['_MyClass__myPrivateMethod', '__doc__', '__module__', 'myPublicMethod']
>>> obj._MyClass__myPrivateMethod()
this is private!!

どうしたんだ?!

それがうまくいかなかった人たちのために、これを少し説明します。

>>> class MyClass:
...     def myPublicMethod(self):
...             print 'public method'
...     def __myPrivateMethod(self):
...             print 'this is private!!'
... 
>>> obj = MyClass()

私がしたことは、パブリックメソッドとプライベートメソッドでクラスを作成し、それをインスタンス化することです。

次に、そのパブリックメソッドを呼び出します。

>>> obj.myPublicMethod()
public method

次に、プライベートメソッドを呼び出してみます。

>>> obj.__myPrivateMethod()
Traceback (most recent call last):
  File "", line 1, in 
AttributeError: MyClass instance has no attribute '__myPrivateMethod'

すべてがここで良さそうに見えます。呼び出すことはできません。それは、実際には「プライベート」です。ええ、実際は違います。オブジェクトに対して dir() を実行すると、pythonがすべての 'private'メソッドに対して魔法のように作成する新しい魔法のメソッドが表示されます。

>>> dir(obj)
['_MyClass__myPrivateMethod', '__doc__', '__module__', 'myPublicMethod']

この新しいメソッドの名前は常にアンダースコア、クラス名、メソッド名の順になります。

>>> obj._MyClass__myPrivateMethod()
this is private!!

カプセル化のためにそんなに、ええ?

いずれにせよ、私はPythonがカプセル化をサポートしていないといつも聞いていたのですが、なぜ試してみるのですか?何ができる?

596
willurd

名前のスクランブリングは、サブクラスがスーパークラスのプライベートメソッドと属性を誤ってオーバーライドしないようにするために使用されます。意図的な外部からのアクセスを防ぐようには設計されていません。

例えば:

>>> class Foo(object):
...     def __init__(self):
...         self.__baz = 42
...     def foo(self):
...         print self.__baz
...     
>>> class Bar(Foo):
...     def __init__(self):
...         super(Bar, self).__init__()
...         self.__baz = 21
...     def bar(self):
...         print self.__baz
...
>>> x = Bar()
>>> x.foo()
42
>>> x.bar()
21
>>> print x.__dict__
{'_Bar__baz': 21, '_Foo__baz': 42}

もちろん、2つの異なるクラスが同じ名前を持っていると、それは失敗します。

548
Alya

プライベート機能の例

import re
import inspect

class MyClass :

    def __init__(self) :
        pass

    def private_function ( self ) :
        try :
            function_call = inspect.stack()[1][4][0].strip()

            # See if the function_call has "self." in the begining
            matched = re.match( '^self\.', function_call )
            if not matched :
                print 'This is Private Function, Go Away'
                return
        except :
            print 'This is Private Function, Go Away'
            return

        # This is the real Function, only accessible inside class #
        print 'Hey, Welcome in to function'

    def public_function ( self ) :
        # i can call private function from inside the class
        self.private_function()

### End ###
205
arun

私が最初にJavaからPythonにやってきたときは、 hated thisです。それは私を死に至らせた。

今日のそれはただ一つのことかもしれません 私は最も好きです Pythonについて。

私はプラットフォーム上にいるのが大好きです。そこでは人々がお互いを信頼し合い、自分たちのコードの周りに突き抜けられない壁を構築する必要があるとは思わないのです。高度にカプセル化された言語では、APIにバグがあり、何が問題なのかを突き止めたとしても、必要なメソッドが非公開であるため、まだ回避できない可能性があります。 Pythonでは、態度は「確実」です。あなたが状況を理解していると思うなら、おそらくあなたはそれを読んだことさえあります、そしてそれで我々が言うことができるのは「幸運!」だけです。

覚えておいて、カプセル化は "セキュリティ"、または子供たちを芝生から遠ざけることとさえ弱く関連していません。これは、コードベースを理解しやすくするために使用されるべき別のパターンです。

146
Thomas Ahle

からhttp://www.faqs.org/docs/diveintopython/fileinfo_private.html

厳密に言えば、プライベートメソッドはクラスの外からアクセスできますが、簡単にはアクセスできません。 Pythonには、プライベートなものは何もありません。内部的には、プライベートなメソッドや属性の名前は、与えられた名前ではアクセスできないようにするために、その場でマングルされたりアンマングルされたりします。 MP3FileInfoクラスの__parseメソッドには_MP3FileInfo__parseという名前でアクセスできます。これは興味深いことであることを認識し、実際のコードでは絶対に行わないことを約束します。プライベートメソッドは、ある理由でプライベートですが、Pythonの他の多くのものと同様に、それらのプライベートさは結局のところ慣例の問題であり、強制的なものではありません。

139
xsl

一般的に使用されているフレーズは、「私たちはすべてここで大人に同意している」です。シングルアンダースコア(表示しない)またはダブルアンダースコア(隠す)を前に付けることで、クラスのユーザーに、何らかの方法でメンバーを「非公開」にするつもりであることを伝えます。しかし、あなたが他の人に責任を持って行動し、それを尊重することを信頼しているのです。

もしあなたが本当に私的なものを持っていなければならないのなら、それをエクステンションで実装することができます(例えばCではCPython)。しかしほとんどの場合、Pythonicのやり方を学ぶだけです。

87
Tony Meyer

どの言語でもメンバーの秘密を完全に回避できないわけではありません(C++のポインタ演算、.NET/JavaのReflections)。

重要なのは、誤ってprivateメソッドを呼び出そうとするとエラーが発生するということです。あなたが足で自分自身を撃つしたいのであればしかし、先に行き、それを行います。

編集:あなたはオブジェクト指向のカプセル化によってあなたのものを保護しようとしないのですか?

32
Maximilian

モジュールの属性名が単一のアンダースコア(例:_foo)で始まる場合も同様の動作があります。

from*メソッドを使用するとき、そのように命名されたモジュール属性はインポートしているモジュールにコピーされません、例えば:

from bar import *

ただし、これは規約であり、言語の制約ではありません。これらはプライベートな属性ではありません。それらは任意のインポーターによって参照および操作されることができます。このため、Pythonは真のカプセル化を実装できないと主張する人もいます。

12
Ross

class.__stuff命名規則により、プログラマーは自分が外部から__stuffにアクセスすることを意図していないことを知ることができます。名前のマングリングは誰もが偶然にそれをすることはまずありません。

確かに、あなたはまだこれを回避することができます、それは他の言語(BTWでもあなたにそうさせます)よりもさらに簡単です、しかし、彼がカプセル化を気にかけているならPythonプログラマーはこれをしません。

12
Nickolay

それはそれらの言語設計の選択肢の1つにすぎません。あるレベルではそれらは正当化されます。それらはそれを作るので、あなたはメソッドを試して呼び出すためにあなたの方法からかなり遠く離れて行く必要があります、そしてあなたが本当にそれをひどく必要とするなら、あなたはかなり正当な理由がなければなりません!

デバッグフックとテストは可能なアプリケーションとして頭に浮かぶ。もちろん責任を持って使われる。

12
ctcherry

Python 3.4ではこれが振舞いです:

>>> class Foo:
        def __init__(self):
                pass
        def __privateMethod(self):
                return 3
        def invoke(self):
                return self.__privateMethod()


>>> help(Foo)
Help on class Foo in module __main__:

class Foo(builtins.object)
 |  Methods defined here:
 |
 |  __init__(self)
 |
 |  invoke(self)
 |
 |  ----------------------------------------------------------------------
 |  Data descriptors defined here:
 |
 |  __dict__
 |      dictionary for instance variables (if defined)
 |
 |  __weakref__
 |      list of weak references to the object (if defined)

 >>> f = Foo()
 >>> f.invoke()
 3
 >>> f.__privateMethod()
 Traceback (most recent call last):
   File "<pyshell#47>", line 1, in <module>
     f.__privateMethod()
 AttributeError: 'Foo' object has no attribute '__privateMethod'

https://docs.python.org/3/tutorial/classes.html#tut-private

マングリングルールは主に事故を避けるために設計されていることに注意してください。 それでもプライベートと見なされる変数にアクセスしたり変更したりすることは可能です。 これは、デバッガのような特別な状況でも役に立ちます。

質問が古い場合でも、私のスニペットが役に立つことを願っています。

4
Alberto

プライベートメソッドとプライベート属性に関する最も重要な関心事は、開発者にクラスの外でそれを呼び出さないように言うことです。これはカプセル化です。カプセル化からセキュリティを誤解するかもしれません。そのような構文を故意に使用している場合(下記)、カプセル化は不要です。

obj._MyClass__myPrivateMethod()

私はC#から移行しましたが、最初は私にとっても奇妙でしたが、しばらくして、Pythonコード設計者がOOPについて考える方法だけが異なるという考えに至りました。

2
Afshin Amiri

なぜPythonの 'private'メソッドは実際にはプライベートではないのですか?

私が理解しているように、彼らは できない 非公開になれます。プライバシーはどのように強化されるのでしょうか。

明白な答えは「プライベートメンバはselfを通してしかアクセスできない」ですが、それはうまくいきません - selfはPythonでは特別ではなく、関数の最初のパラメータによく使われる名前に過ぎません。

0
user200783