web-dev-qa-db-ja.com

名前空間を汚染せずにpythonモジュールでインポートを実行するにはどうすればよいですか?

私はいくつかの科学データを処理するためのPythonパッケージを開発しています。numpyを含む他のモジュールやパッケージから頻繁に使用されるクラスや関数が複数あり、いずれかで定義されているほぼすべての関数で必要です。パッケージのモジュール。

それらに対処するためのPythonの方法は何でしょうか?私は複数のバリエーションを検討しましたが、それぞれに独自の欠点があります。

  • _from foreignmodule import Class1, Class2, function1, function2_を使用してモジュールレベルでクラスをインポートします
    インポートされた関数とクラスは、すべての関数から簡単にアクセスできます。一方、モジュールの名前空間を汚染して、dir(package.module)help(package.module)をインポートされた関数で乱雑にします

  • _from foreignmodule import Class1, Class2, function1, function2_を使用して関数レベルでクラスをインポートします
    関数とクラスは簡単にアクセスでき、モジュールを汚染しませんが、すべての関数の最大12個のモジュールからのインポートは、多くの重複コードのように見えます。

  • _import foreignmodule_を使用してモジュールレベルでモジュールをインポートします
    すべての関数またはクラス呼び出しの前にモジュール名を追加する必要があるため、汚染はそれほど多くありません。

  • これらすべての操作に関数本体を使用し、エクスポートするオブジェクトのみを返すなど、人為的な回避策を使用してください...このように

    _def _export():
        from foreignmodule import Class1, Class2, function1, function2
        def myfunc(x):
            return function1(x, function2(x))
        return myfunc
    myfunc = _export()
    del _export
    _

    これにより、モジュールの名前空間の汚染と関数の使いやすさの両方の問題をなんとか解決できます...しかし、Pythonicではないようです。

では、最もPythonicなソリューションは何ですか?私が見落とした別の良い解決策はありますか?

32
Tanriol

先に進み、通常の_from W import X, Y, Z_を実行してから、___all___特殊記号を使用して、モジュールからインポートする実際の記号を定義します。

___all__ = ('MyClass1', 'MyClass2', 'myvar1', …)
_

これは、ユーザーのモジュールから_import *_の場合に、ユーザーのモジュールにインポートされるシンボルを定義します。

一般に、Pythonプログラマーはnotdir()を使用してモジュールの使用方法を理解する必要があり、そうしている場合は、ドキュメントを読んだり、help(yourmodule)と入力してライブラリの使用方法を理解したり、ソースコードを自分で参照したりする必要があります。その場合、(a)インポートするものの違いそして、あなたが定義することは非常に明確であり、(b)彼らは___all___宣言を見て、どのおもちゃで遊ぶべきかを知っています。

このような状況で、設計されていないタスクに対してdir()をサポートしようとすると、他の回答から明らかなように、自分のコードに厄介な制限を課す必要があります。私のアドバイス:それをしないでください!ガイダンスについては、標準ライブラリを参照してください。コードの明快さと簡潔さが必要な場合はいつでも_from … import …_を実行し、(1)有益なドキュメント文字列、(2)完全なドキュメント、および(3)読み取り可能なコードを提供するため、誰もモジュールでdir()を実行し、モジュールで実際に定義されているものとは別にインポートを伝えようとする必要があります。

20
Brandon Rhodes

標準ライブラリを含め、私が使用した手法の1つは、import module as _moduleまたはfrom module import var as _varを使用することです。つまり、インポートされたモジュール/変数をアンダースコアで始まる名前に割り当てます。

その結果、通常のPython規則に従って、他のコードはそれらのメンバーをプライベートとして扱います。これは、IPythonのオートコンプリート関数など、__all__を参照しないコードにも適用されます。 。

Python 3.3のrandomモジュールの例:

from warnings import warn as _warn
from types import MethodType as _MethodType, BuiltinMethodType as _BuiltinMethodType
from math import log as _log, exp as _exp, pi as _pi, e as _e, ceil as _ceil
from math import sqrt as _sqrt, acos as _acos, cos as _cos, sin as _sin
from os import urandom as _urandom
from collections.abc import Set as _Set, Sequence as _Sequence
from hashlib import sha512 as _sha512

もう1つの手法は、関数スコープでインポートを実行して、ローカル変数になるようにすることです。

"""Some module"""
# imports conventionally go here
def some_function(arg):
    "Do something with arg."
    import re  # Regular expressions solve everything
    ...

これを行う主な理由は、それが事実上怠惰であり、実際に使用されるまでモジュールの依存関係のインポートを遅らせることです。モジュール内の1つの関数が特定の巨大なライブラリに依存していると仮定します。ファイルの先頭にあるライブラリをインポートすると、モジュールをインポートするとライブラリ全体が読み込まれます。このように、モジュールのインポートは迅速に行うことができ、実際にその関数を呼び出すクライアントコードのみが、ライブラリのロードのコストを負担します。さらに、依存関係ライブラリが利用できない場合でも、依存関係機能を必要としないクライアントコードは、モジュールをインポートして他の関数を呼び出すことができます。欠点は、関数レベルのインポートを使用すると、コードの依存関係がわかりにくくなることです。

Python 3.3のos.pyからの例:

def get_exec_path(env=None):
    """[...]"""
    # Use a local import instead of a global import to limit the number of
    # modules loaded at startup: the os module is always loaded at startup by
    # Python. It may also avoid a bootstrap issue.
    import warnings
11

モジュール全体をインポートします:_import foreignmodule_。あなたが欠点として主張することは、実際には利益です。つまり、モジュール名を前に付けると、コードの保守が容易になり、自己文書化が容易になります。

今から6か月後、foo = Bar(baz)のようなコード行を見ると、どのモジュールBarが由来しているかを自問するかもしれませんが、_foo = cleverlib.Bar_を使用すれば、それはそれほど謎ではありません。

もちろん、インポートが少なければ少ないほど、問題は少なくなります。依存関係がほとんどない小さなプログラムの場合、それはそれほど重要ではありません。

このような質問をしていることに気付いたときは、コードを書きやすくするのではなく、コードを理解しやすくする理由を自問してください。あなたは一度それを書きますが、あなたはそれをたくさん読みます。

9
Bryan Oakley

この状況では、すべてのファイルが含まれている_all_imports.py_ファイルを使用します。

_from foreignmodule import .....
from another module import .....
_

次に、作業モジュールで

_import all_imports as fgn # or whatever you want to prepend
...
something = fgn.Class1()
_

注意すべきもう1つのこと

___all__ = ['func1', 'func2', 'this', 'that']
_

これで、モジュール内にあるがnotモジュールの___all___にある関数/クラス/変数などはhelp()に表示されません。 、および_from mymodule import *_によってインポートされません。詳細については、 Making python imports more structure? を参照してください。

3
Ethan Furman

私は妥協して、外部モジュールの短いエイリアスを選択します。

import foreignmodule as fm

それはあなたを汚染(おそらくより大きな問題)から完全に救い、少なくとも前置の負担を減らします。

1
John Y

私はこれが古い質問であることを知っています。 「Pythonic」ではないかもしれませんが、特定のモジュール定義のみをエクスポートするために私が発見した最もクリーンな方法は、実際には、モジュールを関数でグローバルにラップすることです。しかし、それらを返す代わりに、名前をエクスポートするには、単にそれらをグローバル化することができます(グローバルであるため、本質的には一種の「export」キーワードになります)。

 def module():
 global MyPublicClass、ExportedModule 
 
一部のモジュールをExportedModuleとしてインポート
別のモジュールをPrivateModuleとしてインポート
 
 class MyPublicClass:
 def __init __(self):
 pass 
 
 class MyPrivateClass:
 def __init __(self):
 pass 
 
 module()
 del module 

元の結論とそれほど変わらないことはわかっていますが、率直に言って、これが最もクリーンなオプションのようです。もう1つの利点は、この方法で記述されたモジュールをいくつでも1つのファイルにグループ化でき、それらのプライベート用語が重複しないことです。

 def module():
グローバルA 
 
 i、j、k = 1,2,3 
 
クラスA :
 pass 
 
 module()
 del module 
 
 def module():
グローバルB 
 
 i、j、k = 7,8,9#以前の宣言を上書きしません
 
クラスB:
 pass 
 
 module()
 del module 

ただし、それらのpublic定義は、もちろん重複することに注意してください。

0
Codesmith