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


658

Pythonでは、次のように、名前の前に2つのアンダースコアを付けることで、クラス内に「プライベート」メソッドと変数を作成できます__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がすべての「プライベート」メソッドに対して魔法のように作成する新しい魔法のメソッドが明らかになります。

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

この新しいメソッドの名前は常にアンダースコアであり、その後にクラス名、メソッド名が続きます。

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

カプセル化についてはこれで十分でしょう。

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


18
リフレクションを使用する場合、JavaまたはC#でも同じことが言えます(これは何とかしてそこで行います)。
0x434D53

4
ユニットテストの目的でビルドされたので、その「ハック」を使用して、クラスのプライベートメソッドを外部からユニットテストできます。
waas1919

16
プライベートメソッドのテストはアンチパターンではありませんか?プライベートメソッドはいくつかのパブリックメソッドで使用されますが、それ以外の場合は永久に使用されません。プライベートメソッドをテストする正しい方法(ThoughtWorksからのこれまでの学習に基づく)は、すべてのケースをカバーするパブリックメソッドのみのテストを作成することです。それが正常に機能する場合、外部からプライベートメソッドをテストする必要はまったくありません。
Vishnu Narang 2017年

3
@VishnuNarang:ええ、それはよく教えられていることです。しかし、いつものように、「これを常に行う、決して行わない」というほぼ「宗教的な」アプローチは、「決して」が良いことではありません。単体テストが回帰テストまたはパブリックAPIのテストに「のみ」使用されている場合、プライベートをテストする必要はありません。ただし、ユニットテスト駆動型の開発を行う場合、開発中にプライベートメソッドをテストするのに十分な理由があります(たとえば、パブリックインターフェイスを介して特定の異常な/極端なパラメーターをモックするのが難しい場合など)。一部の言語/単体テスト環境ではこれを実行できませんが、IMHOは良くありません。
Marco Freudenberger、2017

5
@MarcoFreudenberger私はあなたの意見を理解しています。私はユニットテスト駆動開発の経験があります。多くの場合、パラメーターのモックが困難になると、ほとんどの場合、設計を変更および改善することで解決されます。設計が完全であり、それでもユニットテストがプライベートメソッドのテストを回避するのが非常に困難であるシナリオにまだ出会っていません。私はそのような場合に注意します。ありがとう。理解を深めるために、頭の上のシナリオを1つ共有していただければ幸いです。
Vishnu Narang 2017

回答:


592

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

例えば:

>>> 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つの異なるクラスに同じ名前が付いている場合は機能しません。


12
docs.python.org/2/tutorial/classes.html。セクション:プライベート変数とクラスローカル参照についての9.6。
gjain 2013年

72
スクロール/検索が
面倒なので、

3
変数をプライベートと見なす必要があることを指定するには、単一の下線を付ける必要があります。繰り返しますが、これは誰かが実際にそれにアクセスすることを妨げません。
IGON

14
グイドはこの質問に答えました -「(ほぼ)すべてを発見可能にする主な理由はデバッグでした:デバッグするとき、抽象化を打ち破る必要があることがよくあります」-手遅れなので、コメントとして追加しました-回答が多すぎます。
Peter

1
「故意のアクセスを防ぐ」という基準に従うと、ほとんどのOOP言語は真にプライベートなメンバーをサポートしません。たとえば、C ++ではメモリに直接アクセスでき、C#では信頼できるコードでプライベートリフレクションを使用できます。
CodesInChaos 2016

207

プライベート機能の例

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 ###

12
self = MyClass() self.private_function()。:もちろんDは、それがクラスで仕事をしませんが、あなただけのカスタム関数を定義する必要があります def foo(self): self.private_function()
ケーシーKuball

161
念のため、はっきりしていません:実際のコードでは絶対にこれを行わないでください;)
Sudo Bash

10
@ThorSummonerまたは単にfunction_call.startswith('self.')
nyuszika7h 2014

13
inspect.stack()[1][4][0].strip()<-それらの1、4、0のマジックナンバーは何ですか?
akhy 2016年

5
これは、実行することで非常に簡単に無効にするself = MyClass(); self.private_function()ことができx = self.private_function()、メソッド内で使用すると失敗します。
ウィル

171

私が最初にJavaからPythonに来たとき、これは嫌いでした。それは私を死ぬほど怖がらせた。

今日、それはPythonで私が最も好きなものの1つにすぎないかもしれません。

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

カプセル化は、「セキュリティ」や、子供たちを芝生から遠ざけることとはあまり関係がありません。これは、コードベースを理解しやすくするために使用すべきもう1つのパターンです。


36
@CamJackson Javascriptはあなたの例ですか?prototybeベースの継承を持ち、関数型プログラミングを支持する唯一の広く使用されている言語ですか?JSは、従来のOOPからいくつかの直交するステップを踏むため、他のほとんどの言語よりも学ぶのがはるかに難しいと思います。これは馬鹿がJSを書くのを防ぐのではなく、それを知らないだけです;)
K.Steff

23
APIは、カプセル化が重要である理由と、プライベートメソッドが好まれる場合の実に良い例です。プライベートになることを意図したメソッドは、その後の新しいバージョンでは、すべての警告なしで、消えてしまうか、署名を変更するか、すべての変更動作の中で最悪の場合があります。賢くて大人のチームメンバーは、更新してから1年後、プライベートになる方法にアクセスしたことを本当に覚えていますか?彼女はもうそこで働くのでしょうか?
2014年

2
私はその議論に同意しません。プロダクションコードでは、パブリックメンバーを「機能させる」ように変更させるバグのあるAPIを使用することはおそらくないでしょう。APIが機能するはずです。そうでない場合は、バグレポートを提出するか、同じAPIを自分で作成します。私は哲学が好きではありませんが、Pythonはあまり好きではありませんが、その構文により、小さなスクリプトを書くのが楽しくなります...
Yngve Sneen Lindal 14

4
JavaにはMethod.setAccessibleとField.setAccessibleがあります。また怖い?
トニー、

15
JavaとC ++での強制は、Pythonがそうであるのに対してJavaがユーザーを信用しないためではありません。これは、コンパイラやvmがこの情報を知っている場合、ビジネスについての処理を行うときにさまざまな仮定を行うことができるためです。たとえば、C ++は、仮想呼び出しの代わりに通常の古いC呼び出しを使用して、間接参照のレイヤー全体をスキップできます。高性能または高精度のものに取り組むときに問題になります。Pythonの性質上、そのダイナミズムを損なうことなく情報を実際に活用することはできません。どちらの言語も異なる目的を目指しているため、どちらも「間違っている」わけではありません
Shayne

144

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

厳密に言えば、プライベートメソッドはクラスの外部からアクセスできますが、簡単にはアクセスできません。Pythonには真にプライベートなものはありません。内部的には、プライベートメソッドと属性の名前は、指定された名前ではアクセスできないように見えるように、オンザフライでマングルおよびアンマングルされます。MP3FileInfoクラスの__parseメソッドには、_MP3FileInfo__parseという名前でアクセスできます。これが面白いことを認め、実際のコードでは絶対にしないでください。プライベートメソッドは理由によりプライベートですが、Pythonの他の多くのものと同様に、そのプライベート性は最終的には強制ではなく慣例の問題です。


202
または、Guido van Rossumが言うように、「私たちはすべて大人です」。

35
-1:これは間違いです。ダブルアンダースコアは、そもそもプライベートとして使用することを意図したものではありません。以下のAlyaからの答えは、名前マングリング構文の本当の意図を伝えています。実際の規則は、単一の下線です。
-nosklo

2
アンダースコアを1つだけ使用してみてください。そうすれば、結果が表示されます。@nosklo
Billal Begueradj 2017

93

一般的に使用されるフレーズは、「ここではすべて同意する大人です」です。1つのアンダースコア(非公開)または2つのアンダースコア(非表示)を前に付けることにより、クラスのユーザーに、メンバーを何らかの方法で「プライベート」にするつもりであることを伝えます。ただし、やむを得ない理由(デバッガ、コード補完など)がない限り、他のすべての人が責任を持って行動し、それを尊重することを信頼しています。

本当にプライベートなものが必要な場合は、拡張機能(Cの場合はCPythonなど)に実装できます。ただし、ほとんどの場合、Pythonで行う方法を学ぶだけです。


保護された変数にアクセスするために使用するはずのある種のラッパープロトコルはありますか?
2010年

3
「保護された」変数は「プライベート」以上ありません。アンダースコアで始まる属性にアクセスしたい場合は、それを行うことができます(ただし、著者はこれを推奨していません)。二重下線で始まる属性にアクセスする必要がある場合は、名前を自分でマングルすることができますが、ほとんどの場合、これを行いたくありません。
Tony Meyer

33

どの言語でも、メンバーのプライベート性(C ++でのポインター演算、.NET / Javaでのリフレクション)を完全に回避できないわけではありません。

ポイントは、誤ってプライベートメソッドを呼び出そうとすると、エラーが発生するということです。しかし、自分を足で撃ちたい場合は、先に進んでください。

編集:オブジェクトをOOカプセル化で保護しようとしないのですか?


2
どういたしまして。私は、開発者に簡単に、そして私見では魔法のように「プライベート」なプロパティにアクセスする方法を提供するのは奇妙だということを指摘しています。
ウィルド

2
ええ、私はポイントを説明しようとしただけです。プライベートにすることは、コンパイラに文句を言うことによって「これに直接アクセスすべきではない」とだけ言っています。しかし、彼は本当にそれを本当にやりたいと思っています。しかし、そうです、Pythonでは他のほとんどの言語よりも簡単です。
Maximilian

7
Javaでは、実際にはカプセル化によってデータを保護できますが、そのためにはスマートで信頼できないコードをSecurityManagerで実行する必要があり、十分に注意してください。オラクルでさえ、時々間違っている。
アンチモン2013

12

class.__stuff命名規則は、プログラマは、彼がアクセスすることを意味するものではない知ることができます__stuff外から。マングリングという名前は、誰もが偶然にそうすることはほとんどありません。

確かに、これを回避することはできます。他の言語よりも簡単です(BTWでもこれを行うことができます)。ただし、カプセル化に関心がある場合、Pythonプログラマーはこれを行いません。


12

モジュールの属性名が単一のアンダースコア(_fooなど)で始まる場合も、同様の動作が発生します。

このように名前が付けられたモジュール属性は、from*メソッドの使用時にインポートモジュールにコピーされません。例:

from bar import *

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


12

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

もちろん、責任を持って使用される可能なアプリケーションとして、デバッグフックとテストが思い浮かびます。


4

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

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

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


2

プライベートメソッドと属性に関する最も重要な問題は、クラスの外で呼び出さないように開発者に指示することです。これはカプセル化です。セキュリティをカプセル化と誤解する可能性があります。あなたがあなたが言及した(以下の)そのような構文を故意に使用するとき、あなたはカプセル化を望まないでしょう。

obj._MyClass__myPrivateMethod()

私はC#から移行しましたが、最初は私にとっても奇妙でしたが、しばらくしてから、PythonコードデザイナーがOOPについて考える方法だけが違うという考えに行き着きました。


1

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

私が理解しているように、彼ら非公開にすることはできません。プライバシーはどのように施行されますか?

明白な答えは「プライベートメンバーはを通じてのみアクセスできるselfselfですが、これは機能しません。Pythonでは特別ではなく、関数の最初のパラメーターに一般的に使用される名前にすぎません。

弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.