誰かが Python で __all__ を説明できますか?
-
09-06-2019 - |
質問
Python をますます使用するようになりましたが、変数が次のように表示されます。 __all__
別の設定にします __init__.py
ファイル。誰かこれが何をするのか説明してもらえますか?
解決
これは、によって解釈される、そのモジュールのパブリック オブジェクトのリストです。 import *
. 。これは、アンダースコアで始まるすべてを非表示にするデフォルトをオーバーライドします。
他のヒント
にリンクされていますが、ここでは明示的に言及されていませんが、まさにそのときです __all__
使用されている。これは、モジュール内のどのシンボルがいつエクスポートされるかを定義する文字列のリストです。 from <module> import *
モジュールで使用されます。
たとえば、次のコードは 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>
.
正確に言うためにこれを追加しているだけです:
他のすべての回答は以下を参照しています モジュール. 。元の質問は明示的に言及されています __all__
で __init__.py
ファイルなので、これはPythonについてです パッケージ.
一般的に、 __all__
の場合にのみ機能します。 from xxx import *
の亜種 import
というステートメントが使用されます。これはモジュールだけでなくパッケージにも当てはまります。
モジュールの動作については、他の回答で説明されています。パッケージの正確な動作について説明します ここ 詳細に。
要するに、 __all__
パッケージ レベルでは、モジュールの場合とほぼ同じことを行いますが、処理する点が異なります。 パッケージ内のモジュール (指定するのとは対照的に、 モジュール内の名前)。それで __all__
を使用するときに現在の名前空間にロードおよびインポートされるすべてのモジュールを指定します。 from package import *
.
大きな違いは、 省略 の宣言 __all__
パッケージの中で __init__.py
, 、ステートメント from package import *
何もインポートしません (ドキュメントで説明されている例外はあります。上記のリンクを参照してください)。
一方、省略すると、 __all__
モジュールでは、「スター付きインポート」は、モジュールで定義されているすべての名前 (アンダースコアで始まらない) をインポートします。
Python で __all__ を説明してください?
変数を見続けています
__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 # makes directory a Python package
|-module_1.py
|-module_2.py
の中に __init__.py
あなたが書く:
from module_1 import *
from module_2 import *
そしてで module_1
あなたが持っている:
__all__ = ['foo',]
そしてで module_2
あなたが持っている:
__all__ = ['Bar',]
これで、他の人がパッケージをインポートするときに使用できる完全な API が提示されました。次のようになります。
import package
package.foo()
package.Bar()
また、モジュールを作成するときに使用した他の名前がすべて付いて、ファイルが乱雑になることもありません。 package
名前空間。
__all__
で __init__.py
さらに作業を行った結果、モジュールが大きすぎるため分割する必要があると判断したかもしれません。そこで、次のことを行います。
package/
|-__init__.py
|-module_1/
| |-__init__.py
| |-foo_implementation.py
|-module_2/
|-__init__.py
|-Bar_implementation.py
そしてそれぞれに __init__.py
あなたは宣言します __all__
, 、例えばmodule_1 内:
from foo_implementation import *
__all__ = ['foo']
そして module_2 の __init__.py
:
from Bar_implementation import *
__all__ = ['Bar']
また、サブパッケージのモジュール レベルではなく、サブパッケージ レベルで管理できるものを 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
の使用 _
規則により、名前を再度命名するという冗長性がなくなるため、より洗練されたものになります。ただし、インポートの冗長性が追加されます (インポートが多数ある場合)。 簡単 これを一貫して行うことを忘れること - そして最も避けたいのは、単に実装の詳細であるつもりだったものを、プレフィックスを付け忘れたからといって無期限にサポートしなければならないことです。 _
関数に名前を付けるとき。
私は個人的に書いています __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
Help on module module1: 名前 module1 ファイル module1.py データ ある = 'A' b = 'B' c = 'C'
$ pydocモジュール2
Help on module module2: 名前 module2 ファイル module2.py データ __全て__ = ['a', 'b'] ある = 'A' b = 'B'
私は宣言する __all__
私のすべてのモジュール、およびアンダースコアの内部詳細で、これらはライブ通訳セッションでこれまで使用したことのないものを使用するときに非常に役立ちます。
モジュールによって定義されたパブリック名は、モジュールの名前空間で変数名をチェックすることによって決定されます。
__all__
;定義されている場合は、そのモジュールによって定義またはインポートされた名前である文字列のシーケンスである必要があります。で与えられた名前__all__
これらはすべてパブリックとみなされ、存在する必要があります。もし__all__
が定義されていない場合、パブリック名のセットには、モジュールの名前空間で見つかった、アンダースコア文字 (「_」) で始まらないすべての名前が含まれます。__all__
パブリック API 全体を含める必要があります。これは、API の一部ではない項目 (モジュール内でインポートおよび使用されたライブラリ モジュールなど) を誤ってエクスポートすることを避けることを目的としています。
__all__
カスタマイズする アスタリスク で from <module> import *
__all__
カスタマイズする アスタリスク で 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
のファイル パッケージ __all__
パブリック モジュールまたはその他のオブジェクトの名前を含む文字列のリストです。これらの機能はワイルドカードインポートで利用できます。モジュールと同様に、 __all__
をカスタマイズします *
パッケージからワイルドカードをインポートするとき。[@MartinStettner]
以下はその抜粋です Python MySQL コネクタ __init__.py
:
__all__ = [
'MySQLConnection', 'Connect', 'custom_error_exception',
# Some useful constants
'FieldType', 'FieldFlag', 'ClientFlag', 'CharacterSet', 'RefreshOption',
'HAVE_CEXT',
# Error handling
'Error', 'Warning',
...etc...
]
デフォルトのケースでは、 なしのアスタリスク __all__
パッケージ用, 、明白な動作はコストがかかるため、複雑になります。ファイル システムを使用してパッケージ内のすべてのモジュールを検索します。代わりに、私がドキュメントを読んだ限りでは、で定義されているオブジェクトのみが含まれていました。 __init__.py
インポートされます:
もし
__all__
が定義されていない場合、ステートメントはfrom sound.effects import *
する ない パッケージからすべてのサブモジュールをインポートしますsound.effects
現在の名前空間に。パッケージが確実に存在することだけを保証します。sound.effects
インポートされました (おそらく初期化コードが実行されています)__init__.py
) そして、パッケージ内で定義されている名前をインポートします。これには、によって定義された名前 (および明示的にロードされたサブモジュール) が含まれます。__init__.py
. 。これには、以前の import ステートメントによって明示的にロードされたパッケージのサブモジュールも含まれます。
ワイルドカードのインポート ...読者や多くの自動ツールを[混乱させる]ため、避けるべきです。
[PEP8, 、@ToolmakerSteve]
短い答え
__all__
影響を与える from <module> import *
発言。
長い答え
次の例を考えてみましょう。
foo
├── bar.py
└── __init__.py
で foo/__init__.py
:
(暗黙的に)定義しない場合
__all__
, 、 それからfrom foo import *
で定義された名前のみをインポートしますfoo/__init__.py
.(明示的に) 定義すると
__all__ = []
, 、 それからfrom foo import *
何もインポートしません。(明示的に) 定義すると
__all__ = [ <name1>, ... ]
, 、 それからfrom foo import *
これらの名前のみをインポートします。
暗黙的な場合、Python は次で始まる名前をインポートしないことに注意してください。 _
. 。ただし、次を使用してそのような名前を強制的にインポートできます。 __all__
.
Python ドキュメントを表示できます ここ.
__all__
Python モジュールのパブリック API を文書化するために使用されます。オプションではありますが、 __all__
使用すべきです。
関連する抜粋は次のとおりです Python 言語リファレンス:
モジュールによって定義されたパブリック名は、モジュールの名前空間で次の名前の変数をチェックすることによって決定されます。
__all__
;定義されている場合は、そのモジュールによって定義またはインポートされた名前である文字列のシーケンスである必要があります。で与えられた名前__all__
これらはすべてパブリックとみなされ、存在する必要があります。もし__all__
が定義されていない場合、パブリック名のセットには、モジュールの名前空間で見つかった、アンダースコア文字 ('_') で始まらないすべての名前が含まれます。__all__
パブリック API 全体を含める必要があります。これは、API の一部ではない項目 (モジュール内でインポートおよび使用されたライブラリ モジュールなど) を誤ってエクスポートすることを避けることを目的としています。
PEP8 も同様の文言を使用していますが、インポートされた名前がパブリック API の一部ではないことも明確にしています。 __all__
欠席だ:
イントロスペクションをより適切にサポートするには、モジュールはパブリック API で名前を明示的に宣言する必要があります。
__all__
属性。設定__all__
空のリストは、モジュールにパブリック API がないことを示します。[...]
インポートされた名前は常に実装の詳細として考慮される必要があります。他のモジュールは、含まれるモジュールの API の明示的に文書化された部分でない限り、そのようなインポートされた名前への間接的なアクセスに依存してはなりません。
os.path
またはパッケージの__init__
サブモジュールの機能を公開するモジュール。
さらに、他の回答でも指摘されているように、 __all__
を有効にするために使用されます パッケージのワイルドカードインポート:
import ステートメントでは次の規則が使用されます。パッケージの場合
__init__.py
コードはという名前のリストを定義します__all__
, 、これは、インポートする必要があるモジュール名のリストとみなされます。from package import *
に遭遇します。