私はますます Python を使用しており__all__
、さまざまなファイルに変数が設定されているのを見続けてい__init__.py
ます。誰かがこれが何をするのか説明できますか?
11 に答える
リンクされていますが、ここでは明示的に言及されていませんが、正確に__all__
使用される場合です。モジュールで が使用されたときにモジュール内のどのシンボルがエクスポートされるかを定義する文字列のリストfrom <module> import *
です。
たとえば、 a の次のコードはfoo.py
、シンボルbar
および を明示的にエクスポートしbaz
ます。
__all__ = ['bar', 'baz']
waz = 5
bar = 10
def baz(): return 'baz'
これらのシンボルは、次のようにインポートできます。
from foo import *
print(bar)
print(baz)
# The following will trigger an exception, as "waz" is not exported by the module
print(waz)
__all__
上記がコメントアウトされている場合、このコードは最後まで実行されます。デフォルトの動作は、import *
指定された名前空間からアンダースコアで始まらないすべてのシンボルをインポートするためです。
参照: https://docs.python.org/tutorial/modules.html#importing-from-a-package
注: 動作のみ__all__
に影響します。from <module> import *
で言及されていないメンバーは__all__
、モジュールの外部から引き続きアクセスでき、 でインポートできますfrom <module> import <member>
。
によって解釈される、そのモジュールの公開オブジェクトのリストですimport *
。アンダースコアで始まるすべてを非表示にするデフォルトをオーバーライドします。
Pythonですべて説明しますか?
さまざまなファイルに変数
__all__
セットが表示され続け__init__.py
ます。これは何をしますか?
何をし__all__
ますか?
モジュールから意味的に「パブリック」な名前を宣言します。に名前がある場合__all__
、ユーザーはそれを使用することが期待され、変更されないという期待を持つことができます。
また、プログラム上の効果もあります。
import *
__all__
モジュールで、例えばmodule.py
:
__all__ = ['foo', 'Bar']
つまりimport *
、モジュールから移動すると、 内の名前のみ__all__
がインポートされます。
from module import * # imports foo and Bar
ドキュメンテーション ツール
ドキュメンテーションおよびコード自動補完ツールは__all__
、モジュールから利用可能なものとして表示する名前を決定するために を検査する場合もあります (実際、そうすべきです)。
__init__.py
ディレクトリを Python パッケージにする
ドキュメントから:
これらの
__init__.py
ファイルは、Python がディレクトリをパッケージを含むものとして扱うために必要です。これは、文字列などの共通名を持つディレクトリが、モジュール検索パスで後で発生する有効なモジュールを意図せずに隠してしまうのを防ぐために行われます。
最も単純なケースで
__init__.py
は、空のファイルにすることもできますが、パッケージの初期化コードを実行したり、__all__
変数を設定したりすることもできます。
したがって、はパッケージに対して__init__.py
を宣言できます。__all__
API の管理:
パッケージは通常、相互にインポートできるモジュールで構成されていますが、それらは必ずファイルと結び付けられてい__init__.py
ます。そのファイルは、ディレクトリを実際の Python パッケージにするものです。たとえば、パッケージに次のファイルがあるとします。
package
├── __init__.py
├── module_1.py
└── module_2.py
これらのファイルを Python で作成して、従うことができるようにしましょう。以下を Python 3 シェルに貼り付けることができます。
from pathlib import Path
package = Path('package')
package.mkdir()
(package / '__init__.py').write_text("""
from .module_1 import *
from .module_2 import *
""")
package_module_1 = package / 'module_1.py'
package_module_1.write_text("""
__all__ = ['foo']
imp_detail1 = imp_detail2 = imp_detail3 = None
def foo(): pass
""")
package_module_2 = package / 'module_2.py'
package_module_2.write_text("""
__all__ = ['Bar']
imp_detail1 = imp_detail2 = imp_detail3 = None
class Bar: pass
""")
これで、他の誰かがあなたのパッケージをインポートするときに使用できる完全な API が表示されました。次のようになります。
import package
package.foo()
package.Bar()
package
また、パッケージには、名前空間を乱雑にするモジュールを作成するときに使用した他の実装の詳細がすべて含まれているわけではありません。
__all__
の__init__.py
さらに作業を重ねた結果、モジュールが大きすぎて (数千行のように)、分割する必要があると判断したかもしれません。したがって、次のようにします。
package
├── __init__.py
├── module_1
│ ├── foo_implementation.py
│ └── __init__.py
└── module_2
├── Bar_implementation.py
└── __init__.py
まず、モジュールと同じ名前のサブパッケージ ディレクトリを作成します。
subpackage_1 = package / 'module_1'
subpackage_1.mkdir()
subpackage_2 = package / 'module_2'
subpackage_2.mkdir()
実装を移動します。
package_module_1.rename(subpackage_1 / 'foo_implementation.py')
package_module_2.rename(subpackage_2 / 'Bar_implementation.py')
for each__init__.py
を宣言するサブパッケージの を作成します。__all__
(subpackage_1 / '__init__.py').write_text("""
from .foo_implementation import *
__all__ = ['foo']
""")
(subpackage_2 / '__init__.py').write_text("""
from .Bar_implementation import *
__all__ = ['Bar']
""")
これで、API がパッケージ レベルでプロビジョニングされました。
>>> import package
>>> package.foo()
>>> package.Bar()
<package.module_2.Bar_implementation.Bar object at 0x7f0c2349d210>
また、サブパッケージのモジュール レベルではなく、サブパッケージ レベルで管理できるものを API に簡単に追加できます。API に新しい名前を追加したい場合は__init__.py
、module_2 などの を更新するだけです。
from .Bar_implementation import *
from .Baz_implementation import *
__all__ = ['Bar', 'Baz']
Baz
また、トップ レベル API で公開する準備ができていない場合は、トップ レベルで次の__init__.py
ようにすることができます。
from .module_1 import * # also constrained by __all__'s
from .module_2 import * # in the __init__.py's
__all__ = ['foo', 'Bar'] # further constraining the names advertised
ユーザーが の可用性を認識している場合はBaz
、それを使用できます。
import package
package.Baz()
しかし、彼らがそれについて知らない場合、他のツール ( pydocなど) は通知しません。
Baz
がプライム タイムの準備ができたら、後で変更できます。
from .module_1 import *
from .module_2 import *
__all__ = ['foo', 'Bar', 'Baz']
接頭辞_
と__all__
:
デフォルトでは、Python は . で始まらないすべての名前をエクスポートします_
。あなたは確かにこのメカニズムに頼ることができます. 実際、Python 標準ライブラリの一部のパッケージはこれに依存していますが、そうするために、たとえば次のようにインポートにエイリアスを設定していますctypes/__init__.py
。
import os as _os, sys as _sys
この_
規則を使用すると、名前を再度命名する冗長性がなくなるため、より洗練されたものになる可能性があります。ただし、インポートの冗長性が追加されます (インポートが多数ある場合) 。これを一貫して行うことを忘れがちです。また、実装の詳細のみを意図したものを無期限にサポートする必要があることは、最も避けたいことです。関数に名前を付けるときにan をプレフィックスにするのを忘れたためです_
。
__all__
私は自分のコードを使用する可能性のある他の人が何を使用し、何を使用しないかを理解できるように、モジュールの開発ライフサイクルの早い段階で個人的に書いています。
標準ライブラリのほとんどのパッケージも__all__
.
回避すること__all__
が理にかなっている場合
次の場合_
の代わりに、接頭辞の規則に固執することは理にかなっています。__all__
- あなたはまだ開発初期段階にあり、ユーザーもおらず、常に API を微調整しています。
- ユーザーはいるかもしれませんが、API をカバーする単体テストがあり、まだ積極的に API に追加し、開発を微調整しています。
export
デコレータ_
使用の欠点は、__all__
エクスポートされる関数とクラスの名前を 2 回記述する必要があることです。情報は定義とは別に保持されます。この問題を解決するには、デコレータを使用できます。
このようなエクスポート デコレーターのアイデアは、David Beazley のパッケージングに関する講演から得ました。この実装は、CPython の従来のインポーターでうまく機能するようです。特別なインポート フックまたはシステムがある場合、私はそれを保証しませんが、それを採用する場合、取り消すのはかなり簡単です。__all__
たとえば、ユーティリティ ライブラリでは、デコレータを次のように定義します。
import sys
def export(fn):
mod = sys.modules[fn.__module__]
if hasattr(mod, '__all__'):
mod.__all__.append(fn.__name__)
else:
mod.__all__ = [fn.__name__]
return fn
そして、 を定義する場所で、次の__all__
ようにします。
$ cat > main.py
from lib import export
__all__ = [] # optional - we create a list if __all__ is not there.
@export
def foo(): pass
@export
def bar():
'bar'
def main():
print('main')
if __name__ == '__main__':
main()
そして、これはメインとして実行されているか、別の関数によってインポートされているかに関係なく、正常に機能します。
$ cat > run.py
import main
main.main()
$ python run.py
main
また、次の API プロビジョニングimport *
も機能します。
$ cat > run.py
from main import *
foo()
bar()
main() # expected to error here, not exported
$ python run.py
Traceback (most recent call last):
File "run.py", line 4, in <module>
main() # expected to error here, not exported
NameError: name 'main' is not defined
また、pydoc が表示する内容も変更されます。
module1.py
a = "A"
b = "B"
c = "C"
module2.py
__all__ = ['a', 'b']
a = "A"
b = "B"
c = "C"
$ pydoc モジュール 1
モジュール module1 のヘルプ: 名前 モジュール1 ファイル module1.py データ a = 'A' b = 'B' c = 'C'
$ pydoc モジュール 2
モジュール module2 のヘルプ: 名前 モジュール2 ファイル module2.py DATA __all__ = ['a', 'b'] a = 'A' b = 'B'
私はすべてのモジュールで宣言__all__
し、内部の詳細にアンダースコアを付けます。これらは、ライブ通訳セッションでこれまでに使用したことがないものを使用する場合に非常に役立ちます。
__all__
とでカスタマイズ*
します。from <module> import *
from <package> import *
モジュールは、.py
インポートするためのファイルです。
パッケージは、__init__.py
ファイルを含むディレクトリです。通常、パッケージにはモジュールが含まれています。
モジュール
""" cheese.py - an example module """
__all__ = ['swiss', 'cheddar']
swiss = 4.99
cheddar = 3.99
gouda = 10.99
__all__
モジュールの「公開」機能を人間に知らせます。[ @AaronHall ] また、pydoc はそれらを認識します。[ @ロングポーク]
モジュールのインポートから*
とがローカル名前空間に持ち込まれる方法swiss
を確認しますが、そうではありません:cheddar
gouda
>>> from cheese import *
>>> swiss, cheddar
(4.99, 3.99)
>>> gouda
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
NameError: name 'gouda' is not defined
がなければ__all__
、任意の記号 (アンダースコアで始まらないもの) が使用可能になります。
なしのインポート*
は影響を受けません__all__
モジュールのインポート
>>> import cheese
>>> cheese.swiss, cheese.cheddar, cheese.gouda
(4.99, 3.99, 10.99)
モジュールのインポート名から
>>> from cheese import swiss, cheddar, gouda
>>> swiss, cheddar, gouda
(4.99, 3.99, 10.99)
モジュールをローカル名としてインポート
>>> import cheese as ch
>>> ch.swiss, ch.cheddar, ch.gouda
(4.99, 3.99, 10.99)
パッケージ
パッケージの__init__.py
ファイルには、公開モジュールまたはその他のオブジェクトの名前を含む文字列のリストがあります。これらの機能は、ワイルドカード インポートで使用できます。モジュールと同様に、パッケージからのワイルドカード インポート時に をカスタマイズします。[ @MartinStettner ] __all__
__all__
*
Python MySQL Connector からの抜粋を次に示し__init__.py
ます。
__all__ = [
'MySQLConnection', 'Connect', 'custom_error_exception',
# Some useful constants
'FieldType', 'FieldFlag', 'ClientFlag', 'CharacterSet', 'RefreshOption',
'HAVE_CEXT',
# Error handling
'Error', 'Warning',
...etc...
]
パッケージ内のすべてのモジュールを検索するためにファイル システムを使用するという明らかな動作はコストがかかるため、デフォルトのケースであるパッケージにno を指定したアスタリスクは複雑です。__all__
代わりに、ドキュメントを読むと、で定義されているオブジェクトのみ__init__.py
がインポートされます。
が定義されていない場合
__all__
、ステートメントはすべてのサブモジュールをパッケージから現在の名前空間にインポートfrom sound.effects import *
しません。sound.effects
パッケージがインポートされていることを確認するだけでsound.effects
(おそらく で初期化コードを実行している可能性があります__init__.py
)、パッケージで定義されている名前が何であれインポートします。これには、 によって定義された名前 (および明示的にロードされたサブモジュール) が含まれ__init__.py
ます。また、以前の import ステートメントによって明示的にロードされたパッケージのサブモジュールも含まれます。
そして最後に、どこにでもあるスタック オーバーフローの回答、教授、マンスプレイナーの尊敬される伝統は、そもそも質問をすることに対する非難のボンモットです。
ワイルドカード インポートは ... リーダーや多くの自動ツールを [混乱させる] ため、避ける必要があります。
[ PEP 8、@ToolmakerSteve]
モジュールによって定義されたパブリック名は、モジュールの名前空間で という名前の変数をチェックすることによって決定されます
__all__
。定義されている場合、そのモジュールによって定義またはインポートされた名前である一連の文字列でなければなりません。で指定された名前__all__
はすべてパブリックと見なされ、存在する必要があります。が定義されていない場合__all__
、パブリック名のセットには、アンダースコア文字 ("_") で始まらないモジュールの名前空間にあるすべての名前が含まれます。__all__
パブリック API 全体を含める必要があります。API の一部ではないアイテム (モジュール内でインポートおよび使用されたライブラリ モジュールなど) を誤ってエクスポートしないようにすることを目的としています。
__all__
Python モジュールのパブリック API を文書化するために使用されます。オプションですが、__all__
使用する必要があります。
Python 言語リファレンスからの関連する抜粋を次に示します。
モジュールによって定義されたパブリック名は、モジュールの名前空間で という名前の変数をチェックすることによって決定されます
__all__
。定義されている場合、そのモジュールによって定義またはインポートされた名前である一連の文字列でなければなりません。で指定された名前__all__
はすべてパブリックと見なされ、存在する必要があります。が定義されていない場合__all__
、パブリック名のセットには、アンダースコア文字 ('_') で始まらないモジュールの名前空間にあるすべての名前が含まれます。__all__
パブリック API 全体を含める必要があります。API の一部ではないアイテム (モジュール内でインポートおよび使用されたライブラリ モジュールなど) を誤ってエクスポートしないようにすることを目的としています。
PEP 8も同様の表現を使用していますが、 が存在しない場合、インポートされた名前はパブリック API の一部ではないことも明確にしています__all__
。
__all__
イントロスペクションをより適切にサポートするには、モジュールは属性を使用してパブリック API で名前を明示的に宣言する必要があります。__all__
空のリストに設定すると、モジュールにパブリック API がないことを示します。[...]
インポートされた名前は、常に実装の詳細と見なされる必要があります。
os.path
他のモジュールは、サブモジュールから機能を公開するパッケージの__init__
モジュールなど、含まれるモジュールの API の明示的に文書化された部分でない限り、そのようなインポートされた名前への間接アクセスに依存してはなりません。
さらに、他の回答で指摘されているように、パッケージのワイルドカード インポート__all__
を有効にするために使用されます。
import ステートメントは次の規則を使用します: パッケージの
__init__.py
コードが という名前のリストを定義している場合__all__
、それは、 が検出されたときにインポートする必要があるモジュール名のリストであると見なされますfrom package import *
。
__all__
働き方に影響しますfrom foo import *
。
*
モジュール本体内にあるコード (ただし、関数またはクラスの本体内ではない) では、from
ステートメントでアスタリスク ( ) を使用できます。
from foo import *
モジュールの*
すべての属性foo
(アンダースコアで始まるものを除く) が、インポート モジュールでグローバル変数としてバインドされるように要求します。foo
が属性を持つ場合__all__
、属性の値は、このタイプのfrom
ステートメントによってバインドされる名前のリストです。
foo
がパッケージで、__init__.py
という名前のリストが定義されている場合__all__
、 が検出されたときにインポートする必要があるサブモジュール名のリストと見なされますfrom foo import *
。が定義されていない場合__all__
、ステートメントfrom foo import *
はパッケージで定義されている名前をインポートします。これには、 によって定義された名前 (および明示的にロードされたサブモジュール) が含まれ__init__.py
ます。
__all__
リストである必要はないことに注意してください。import
ステートメントのドキュメントに従って、定義されている場合、モジュールによって定義またはインポートされた名前である文字列__all__
のシーケンスである必要があります。したがって、タプルを使用してメモリと CPU サイクルを節約することもできます。モジュールが単一のパブリック名を定義する場合は、カンマを忘れないでください:
__all__ = ('some_name',)
「import *」が悪いのはなぜですか?も参照してください。
これはPEP8で定義されています:
グローバル変数名
(これらの変数が 1 つのモジュール内でのみ使用されることを期待しましょう。) 規則は関数の規則とほぼ同じです。
経由で使用するように設計されたモジュールは、グローバルのエクスポートを防ぐメカニズムを
from M import *
使用する__all__
か、そのようなグローバルの前にアンダースコアを付けるという古い規則を使用する必要があります (これらのグローバルが「モジュール非公開」であることを示すために行うことができます)。
PEP8 は、主要な Python ディストリビューションの標準ライブラリを構成する Python コードのコーディング規則を提供します。これを踏襲すればするほど、本来の意図に近づきます。