1369

私はますます Python を使用しており__all__、さまざまなファイルに変数が設定されているのを見続けてい__init__.pyます。誰かがこれが何をするのか説明できますか?

4

11 に答える 11

1225

リンクされていますが、ここでは明示的に言及されていませんが、正確に__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>

于 2008-09-15T15:49:50.213 に答える
786

によって解釈される、そのモジュールの公開オブジェクトのリストですimport *。アンダースコアで始まるすべてを非表示にするデフォルトをオーバーライドします。

于 2008-09-04T21:30:46.213 に答える
440

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
于 2016-02-29T21:58:50.740 に答える
92

また、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__し、内部の詳細にアンダースコアを付けます。これらは、ライブ通訳セッションでこれまでに使用したことがないものを使用する場合に非常に役立ちます。

于 2010-05-15T03:22:29.817 に答える
77

__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を確認しますが、そうではありません:cheddargouda

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

于 2016-03-20T20:20:43.033 に答える
56

(非公式) Python リファレンス Wikiから:

モジュールによって定義されたパブリック名は、モジュールの名前空間で という名前の変数をチェックすることによって決定されます__all__。定義されている場合、そのモジュールによって定義またはインポートされた名前である一連の文字列でなければなりません。で指定された名前__all__はすべてパブリックと見なされ、存在する必要があります。が定義されていない場合__all__、パブリック名のセットには、アンダースコア文字 ("_") で始まらないモジュールの名前空間にあるすべての名前が含まれます。__all__パブリック API 全体を含める必要があります。API の一部ではないアイテム (モジュール内でインポートおよび使用されたライブラリ モジュールなど) を誤ってエクスポートしないようにすることを目的としています。

于 2008-09-04T21:31:16.370 に答える
9

__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 *

于 2016-04-26T01:39:25.020 に答える
9

__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 *」が悪いのはなぜですか?も参照してください。

于 2019-01-09T14:48:45.013 に答える
3

これはPEP8で定義されています

グローバル変数名

(これらの変数が 1 つのモジュール内でのみ使用されることを期待しましょう。) 規則は関数の規則とほぼ同じです。

経由で使用するように設計されたモジュールは、グローバルのエクスポートを防ぐメカニズムをfrom M import *使用する__all__か、そのようなグローバルの前にアンダースコアを付けるという古い規則を使用する必要があります (これらのグローバルが「モジュール非公開」であることを示すために行うことができます)。

PEP8 は、主要な Python ディストリビューションの標準ライブラリを構成する Python コードのコーディング規則を提供します。これを踏襲すればするほど、本来の意図に近づきます。

于 2019-06-17T17:55:21.017 に答える