Pythonでのプライベートモジュール関数の定義


238

http://www.faqs.org/docs/diveintopython/fileinfo_private.htmlによると:

ほとんどの言語と同様に、Pythonにもプライベート要素の概念があります。

  • モジュールの外部から呼び出すことができないプライベート関数

ただし、2つのファイルを定義した場合:

#a.py
__num=1

そして:

#b.py
import a
print a.__num

実行するb.py1、例外なく印刷されます。diveintopythonは間違っていますか、それとも誤解していませんか?そして、するいくつかの方法があるプライベートとしてモジュールの関数を定義するには?


diveintopythonが間違っているわけではありませんが、その例で >>> import fileinfo >>> m = fileinfo.MP3FileInfo() >>> m.__parse("/music/_singles/kairo.mp3") 1 Traceback (innermost last): File "<interactive input>", line 1, in ? AttributeError: 'MP3FileInfo' instance has no attribute '__parse' は、fileinfo.MP3FileInfo()はクラスのインスタンスです。ダブルアンダースコアを使用すると、この例外が発生します。あなたの場合では、クラスを作成しなかったのに対して、モジュールを作成しただけです。参照:stackoverflow.com/questions/70528/...
Homero Esmeraldo

回答:


323

Pythonでは、「プライバシー」は「同意する大人」の合意レベルに依存します-あなたはそれを強制することはできません(実際の生活でできること以上のものはありません;-)。単一の先行アンダースコアは、「外部から」それにアクセスすること想定していないことを意味します。2つの先行アンダースコア(後続アンダースコアなし)は、メッセージをさらに強力に伝えます...しかし、結局、それは依然としてソーシャルに依存します慣習とコンセンサス:Pythonの内省は非常に強力であるため、世界中の他のすべてのプログラマーが自分の希望を尊重するために手錠をかけることはできません。

((それは秘密にされた秘密ですが、C ++でもほぼ同じです。ほとんどのコンパイラーでは、ファイル#define private public#includeINGする前の簡単な行で.h、賢明なコーダーが「プライバシー」のハッシュを作成するだけです...!-) )


82
C ++に関する注意事項は正しくありません。#define private publicを使用することで、名前の変換が行われるコンパイラーに送信されるコードを変更します。
rhinoinrepose

14
また、C ++マングリングはあいまいですが、ほとんど秘密にされていません。C ++によって生成されたバイナリを「内省」することもできます。OT、ごめんなさい。
ファルケン教授の契約が

47
@rhinoinreposeの更新として、これは単に正しくないだけでなく、プリプロセッサマクロを使用してキーワードを再定義することは、標準に従って未定義の動作です。
Cory Kramer

3
@AlexMartelliは非公開ではありませんstatic void foo()。少なくともリンカには隠されており、インライン化することで関数を完全に削除できます。
user877329 2017年

3
彼らは法律を破る場合は実際の生活の人々で起訴ます
レイ・ゴンサレス

288

クラスprivatesモジュールprivatesの間に混乱があるかもしれません。

モジュールプライベートで始まるアンダー1
このようなAエレメントが使用時に沿ってコピーされていないfrom <module_name> import *インポート・コマンドの形式を、ただし、import <moudule_name>構文を使用する場合はインポートされます(Ben Wilhelmの回答を参照
質問の例のa .__ numから1つのアンダースコアを削除するだけで、from a import *構文を使用してa.pyをインポートするモジュールには表示されません。

クラスプライベートで始まる2つの下線 (別名dunderすなわちD-oubleアンダースコア)は、
このような変数は、クラス名などを含めるように「マングル」その名前を持っている
それはまだマングルされた名前を通じて、クラスロジックの外にアクセスすることができます。
名前マングリングは、不正アクセスに対する穏やかな防止デバイスとして機能しますが、その主な目的は、祖先クラスのクラスメンバーとの名前の衝突を防ぐことです。これらの変数に関して使用されている規則について説明しているアレックスマーテリの同意している大人への面白いが正確なリファレンスを参照してください。

>>> class Foo(object):
...    __bar = 99
...    def PrintBar(self):
...        print(self.__bar)
...
>>> myFoo = Foo()
>>> myFoo.__bar  #direct attempt no go
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
AttributeError: 'Foo' object has no attribute '__bar'
>>> myFoo.PrintBar()  # the class itself of course can access it
99
>>> dir(Foo)    # yet can see it
['PrintBar', '_Foo__bar', '__class__', '__delattr__', '__dict__', '__doc__', '__
format__', '__getattribute__', '__hash__', '__init__', '__module__', '__new__',
'__reduce__', '__reduce_ex__', '__repr__', '__setattr__', '__sizeof__', '__str__
', '__subclasshook__', '__weakref__']
>>> myFoo._Foo__bar  #and get to it by its mangled name !  (but I shouldn't!!!)
99
>>>

さて、TIL。彼らがモジュールレベルを強制しない理由__private_functionはありますか?私はこれに遭遇し、それが原因でエラーに遭遇しました。
サンタ

@Terrabitsという優しい言葉をありがとうございましたが、私はすべての「Python」に関してAlexの次に(2つ後ろに)喜んで立っています。さらに、アレックスが言語とコミュニティに多大な貢献をしているので、彼の回答は通常、より簡潔でユーモラスでありながら、高いレベルの権威を保持しています。
mjv

1
@mjvこれはとても役立つ説明でした!ありがとうございました!私はしばらくの間、この振る舞いにかなり困惑しています。クラスprivateに直接アクセスしようとした場合、AttributeError以外の何らかのエラーをスローするように選択されていればよかったのですが。おそらく "PrivateAccessError"か何かがもっと明示的で役に立つでしょう。(属性がないというエラーが発生するため、実際には真ではありませ)。
HFBrowning 2018年

82

モジュールのプライバシーは純粋に慣習的ではなく、インポートの使用方法によってはモジュールのプライバシーが認識される場合と認識されない場合があるため、この質問には完全には答えられませんでした。

モジュールでプライベート名を定義すると、それらの名前、「import module_name」という構文を使用するスクリプトにインポートされます。したがって、例のようにa.pyでモジュールprivate _numを正しく定義したとすると、次のようになります。

#a.py
_num=1

..モジュール名シンボルを使用してb.pyでアクセスできます。

#b.py
import a
...
foo = a._num # 1

非プライベートのみをa.pyからインポートするには、from構文を使用する必要があります。

#b.py
from a import *
...
foo = _num # throws NameError: name '_num' is not defined

ただし、わかりやすくするために、名前をすべて「*」でインポートするのではなく、モジュールから名前をインポートする場合は明示する方が適切です。

#b.py
from a import name1 
from a import name2
...

1
インポートする関数/ライブラリはどこで指定しますか?内のinitの.py?
FistOfFury

_names呼び出されたときに名前が衝突するリスクはありません。import aこれらはa._names、このスタイルを使用する場合と同様にアクセスです。
Josiah Yoder 2017

@FistOfFuryはい、__init__.pyファイルにインポートされた関数を指定します。これに関するいくつかのヘルプについては、こちらを参照してください。
マイクウィリアムソン

29

Pythonでは、2つの下線プレフィックスを持つプライベートクラスメンバーを使用できます。この手法はモジュールレベルでは機能しないため、これはDive Into Pythonの誤りだと思います。

次に、プライベートクラス関数の例を示します。

class foo():
    def bar(self): pass
    def __bar(self): pass

f = foo()
f.bar()   # this call succeeds
f.__bar() # this call fails

2
OPの目的は、たとえば商用パッケージの外部ではアクセスできない関数を記述することだと思います。その点で、この答えは完全ではありません。__bar()関数は、f._foo__bar()を介して外部から引き続きアクセスできます。したがって、二重先頭のアンダースコアはそれをプライベートにしません。
SevakPrime

24

内部関数を追加できます:

def public(self, args):
   def private(self.root, data):
       if (self.root != None):
          pass #do something with data

あなたが本当にそのレベルのプライバシーを必要とするならば、そのような何か。


9
なぜこれが最善の答えではないのですか?
サファイ


1

クロージャまたは関数を埋め込むのも1つの方法です。これはJSで一般的ですが、ブラウザ以外のプラットフォームやブラウザワーカーには必要ありません。

Pythonでは少し奇妙に見えますが、何かを本当に非表示にする必要がある場合は、それが方法かもしれません。Python APIを使用し、C(または他の言語)で非表示にする必要があるものを維持することは、おそらく最善の方法です。失敗すると、コードを関数内に配置し、それを呼び出して、エクスポートするアイテムを返します。


-11

Pythonには、プライベート、パブリック、保護の3つのモードがあります。モジュールのインポート中は、パブリックモードのみにアクセスできます。したがって、プライベートモジュールと保護されたモジュールは、モジュールの外部から、つまりインポート時に呼び出すことができません。

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