webdevqa.jp.net

Python 3の相対インポート

同じディレクトリ内の別のファイルから関数をインポートしたいのですが。

時にはそれはfrom .mymodule import myfunctionで私のために働くが、時々私は得る:

SystemError: Parent module '' not loaded, cannot perform relative import

時にはそれはfrom mymodule import myfunctionで動作しますが、時々私はまた得る:

SystemError: Parent module '' not loaded, cannot perform relative import

私はここで論理を理解していません、そして私は説明を見つけることができませんでした。これは完全にランダムに見えます。

誰かが私にこれすべての背後にある論理は何ですか説明してもらえますか?

503

残念ながら、このモジュールはパッケージ内にある必要があります。また、場合によってはスクリプトとして実行できる必要もあります。どうすればそれを達成できますか?

このようなレイアウトを持つことは非常に一般的です...

main.py
mypackage/
    __init__.py
    mymodule.py
    myothermodule.py

...このようなmymodule.pyで...

#!/usr/bin/env python3

# Exported function
def as_int(a):
    return int(a)

# Test function for module  
def _test():
    assert as_int('1') == 1

if __== '__main__':
    _test()

...myothermodule.pyこのように...

#!/usr/bin/env python3

from .mymodule import as_int

# Exported function
def add(a, b):
    return as_int(a) + as_int(b)

# Test function for module  
def _test():
    assert add('1', '1') == 2

if __== '__main__':
    _test()

...およびmain.pyは次のようになります...

#!/usr/bin/env python3

from mypackage.myothermodule import add

def main():
    print(add('1', '1'))

if __== '__main__':
    main()

...main.pyまたはmypackage/mymodule.pyを実行すると正常に動作しますが、相対的なインポートのためにmypackage/myothermodule.pyで失敗します...

from .mymodule import as_int

あなたがそれを実行することになっている方法は...

python3 -m mypackage.myothermodule

...しかし、それはいくぶん冗長で、#!/usr/bin/env python3のようなシバン行とうまく混ざりません。

この場合の最も簡単な修正は、名前mymoduleがグローバルに一意であると仮定して、相対インポートの使用を避け、単に使用することです...

from mymodule import as_int

...ただし、一意ではない場合、またはパッケージ構造がより複雑な場合は、パッケージディレクトリを含むディレクトリをPYTHONPATHに含めて、このようにする必要があります...

from mypackage.mymodule import as_int

...または「そのまま使用」したい場合は、最初にコードでPYTHONPATHをフロブすることができます...

import sys
import os

PACKAGE_PARENT = '..'
SCRIPT_DIR = os.path.dirname(os.path.realpath(os.path.join(os.getcwd(), os.path.expanduser(__file__))))
sys.path.append(os.path.normpath(os.path.join(SCRIPT_DIR, PACKAGE_PARENT)))

from mypackage.mymodule import as_int

ちょっとした痛みですが、なぜ 電子メール に特定のGuido van Rossumが書いたのかについての手がかりがあります...

私はこれと__main__機械の他の提案された調整について-1です。唯一のユースケースは、モジュールのディレクトリ内に存在するスクリプトを実行することであるように思われますが、これは常にアンチパターンと見なされてきました。私の考えを変えさせるには、そうではないことを私に納得させる必要があります。

パッケージ内でスクリプトを実行するのがアンチパターンであるかどうかは主観的ですが、個人的には、いくつかのカスタムwxPythonウィジェットを含むパッケージで本当に便利なので、ソースファイルのいずれかに対してスクリプトを実行してwx.Frameテスト目的のウィジェットのみを含む。

424
Aya

これをあなたのパッケージの__init__.pyファイルの中に入れてください

# For relative imports to work in Python 3.6
import os, sys; sys.path.append(os.path.dirname(os.path.realpath(__file__)))

あなたのパッケージがこのようになっていると仮定します。

├── project
│   ├── package
│   │   ├── __init__.py
│   │   ├── module1.py
│   │   └── module2.py
│   └── setup.py

以下のように、パッケージ内で通常のインポートを使用します。

# in module2.py
from module1 import class1

これはpython 2と3の両方で動作します。

36
am5

私はこの問題に遭遇しました。ハックの回避策は、次のようにif/elseブロックを介してインポートすることです。

#!/usr/bin/env python3
#myothermodule

if __== '__main__':
    from mymodule import as_int
else:
    from .mymodule import as_int


# Exported function
def add(a, b):
    return as_int(a) + as_int(b)

# Test function for module  
def _test():
    assert add('1', '1') == 2

if __== '__main__':
    _test()
33
goffer

うまくいけば、これはそこに誰かに価値があるでしょう - 私はここに上に掲載されたものと同様の相対的な輸入を把握しようとしている半ダースstackoverflowの投稿を見ました。提案どおりにすべてを設定しましたが、まだModuleNotFoundError: No module named 'my_module_name'を打っていました

私はローカルで開発して遊んでいるだけなので、私はsetup.pyファイルを作成/実行していませんでした。私はまた、どうやら私のPYTHONPATHを設定していませんでした。

テストがモジュールと同じディレクトリにあったときと同じようにコードを実行したときに、自分のモジュールが見つからないことに気付きました。

$ python3 test/my_module/module_test.py                                                                                                               2.4.0
Traceback (most recent call last):
  File "test/my_module/module_test.py", line 6, in <module>
    from my_module.module import *
ModuleNotFoundError: No module named 'my_module'

しかし、私が明示的にパスを指定したとき、ものが動き始めました:

$ PYTHONPATH=. python3 test/my_module/module_test.py                                                                                                  2.4.0
...........
----------------------------------------------------------------------
Ran 11 tests in 0.001s

OK

したがって、誰かがいくつかの提案を試みた場合、自分のコードが正しく構造化されていて、現在のディレクトリをPYTHONPATHにエクスポートしないと、私自身が次のいずれかを試みるのと同じ状況にあると考えます。

  1. コードを実行し、そのように明示的にパスを含めます。$ PYTHONPATH=. python3 test/my_module/module_test.py
  2. PYTHONPATH=.を呼び出さないようにするには、次のような内容のsetup.pyファイルを作成し、python setup.py developmentを実行してパッケージをパスに追加します。
# setup.py
from setuptools import setup, find_packages

setup(
    name='sample',
    packages=find_packages()
)
4
LaCroixed

この問題を回避するために、私は repackage パッケージを使って解決策を考案しました。それはlibパスに上位ディレクトリを追加します。

import repackage
repackage.up()
from mypackage.mymodule import myfunction

再パッケージ化では、インテリジェントな戦略(コールスタックの検査)を使用して、さまざまなケースで機能する相対インポートを実行できます。

2
fralau

私はそれを動作させるためにメインプロジェクトディレクトリからpython3を実行する必要がありました。

たとえば、プロジェクトが次のような構造になっているとします。

project_demo/
├── main.py
├── some_package/
│   ├── __init__.py
│   └── project_configs.py
└── test/
    └── test_project_configs.py

溶液

Python3をフォルダ project_demo/ の中で実行してから、

from some_package import project_configs
1
Árthur

両方のパッケージがあなたのインポートパス(sys.path)にあり、あなたが欲しいモジュール/クラスがexample/example.pyにあるなら、相対インポートなしでクラスにアクセスするために試してみてください:

from example.example import fkt
1
Salt999