2

一部の機能に必要なライブラリの一部が欠落している場合でも、実行できる必要がある Python プログラムに取り組んでいます。(編集:提案された最良の解決策を実装するための小さなコードを書きました。それはhereであり、doctest hereがあります。)

このようなライブラリの import ステートメントを、Python ファイルの先頭ではなく、それらを使用する関数にインラインで配置することで、これを解決しました。これは、ライブラリがなくてもファイルを完全にロードできることを意味しますが、関数の 1 つを呼び出そうとすると、もちろん ImportError がスローされます。

これは非常にうまく機能しているので、標準ライブラリ モジュールに対してもこれを行うことがあります。

ベースライン コード:

import numpy

def foo(): 
  return numpy.array([])

def bar(): 
  return numpy.array([1, 2, 3])

インライン インポートを含むコード:

def foo(): 
  import numpy
  return numpy.array([])

def bar(): 
  import numpy
  return numpy.array([1, 2, 3])

編集:

標準ライブラリコードをインライン化しないことに完全に同意します-明らかに悪いです。

保護されたインポートが正しい解決策だと思います。

特に、私は呼び出しに対していくつかのタイミング テストを行いました。時間差は、おそらくほとんどのアプリケーションにとって重要ではありませんが、かなりの差があります (細かい線、私は知っています!)。

些細なケースでは

import numpy
def f():
  return numpy

私のマシンでは、100,000回の繰り返しで約180ミリ秒かかりますが、

def f():
  import numpy
  return numpy

約870msかかります。

非常に大雑把に言えば、これには 4 つの些細な関数呼び出しに相当するコストがかかるということです。それでも、費用がかからない場合は避けるのが最善です。

試してみると、インライン インポートのもう 1 つの欠点にも気付きました。関数が呼び出されたときに、これらのインポートが予期しないタイミングでオフになることです。リアルタイム要素を持つ私のアプリケーションでは、これは受け入れられません。

4

4 に答える 4

9

パフォーマンスに大きな影響はありませんが、コードが乱雑になります。新しいインポートを追加するか、古いインポートを変更する必要がある場合は、1 か所だけでなく、どこでも変更する必要があります。

また、これが文書化されていることを確認する必要があります。ライブラリが正しくインポートされているように見えても、後で特定の関数が呼び出されたときに失敗する場合、一部のユーザーはイライラする可能性があります。さらに、全体的なパフォーマンス ヒットはありませんが、予期しない場所でスローダウンを引き起こすパフォーマンスの「入れ替え」が発生する可能性があります。をインポートする関数を初めて呼び出すときnumpyは、インポートを実行する必要があり、これには時間がかかります。ユーザーはこれが望ましくなく、すべての遅いインポートを前もって実行したいと思うかもしれません。

all-at-the-top インポートを使用すると、同様の効果を簡単に得ることができます。

try:
    import numpy
except ImportError:
    warnings.warn("Numpy not available, some functions may not work!")

アクセスしようとする関数を後で使用しようとnumpyすると、NameError で失敗するようになりました。警告 (または単に印刷/ログに記録されたメッセージ) を使用することで、後で突然失敗するのではなく、いくつかのことが機能しないことを事前に通知することもできます。

于 2013-02-12T20:19:54.607 に答える
3

これを行うことで、PEP 8に従っていません。標準ライブラリのインポートの場合、正当な理由もなくそうしています。これは二重に悪いことであり、一部の人々があなたのコードを敬遠するのに十分です (または、少なくとも丁寧にそうすべきではないとしつこく言います)。

もちろん、PEP 8 は理由もなくそう言っているわけではありません。この場合、個人的な好みや統一性よりも優れた理由があります。すべてのインポートを一番上に置くと、モジュールの依存関係を非常に簡単に見つけることができます。インポートがファイル全体に分散している場合、これはさらに面倒になります。さらに、実質的にライブラリへのすべての呼び出しで が発生する可能性がありますがImportError、これはかなり残念なことです: 通常のワークフローはすべてをインポートすることであり、インポートできる場合は動作すると見なされます (これは、virtualenv をセットアップする際の手動テストに役立ちます)。あまりよく書かれていないコードは、I/O などの処理を開始し、途中で関数を呼び出し ( ImportError.

また、インポートを含む関数が呼び出されるたびにいくつかの追加の命令が実行されるという点で、わずかなオーバーヘッドもあります。ただし、このオーバーヘッドはほとんどの目的にとってかなり小さく、モジュールを 2 回 (または 3 回、または何度も) インポートすることはありません。もちろん、DRYにも違反しています。

この問題に直面したとき、私と他の人々は、インポートをとにかくファイルの先頭に置き、 で囲むことを選択しましたtry: ... except ImportError:。次に、ダミーの値を割り当てたり、警告を発したり、何かをログに記録したり、ケースで意味のあることをしたりできます。代替モジュールをインポートすることもできます (たとえば、特定のモジュールを持たない古い Python バージョンをサポートする場合)、または自分で提供するスタブ モジュールをインポートすることもできます。

于 2013-02-12T20:21:15.183 に答える
1

あまり。

インポートは 1 回だけ行われますが、(ユーザーにとって) 予期しないときに発生する可能性があります。つまり、インポートを実行する関数が初めて呼び出されたときです。

また、読みやすさの問題でもあります。慣例に従ってインポートを上部で行うと、コードのすべての読者は、その依存関係が何であるかをすぐに理解できます。モジュールの 284 行目でインポートが行われると、その明快さが失われる可能性があります...

于 2013-02-12T20:19:47.727 に答える
0

いいえ、これを行う際にマイナス面や隠れたコストがあってはなりません。モジュールはキャッシュされ、複数回インポートしても 1 回だけ実行されます。インポートは、モジュールへのローカル参照を (再) 設定するだけです。

于 2013-02-12T20:19:10.643 に答える