56

importステートメントをそれを使用するフラグメントの近くに配置すると、依存関係がより明確になり、読みやすくなると思います。Pythonはこれをキャッシュしますか?気にする必要がありますか?これは悪い考えですか?

def Process():
    import StringIO
    file_handle=StringIO.StringIO('hello world')
    #do more stuff

for i in xrange(10): Process()

もう少し正当化:ライブラリの難解なビットを使用するメソッドの場合ですが、メソッドを別のファイルにリファクタリングすると、実行時エラーが発生するまで外部依存関係を見逃したことに気づきません。

4

6 に答える 6

83

他の答えは、実際にどのように機能するかについての穏やかな混乱を示しimportています。

この文:

import foo

次のステートメントとほぼ同等です。

foo = __import__('foo', globals(), locals(), [], -1)

つまり、現在のスコープに、要求されたモジュールと同じ名前の変数を作成し、__import__()そのモジュール名と大量のデフォルト引数を使用して呼び出した結果を割り当てます。

__import__()関数handlesは、概念的に文字列()をモジュールオブジェクトに変換します'foo'。モジュールはにキャッシュされsys.modules、それが最初に__import__()見えます。sys.modulesにのエントリがある場合'foo'__import__('foo')それが何であれ、それが返されます。それは本当にタイプを気にしません。これが実際に動作しているのを自分で見ることができます。次のコードを実行してみてください。

import sys
sys.modules['boop'] = (1, 2, 3)
import boop
print boop

今のところ文体の懸念はさておき、関数内にimportステートメントを含めると、希望どおりに機能します。モジュールがこれまでにインポートされたことがない場合は、インポートされてsys.modulesにキャッシュされます。次に、モジュールをその名前のローカル変数に割り当てます。モジュールレベルの状態は変更されません。一部のグローバル状態を変更する可能性あります(sys.modulesに新しいエントリを追加します)。

そうは言っても、私は関数内で使用することはほとんどありませんimport。モジュールをインポートすると、静的初期化で長い計算が実行される場合や、単に大規模なモジュールである場合など、プログラムに顕著な速度低下が発生し、プログラムが実際にモジュールを必要とすることはめったにない場合は、インポートを内部にのみ行うことはまったく問題ありません。それが使用される機能。(これが不快な場合、Guidoはタイムマシンにジャンプし、Pythonを変更して、それができないようにします。)しかし、原則として、私と一般的なPythonコミュニティは、すべてのインポートステートメントをモジュールスコープのモジュールの先頭に配置します。

于 2009-11-11T16:31:37.797 に答える
14

PEP 8を参照してください:

インポートは、モジュールのコメントとdocstringの直後、モジュールのグローバル変数と定数の前に、常にファイルの先頭に配置されます。

importPythonは、ソースファイルのどこで宣言されているかに関係なく、すべてのステートメントを同じように扱うため、これは純粋に文体的な選択であることに注意してください。それでも、コードを他の人が読みやすくするために、一般的な方法に従うことをお勧めします。

于 2009-11-09T04:35:55.333 に答える
13

スタイルはさておき、インポートされたモジュールは一度だけインポートされることは事実です(そのモジュールreloadで呼び出されない限り)。ただし、への各呼び出しimport Fooは、そのモジュールがすでにロードされているかどうかを暗黙的にチェックします(チェックによってsys.modules)。

一方がモジュールをインポートしようとし、もう一方がインポートしようとしない、他の点では同等の2つの関数の「分解」についても検討してください。

>>> def Foo():
...     import random
...     return random.randint(1,100)
... 
>>> dis.dis(Foo)
  2           0 LOAD_CONST               1 (-1)
              3 LOAD_CONST               0 (None)
              6 IMPORT_NAME              0 (random)
              9 STORE_FAST               0 (random)

  3          12 LOAD_FAST                0 (random)
             15 LOAD_ATTR                1 (randint)
             18 LOAD_CONST               2 (1)
             21 LOAD_CONST               3 (100)
             24 CALL_FUNCTION            2
             27 RETURN_VALUE        
>>> def Bar():
...     return random.randint(1,100)
... 
>>> dis.dis(Bar)
  2           0 LOAD_GLOBAL              0 (random)
              3 LOAD_ATTR                1 (randint)
              6 LOAD_CONST               1 (1)
              9 LOAD_CONST               2 (100)
             12 CALL_FUNCTION            2
             15 RETURN_VALUE        

仮想マシン用にバイトコードがどれだけ変換されるかはわかりませんが、これがプログラムの重要な内部ループである場合は、BarアプローチよりもFooアプローチに重点を置く必要があります。

迅速で汚れtimeitたテストでは、以下を使用すると速度が適度に向上することが示されていBarます。

$ python -m timeit -s "from a import Foo,Bar" -n 200000 "Foo()"
200000 loops, best of 3: 10.3 usec per loop
$ python -m timeit -s "from a import Foo,Bar" -n 200000 "Bar()"
200000 loops, best of 3: 6.45 usec per loop
于 2009-11-09T04:41:17.387 に答える
8

私はこれをしました、そしてそれから私がしなかったらいいのにと思いました。通常、関数を作成していて、その関数を使用する必要がある場合StringIOは、モジュールの上部を見て、インポートされているかどうかを確認し、インポートされていない場合は追加します。

私がこれをしないとしましょう。関数内でローカルに追加するとします。そして、ある時点で、私または他の誰かが、を使用する他の関数の束を追加するとしますStringIO。その人はモジュールの上部を見て、を追加しimport StringIOます。これで、関数に予期しないだけでなく冗長なコードが含まれるようになりました。

また、これは非常に重要な原則であると私が考えることに違反しています。関数内からモジュールレベルの状態を直接変更しないでください。

編集:

実際、上記のすべてがナンセンスであることがわかります。

モジュールをインポートしても、モジュールレベルの状態変更されません(他にまだ何もない場合は、インポートされるモジュールが初期化されますが、それはまったく同じではありません)。すでに他の場所にインポートしたモジュールをインポートする場合sys.modules、ローカルスコープを検索して変数を作成する以外に費用はかかりません。

これを知っていると、コード内の修正したすべての場所を修正するのはちょっと馬鹿げていると感じますが、それは私の十字架です。

于 2009-11-09T22:03:22.167 に答える
3

Pythonインタープリターがインポートステートメントをヒットすると、インポートされているファイル内のすべての関数定義の読み取りを開始します。これは、インポートに時間がかかる場合がある理由を説明しています。

Andrew Hareが指摘しているように、最初にすべてのインポートを実行する背後にある考え方は、文体的な慣習です。ただし、そうすることで、最初にインポートした後にこのファイルがすでにインポートされているかどうかをインタプリタに暗黙的にチェックさせることに注意する必要があります。また、コードファイルが大きくなり、コードを「アップグレード」して特定の依存関係を削除または置換する場合にも問題になります。これには、コードファイル全体を検索して、このモジュールをインポートしたすべての場所を見つける必要があります。

規則に従い、インポートをコードファイルの先頭に保持することをお勧めします。関数の依存関係を本当に追跡したい場合は、その関数のdocstringにそれらを追加することをお勧めします。

于 2009-11-09T04:45:57.907 に答える
1

ローカルにインポートする必要がある場合、2つの方法がわかります

  1. テスト目的または一時的な使用のために、何かをインポートする必要があります。その場合、使用場所にインポートを配置する必要があります。

  2. 循環依存を回避するために、関数内にインポートする必要がある場合がありますが、それ以外の場合は問題が発生します。

それ以外の場合は、効率と一貫性のために常に一番上に置きます。

于 2009-11-09T05:03:23.003 に答える