内部関数をインポートするのはPythonicですか?
-
06-07-2019 - |
質問
PEP 8 のコメント:
- インポートは常にモジュールの直後のファイルの先頭に配置されます コメントとドキュメント文字列、およびモジュールのグローバルと定数の前。
occationで、私はPEP 8に違反します。関数の中に何かをインポートすることがあります。原則として、単一の関数内でのみ使用されるインポートがある場合、これを行います。
意見はありますか
編集(関数をインポートするのがよいと思う理由):
主な理由:コードを明確にすることができます。
- 関数のコードを見るとき、「関数/クラスxxxとは何ですか?」 (xxxは関数内で使用されています)。モジュールの一番上にすべてのインポートがある場合、xxxが何であるかを判別するためにそこに行く必要があります。これは、
from m import xxx
を使用する場合の問題です。関数でm.xxx
を見ると、おそらくもっとわかります。m
の内容によって異なります:有名なトップレベルのモジュール/パッケージ(import m
)ですか?それともサブモジュール/パッケージ(a.b.c import m
から)ですか? - 場合によっては、xxxが使用されている場所の近くにその追加情報(「xxxとは何ですか?」)があると、関数が理解しやすくなります。
解決
長期的には、インポートのほとんどがファイルの先頭にあることに感謝すると思います。これにより、インポートする必要のあるモジュールがモジュールの複雑さを一目でわかるようになります。
既存のファイルに新しいコードを追加する場合、通常は必要な場所でインポートを行い、コードが残っている場合は、インポート行をファイルの先頭に移動することでより永続的なものにします。
もう1つのポイントとして、コードを実行する前に健全性チェックとして ImportError
例外を取得することを好みます。そのため、一番上でインポートするもう1つの理由です。
未使用のモジュールをチェックするために pyChecker
を使用します。
他のヒント
この点でPEP 8に違反する場合が2つあります:
- 循環インポート:モジュールAはモジュールBをインポートしますが、モジュールBの何かにはモジュールAが必要です(ただし、これは循環依存関係を排除するためにモジュールをリファクタリングする必要があることがよくあります)
- pdbブレークポイントの挿入:
import pdb; pdb.set_trace()
これは便利ですb / cデバッグしたいすべてのモジュールの先頭にimport pdb
を置きたくないので、簡単に削除できますブレークポイントを削除したときのインポート。
これら2つのケース以外では、すべてを最上部に配置することをお勧めします。依存関係が明確になります。
使用する4つのインポートユースケースは次のとおりです
-
上部の
-
import
(およびfrom x import y
およびimport x y as
) -
インポートの選択。上部。
import settings if setting.something: import this as foo else: import that as foo
-
条件付きインポート。 JSON、XMLライブラリなどで使用されます。上部。
try: import this as foo except ImportError: import that as foo
-
動的インポート。これまでのところ、この例は1つしかありません。
import settings module_stuff = {} module= __import__( settings.some_module, module_stuff ) x = module_stuff['x']
この動的インポートはコードをもたらさないが、複雑をもたらすことに注意してください Pythonで記述されたデータ構造。それは一種のデータのピクルスのようなものです 手で漬けた以外は。
これは、多かれ少なかれ、モジュールの上部にもあります
コードを明確にするために行うことは次のとおりです。
-
モジュールを短くします。
-
すべてのインポートがモジュールの上部にある場合、名前を確認するためにそこに行く必要があります。モジュールが短い場合、それは簡単です。
-
場合によっては、名前が使用されている場所の近くに追加情報があると、関数が理解しやすくなります。モジュールが短い場合、それは簡単です。
念頭に置くべきことの1つは、不必要なインポートがパフォーマンスの問題を引き起こす可能性があることです。したがって、これが頻繁に呼び出される関数である場合は、インポートを最上部に配置することをお勧めします。もちろん、これは最適化であるため、関数の内部へのインポートがファイルの先頭でのインポートよりも明確であるという正当なケースがある場合、ほとんどの場合、パフォーマンスよりも優先されます。
IronPythonを実行している場合、関数内にインポートする方が良いと言われます(IronPythonでのコードのコンパイルは遅くなる可能性があるため)。したがって、内部関数をインポートする方法を取得できる場合があります。しかし、それ以外は、コンベンションと戦うだけの価値はないと主張します。
一般的なルールとして、単一の関数内でのみ使用されるインポートがある場合、これを行います。
もう1つ指摘したいのは、これがメンテナンスの問題になる可能性があるということです。以前に1つの関数のみで使用されていたモジュールを使用する関数を追加するとどうなりますか?インポートをファイルの先頭に追加することを忘れないでしょうか?または、インポートのためにすべての関数をスキャンしますか?
FWIW、関数内にインポートすることが理にかなっている場合があります。たとえば、cx_Oracleで言語を設定する場合、NLS _
LANG環境変数をインポートする前に 設定する必要があります。したがって、次のようなコードが表示される場合があります。
import os
oracle = None
def InitializeOracle(lang):
global oracle
os.environ['NLS_LANG'] = lang
import cx_Oracle
oracle = cx_Oracle
自己テストを行うモジュールについては、このルールを破ったことがあります。つまり、通常はサポートのためだけに使用されますが、それらをメインで定義して、自分で実行した場合に機能をテストできるようにします。その場合、メインで getopt
と cmd
をインポートすることがあります。これらのモジュールが通常の操作とは無関係であることをコードを読んでいる人に明確にしたいからです。モジュールの、テスト用にのみ含まれています。
モジュールの2回の読み込みに関する質問から取得-なぜ両方ではないのですか?
スクリプトの上部にあるインポートは、この関数をよりアトミックにする関数の依存関係と別のインポートを示しますが、連続インポートは安価であるため、パフォーマンス上のデメリットはありません。
from x import *
ではなく、 import
である限り、それらを先頭に配置する必要があります。グローバル名前空間に名前を1つだけ追加し、PEP 8に固執します。さらに、後で別の場所で必要になった場合は、何も移動する必要はありません。
大したことではありませんが、違いはほとんどないので、PEP 8の言うことをすることをお勧めします。
sqlalchemyで使用されている代替アプローチを見てください:依存性注入:
@util.dependencies("sqlalchemy.orm.query")
def merge_result(query, *args):
#...
query.Query(...)
デコレータでインポートされたライブラリがどのように宣言され、関数への引数として渡されるかに注意してください!
このアプローチにより、コードがよりクリーンになり、 import
ステートメントよりも 4.5倍高速になります!
ベンチマーク: https://gist.github.com/kolypto/589e84fbcfb6312532658df2fabdb796