1. ホーム
  2. python

[解決済み] from __future__ import absolute_import は実際に何をするのですか?

2022-03-05 18:17:16

質問

私は 回答済み を読んで理解したつもりだったのですが、Pythonの絶対インポートに関する質問です。 Python 2.5 チェンジログ とそれに付随する PEP . しかし、Python 2.5 をインストールして from __future__ import absolute_import しかし、そう簡単にはいきません。

この文章は、上記のリンク先のチェンジログからそのまま、絶対的なインポートの変更について、私の理解を正確に要約したものです。

<ブロッククオート

例えば、こんなパッケージディレクトリがあるとします。

pkg/
pkg/__init__.py
pkg/main.py
pkg/string.py

という名前のパッケージが定義されています。 pkg を含む pkg.mainpkg.string サブモジュールを使用します。

main.pyモジュールのコードを考えてみましょう。もしそれが以下の文を実行したらどうなるでしょうか? import string ? Python 2.4 以前では、まずパッケージのディレクトリを探して相対 import を行い、 pkg/string.py を見つけて、そのファイルの中身を pkg.string という名前にバインドされ、そのモジュールは "string" の中で pkg.main モジュールの名前空間です。

そこで、この通りのディレクトリ構造を作りました。

$ ls -R
.:
pkg/

./pkg:
__init__.py  main.py  string.py

__init__.pystring.py は空です。 main.py には、次のようなコードが含まれています。

import string
print string.ascii_uppercase

予想通り、Python 2.5でこれを実行すると、以下のようなエラーが発生して失敗します。 AttributeError :

$ python2.5 pkg/main.py
Traceback (most recent call last):
  File "pkg/main.py", line 2, in <module>
    print string.ascii_uppercase
AttributeError: 'module' object has no attribute 'ascii_uppercase'

しかし、2.5の変更履歴を見ると、さらに次のようなことが書かれています(強調)。

Python 2.5では、以下のように切り替えることができます。 import の挙動を絶対インポートに変更するには from __future__ import absolute_import ディレクティブを使用します。この絶対インポート動作は将来のバージョン(おそらく Python 2.7)でデフォルトとなる予定です。 一旦絶対インポートがデフォルトになります。 import string は常に標準ライブラリのバージョンを見つけます。

こうして私は pkg/main2.py と同じです。 main.py が、future importディレクティブが追加されています。現在では、次のようになります。

from __future__ import absolute_import
import string
print string.ascii_uppercase

Python 2.5でこれを実行すると... AttributeError :

$ python2.5 pkg/main2.py
Traceback (most recent call last):
  File "pkg/main2.py", line 3, in <module>
    print string.ascii_uppercase
AttributeError: 'module' object has no attribute 'ascii_uppercase'

という記述とかなり真っ向から矛盾しています。 import string 意志 常に は、絶対インポートを有効にした std-lib バージョンを見つけることができます。さらに、絶対インポートが "新しいデフォルトの動作になる予定だという警告にもかかわらず、Python 2.7で __future__ ディレクティブを使用します。

$ python2.7 pkg/main.py
Traceback (most recent call last):
  File "pkg/main.py", line 2, in <module>
    print string.ascii_uppercase
AttributeError: 'module' object has no attribute 'ascii_uppercase'

$ python2.7 pkg/main2.py
Traceback (most recent call last):
  File "pkg/main2.py", line 3, in <module>
    print string.ascii_uppercase
AttributeError: 'module' object has no attribute 'ascii_uppercase'

の有無にかかわらず、Python 3.5 と同じです。 print ステートメントを両方のファイルで変更します。)

$ python3.5 pkg/main.py
Traceback (most recent call last):
  File "pkg/main.py", line 2, in <module>
    print(string.ascii_uppercase)
AttributeError: module 'string' has no attribute 'ascii_uppercase'

$ python3.5 pkg/main2.py
Traceback (most recent call last):
  File "pkg/main2.py", line 3, in <module>
    print(string.ascii_uppercase)
AttributeError: module 'string' has no attribute 'ascii_uppercase'


私はこれの他のバリエーションをテストしました。代わりに string.py という名前のディレクトリを作成しました。 string のみを含む、空の __init__.py -- からインポートを発行するのではなく main.py を持つ。 cd にする。 pkg を実行し、REPLから直接importを実行しました。これらのバリエーションはいずれも(またそれらの組み合わせも)、上記の結果を変えませんでした。このことは、私が読んだ __future__ ディレクティブと絶対インポート

で簡単に説明できそうです。 以下 (これはPython 2のドキュメントからですが、この文はPython 3でも同じドキュメントで変更されていません)。

sys.path

(...)

プログラム起動時に初期化されるため、このリストの最初の項目となります。 path[0] は、Python インタープリタを起動するために使用されたスクリプトを含むディレクトリです。スクリプトディレクトリが利用できない場合(インタープリタが対話的に起動された場合や、スクリプトが標準入力から読み込まれた場合など)。 path[0] は空文字列です。 これは Python に現在のディレクトリのモジュールを最初に検索するように指示します。

では、何が足りないのでしょうか?なぜ __future__ また、この2つの文書、および記述と実際の動作の間の矛盾はどのように解決されるのでしょうか?

解決方法は?

変更履歴がぞんざいな表現になっている。 from __future__ import absolute_import は、何かが標準ライブラリの一部であるかどうかを気にしません。 import string は、常に絶対インポートをオンにした標準ライブラリ・モジュールを与えるとは限りません。

from __future__ import absolute_import というのは、もしあなたが import string は、Python が常にトップレベルの string モジュールではなく current_package.string . しかし、Python がどのファイルが string モジュールになります。を実行すると

python pkg/script.py

pkg/script.py は、Pythonにとってはパッケージの一部には見えません。通常の手続きに従って pkg ディレクトリがパスに追加され、すべての .py のファイルは pkg ディレクトリは、トップレベルのモジュールのように見えます。 import string 見つける pkg/string.py は、相対インポートを行っているからではなく pkg/string.py はトップレベルのモジュールであるように見える string . これが標準ライブラリでないことは string モジュールは出てきません。

の一部として実行するには pkg パッケージの場合、次のようになります。

python -m pkg.script

この場合 pkg ディレクトリはパスに追加されません。しかし、カレント・ディレクトリはパスに追加されます。

また、いくつかのボイラープレートを pkg/script.py の一部としてPythonに扱わせることができます。 pkg パッケージとして実行された場合でも

if __name__ == '__main__' and __package__ is None:
    __package__ = 'pkg'

しかし、これは sys.path . を削除するには、いくつかの追加処理が必要です。 pkg ディレクトリをパスから削除し、もし pkg の親ディレクトリがパス上にない場合は、それもパス上に貼り付ける必要があります。