Pythonで相対インポートを行う方法は?


527

このディレクトリ構造を想像してください:

app/
   __init__.py
   sub1/
      __init__.py
      mod1.py
   sub2/
      __init__.py
      mod2.py

私はコーディングしてmod1おり、から何かをインポートする必要がありmod2ます。どうすればよいですか?

試しましたfrom ..sub2 import mod2が、「パッケージ以外での相対インポートの試み」が発生しました。

私はググったが「sys.path操作」ハックしか見つけなかった。きれいな方法はありませんか?


編集:すべての私__init__.pyのは現在空です

EDIT2:SUB2は、サブパッケージ(全体で共有されているクラス含まれているので、私はこれをやろうとしているsub1subXなど)。

Edit3:私が探している動作はPEP 366で説明されているものと同じです(John Bに感謝)


8
私はあなたの質問を更新することをおすすめしますと、あなたは問題がPEP 366で対処記述していること、それがより明確にする
ジョン・B

2
長い説明ですが、ここを確認してください:stackoverflow.com/a/10713254/1267156私は非常によく似た質問に答えました。昨夜まで同じ問題がありました。
-Sevvy325

3
:任意のパスにあるモジュールをロードしたい人のために、この参照stackoverflow.com/questions/67631/...
エフゲニーSergeev

2
関連する注記として、Python 3はデフォルトでインポートのデフォルトの処理を絶対的なものに変更します。相対インポートは明示的に指定する必要があります。
ロス

回答:


337

誰もが単に質問に答えるのではなく、何をすべきかをあなたに伝えたいようです。

問題は、インタープリターに引数としてmod1.pyを渡すことにより、モジュールを「__main__」として実行していることです。

PEP 328から:

相対インポートは、モジュールの__name__属性を使用して、パッケージ階層におけるそのモジュールの位置を決定します。モジュールの名前にパッケージ情報が含まれていない場合(たとえば、「__ main__」に設定されている場合)、相対インポートは、モジュールがファイルシステムの実際の場所に関係なく、最上位モジュールであるかのように解決されます。

Python 2.6では、メインモジュールに対してモジュールを参照する機能が追加されています。 PEP 366は変更について説明しています。

更新:Nick Coghlan氏によると、推奨される代替手段は-mスイッチを使用してパッケージ内でモジュールを実行することです。


2
ここでの答えは、プログラムへのすべてのエントリポイントでsys.pathをいじることです。それが唯一の方法だと思います。
Nick Retallack

76
推奨される代替策は-m、ファイル名を直接指定するのではなく、スイッチを使用してパッケージ内のモジュールを実行することです。
ncoghlan '23

127
わかりません。ここで答えはどこにありますか?このようなディレクトリ構造でモジュールをインポートするにはどうすればよいですか?
トム

27
@Tom:この場合、mod1はになりfrom sub2 import mod2ます。次に、アプリ内からmod1を実行するには、を実行しますpython -m sub1.mod1
Xiong Chiamiov

11
@XiongChiamiov:これは、Pythonがアプリケーションに埋め込まれている場合は実行できないため、Pythonコマンドラインスイッチにアクセスできないことを意味しますか?
LarsH 2013年

130

ここに私のために働く解決策があります:

from ..sub2 import mod2 次に相対インポート を実行します。実行する場合はmod1.py、の親ディレクトリに移動appし、python -mスイッチを使用してモジュールを実行します python -m app.sub1.mod1

この問題が相対インポートで発生する本当の理由は、相対インポート__name__がモジュールのプロパティを取得することで機能するためです。モジュールが直接実行されている場合は、に__name__設定され__main__、パッケージ構造に関する情報は含まれません。そして、それがpythonがrelative import in non-packageエラーについて文句を言う理由です。

したがって、-mスイッチを使用して、パッケージ構造情報をpythonに提供します。これにより、相対インポートを正常に解決できます。

相対インポートの実行中に、この問題に何度も遭遇しました。そして、以前のすべての回答を読んだ後でも、ボイラープレートコードをすべてのファイルに配置する必要がない限り、それをきれいな方法で解決する方法を見つけることはできませんでした。(@ncoghlanと@XiongChiamiovのおかげで、いくつかのコメントは本当に役に立ちました)

これが相対的な輸入の問題で戦っている人に役立つことを願っています。PEPを通過することは本当に楽しいものではないからです。


9
ベストアンサーIMHO:OPに問題があった理由を説明するだけでなく、モジュールがインポートする方法を変更せずに問題を解決する方法を見つけます。結局、OPの相対的なインポートは問題ありませんでした。犯人は、スクリプトとして直接実行するときに外部パッケージにアクセスできないことでした-m。これは解決するために設計されました。
MestreLion 2013年

26
また、この回答は質問から5年後のことです。当時、これらの機能は利用できませんでした。
JeremyKun 2014

1
同じディレクトリからモジュールをインポートしたい場合は、行うことができますfrom . import some_module
ロタレッティ2016

124
main.py
setup.py
app/ ->
    __init__.py
    package_a/ ->
       __init__.py
       module_a.py
    package_b/ ->
       __init__.py
       module_b.py
  1. あなたが実行しpython main.pyます。
  2. main.py します: import app.package_a.module_a
  3. module_a.py する import app.package_b.module_b

あるいは、2または3は以下を使用できます。 from app.package_a import module_a

あなたがappあなたのPYTHONPATHにいる限り、それはうまくいきます。main.pyそのときどこにでもいることができます。

そのため、setup.pyアプリパッケージとサブパッケージ全体をターゲットシステムのpythonフォルダーとmain.pyターゲットシステムのスクリプトフォルダーにコピー(インストール)するように記述します。


3
すばらしい答えです。PYTHONPATHにパッケージをインストールせずにその方法でインポートする方法はありますか?
オーラハム

4
追加の推奨資料
nosklo

6
その後、ある日、アプリの名前をtest_appに変更する必要があります。どうなる?すべてのソースコードを変更し、app.package_b.module_b-> test_app.package_b.module_bをインポートする必要があります。これは絶対に悪い習慣です...そして、パッケージ内で相対インポートを使用するようにしてください。
Spybdai

49

"Guidoは、パッケージ内で実行中のスクリプトをアンチパターンとして表示します"(拒否された PEP-3122

私は解決策を見つけるために多くの時間を費やし、Stack Overflowの関連記事を読んで、「もっと良い方法があるはずだ!」と自分に言いました。ないように見えます。


10
注:すでに言及したpep-366pep-3122とほぼ同時に作成)は同じ機能を提供しますが、異なる下位互換性のある実装を使用します。つまり、パッケージ内のモジュールをスクリプトとして実行し、明示的な相対インポート使用する場合その中で、-mswitch を使用して実行するpython -m app.sub1.mod1app.sub1.mod1.main()、最上位のスクリプト(たとえば、setup.pyで定義されたsetuptoolsのentry_pointsから生成されたスクリプト)から呼び出すことができます。
jfs 2013

setuptoolsとエントリポイントを使用するための+1-PYTHONPATHを無限にハッキングするのとは対照的に、明確に定義された場所で外部から実行されるスクリプトを設定する適切な方法です
RecencyEffect

38

これは100%解決されます:

  • app /
    • main.py
  • 設定/
    • local_setings.py

app / main.pyにsettings / local_setting.pyをインポートします。

main.py:

import sys
sys.path.insert(0, "../settings")


try:
    from local_settings import *
except ImportError:
    print('No Import')

2
ありがとうございました!すべてのPPLは、スクリプト内でスクリプトを解決する方法を指示する代わりに、スクリプトを別の方法で実行するように強制していました。しかし、私は使用するようにコードを変更しなければならなかったsys.path.insert(0, "../settings")し、その後from local_settings import *
ビタミンBernatikを

25
def import_path(fullpath):
    """ 
    Import a file with full path specification. Allows one to
    import from anywhere, something __import__ does not do. 
    """
    path, filename = os.path.split(fullpath)
    filename, ext = os.path.splitext(filename)
    sys.path.append(path)
    module = __import__(filename)
    reload(module) # Might be out of date
    del sys.path[-1]
    return module

パスからモジュールをインポートするためにこのスニペットを使用しています。


2
このスニペットとimpモジュール(ここで説明する[1]を参照)を組み合わせて使用​​すると、効果が大きくなります。[1]:stackoverflow.com/questions/1096216/...
熊Chiamiov

7
おそらく、sys.path.append(path)をsys.path.insert(0、path)に置き換え、sys.path [-1]をsys.path [0]に置き換える必要があります。そうしないと、検索パスに同じ名前のモジュールがすでに存在する場合、関数は誤ったモジュールをインポートします。たとえば、現在のディレクトリに「some.py」がある場合、import_path( "/ imports / some.py")は間違ったファイルをインポートします。
Alex Che

同意する!他の相対インポートが優先される場合があります。sys.path.insertを使用
iElectric

from x import y(または*)の動作をどのように複製しますか?
レベスク

明確ではありません。OPの問題を解決するには、このスクリプトの完全な使用法を指定してください。
mrgloom 2018

21

nosklo's例付きの回答の説明

注:すべての__init__.pyファイルは空です。

main.py
app/ ->
    __init__.py
    package_a/ ->
       __init__.py
       fun_a.py
    package_b/ ->
       __init__.py
       fun_b.py

app / package_a / fun_a.py

def print_a():
    print 'This is a function in dir package_a'

app / package_b / fun_b.py

from app.package_a.fun_a import print_a
def print_b():
    print 'This is a function in dir package_b'
    print 'going to call a function in dir package_a'
    print '-'*30
    print_a()

main.py

from app.package_b import fun_b
fun_b.print_b()

実行する$ python main.pyと、次が返されます。

This is a function in dir package_b
going to call a function in dir package_a
------------------------------
This is a function in dir package_a
  • main.pyは: from app.package_b import fun_b
  • fun_b.pyは from app.package_a.fun_a import print_a

そのため、必要なのは、フォルダー内のファイルフォルダー内のpackage_bファイルpackage_aです。正しい??


12

これは残念ながらsys.pathハックですが、非常にうまく機能します。

別のレイヤーでこの問題が発生しました。指定された名前のモジュールがすでにありましたが、それは間違ったモジュールでした。

私がやりたかったことは次のとおりです(私が作業していたモジュールはmodule3でした):

mymodule\
   __init__.py
   mymodule1\
      __init__.py
      mymodule1_1
   mymodule2\
      __init__.py
      mymodule2_1


import mymodule.mymodule1.mymodule1_1  

私はすでにmymoduleをインストールしていますが、私のインストールには「mymodule1」がありません。

インストールされているモジュールからインポートしようとしたため、ImportErrorが発生しました。

sys.path.appendを実行しようとしましたが、うまくいきませんでした。うまくいったのはsys.path.insertでした

if __name__ == '__main__':
    sys.path.insert(0, '../..')

ちょっとハックですが、すべてうまくいきました!したがって、他のパスオーバーライドする決定をしたい場合は、sys.path.insert(0、pathname)を使用して機能させる必要があることに注意してください。これは私にとって非常にイライラする問題でした。多くの人がsys.pathに "append"関数を使用すると言っていますが、すでにモジュールが定義されている場合は機能しません(非常に奇妙な動作です)


sys.path.append('../')私にとっては作品の罰金(Pythonの3.5.2)
Nister

ハックを実行可能ファイルにローカライズし、パッケージに依存する可能性のある他のモジュールに影響を与えないため、これは問題ないと思います。
トムラッセル

10

これを自分の参照用にここに置いておきます。私はそれが良いPythonコードではないことを知っていますが、自分が取り組んでいるプロジェクトのスクリプトが必要であり、そのスクリプトをscriptsディレクトリに置きたかったのです。

import os.path
import sys
sys.path.append(os.path.abspath(os.path.join(os.path.dirname(__file__), "..")))

9

OPへのコメントで@EvgeniSergeevが言うように、次のコマンドを.py使用して、任意の場所にあるファイルからコードをインポートできます。

import imp

foo = imp.load_source('module.name', '/path/to/file.py')
foo.MyClass()

これはこのSOの回答から取られたものです。



2

Python docから、

Python 2.5では、from __future__ import absolute_importディレクティブを使用してインポートの動作を絶対インポートに切り替えることができます。この絶対インポートの動作は、将来のバージョン(おそらくPython 2.7)でデフォルトになる予定です。絶対インポートがデフォルトにimport stringなると、は常に標準ライブラリのバージョンを見つけます。ユーザーは絶対インポートをできるだけ使用することをお勧めします。そのためfrom pkg import string、コードの記述を開始することが推奨されます


1

"PYTHONPATH"環境変数を最上位のフォルダーに設定する方が簡単であることがわかりました。

bash$ export PYTHONPATH=/PATH/TO/APP

次に:

import sub1.func1
#...more import

もちろん、PYTHONPATHは「グローバル」ですが、まだ問題は発生していません。


これは基本的にvirtualenv、インポートステートメントを管理する方法です。
byxor 2017

1

ジョンBが言ったことに加えて、__package__変数を設定すると、__main__他のものを台無しにする可能性のあるものを変更するのではなく、役立つはずです。しかし、私がテストできる限り、完全に機能するわけではありません。

私は同じ問題を抱えており、PEP 328も366もどちらも問題を完全に解決しません。どちらも、結局のところ、sys.path私が理解できる限り、パッケージのヘッドをに含める必要があるからです。

また、これらの変数に入れる文字列をフォーマットする方法が見つからなかったことにも触れておきます。それは"package_head.subfolder.module_name"何ですか?


0

モジュールのパスを追加する必要がありますPYTHONPATH

export PYTHONPATH="${PYTHONPATH}:/path/to/your/module/"

1
sys.pathからsys.path初期化されるため、これは操作とほぼ同じですPYTHONPATH
Joril

@ジョリルそれは正しいですがsys.pathPYTHONPATHどちらが環境変数であり、エクスポートできるのかとは対照的に、ソースコードにハードコードする必要があります。
Giorgos Myrianthous
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.