312

兄弟のインポートやパッケージのドキュメントについての質問を読んでみ ましたが、まだ答えが見つかりません。

次の構造で:

├── LICENSE.md
├── README.md
├── api
│   ├── __init__.py
│   ├── api.py
│   └── api_key.py
├── examples
│   ├── __init__.py
│   ├── example_one.py
│   └── example_two.py
└── tests
│   ├── __init__.py
│   └── test_one.py

examplesおよびtestsディレクトリ 内のスクリプトをapiモジュールからインポートして、コマンドラインから実行するにはどうすればよい ですか?

sys.path.insertまた、すべてのファイルの醜いハッキングを避けたいと思います。確かにこれはPythonで実行できますよね?

4

17 に答える 17

300

sys.path のハッキングにうんざりしていませんか?

利用可能なハックはたくさんありますがsys.path.append、手元の問題を解決する別の方法を見つけました。

概要

  • コードを 1 つのフォルダーにラップします (例: packaged_stuff)
  • setuptools.setup()setup.pyを使用するスクリプトを作成します。(以下の最小限を参照)setup.py
  • 編集可能な状態でパッケージをピップインストールしますpip install -e <myproject_folder>
  • を使用してインポートfrom packaged_stuff.modulename import function_name

設定

開始点は、提供されたファイル構造であり、 というフォルダーにラップされていますmyproject

.
└── myproject
    ├── api
    │   ├── api_key.py
    │   ├── api.py
    │   └── __init__.py
    ├── examples
    │   ├── example_one.py
    │   ├── example_two.py
    │   └── __init__.py
    ├── LICENCE.md
    ├── README.md
    └── tests
        ├── __init__.py
        └── test_one.py

ルート フォルダーと呼びます。.この例では、 にありますC:\tmp\test_imports\

api.py

テストケースとして、次の ./api/api.py を使用してみましょう

def function_from_api():
    return 'I am the return value from api.api!'

test_one.py

from api.api import function_from_api

def test_function():
    print(function_from_api())

if __name__ == '__main__':
    test_function()

test_one を実行してみてください:

PS C:\tmp\test_imports> python .\myproject\tests\test_one.py
Traceback (most recent call last):
  File ".\myproject\tests\test_one.py", line 1, in <module>
    from api.api import function_from_api
ModuleNotFoundError: No module named 'api'

また、相対インポートを試みてもうまくいきません:

を使用from ..api.api import function_from_apiすると、

PS C:\tmp\test_imports> python .\myproject\tests\test_one.py
Traceback (most recent call last):
  File ".\tests\test_one.py", line 1, in <module>
    from ..api.api import function_from_api
ValueError: attempted relative import beyond top-level package

手順

  1. setup.py ファイルをルート レベル ディレクトリに作成します。

の内容は次のsetup.pyとおりです*

from setuptools import setup, find_packages

setup(name='myproject', version='1.0', packages=find_packages())
  1. 仮想環境を使用する

仮想環境に精通している場合は、仮想環境をアクティブにして、次の手順に進みます。仮想環境の使用は絶対に必要というわけではありませが、長期的には非常に役立ちます (複数のプロジェクトが進行中の場合..)。最も基本的な手順は次のとおりです (ルート フォルダーで実行)。

  • 仮想環境を作成する
    • python -m venv venv
  • 仮想環境を有効にする
    • source ./venv/bin/activate(Linux、macOS) または./venv/Scripts/activate(Win)

これについて詳しく知るには、「python virtual env tutorial」などをグーグルで検索してください。おそらく、作成、アクティブ化、および非アクティブ化以外のコマンドは必要ありません。

仮想環境を作成してアクティブ化すると、コンソールに仮想環境の名前が括弧内に表示されます。

PS C:\tmp\test_imports> python -m venv venv
PS C:\tmp\test_imports> .\venv\Scripts\activate
(venv) PS C:\tmp\test_imports>

フォルダ ツリーは次のようになります**

.
├── myproject
│   ├── api
│   │   ├── api_key.py
│   │   ├── api.py
│   │   └── __init__.py
│   ├── examples
│   │   ├── example_one.py
│   │   ├── example_two.py
│   │   └── __init__.py
│   ├── LICENCE.md
│   ├── README.md
│   └── tests
│       ├── __init__.py
│       └── test_one.py
├── setup.py
└── venv
    ├── Include
    ├── Lib
    ├── pyvenv.cfg
    └── Scripts [87 entries exceeds filelimit, not opening dir]
  1. プロジェクトを編集可能な状態で pip install

myprojectを使用して最上位パッケージをインストールしますpip。トリックは-e、インストールを行うときにフラグを使用することです。このようにして、編集可能な状態でインストールされ、.py ファイルに対して行われたすべての編集が、インストールされたパッケージに自動的に含まれます。

ルート ディレクトリで、次を実行します。

pip install -e .(ドットに注意してください。「現在のディレクトリ」を表します)

を使用してインストールされていることも確認できます。pip freeze

(venv) PS C:\tmp\test_imports> pip install -e .
Obtaining file:///C:/tmp/test_imports
Installing collected packages: myproject
  Running setup.py develop for myproject
Successfully installed myproject
(venv) PS C:\tmp\test_imports> pip freeze
myproject==1.0
  1. myproject.インポートに追加

myproject.他の方法では機能しないインポートにのみ追加する必要があることに注意してください。setup.py&なしで機能したインポートは、pip install引き続き正常に機能します。以下の例を参照してください。


ソリューションをテストする

api.pyそれでは、上で定義したものと下で定義したものを使用してソリューションをテストしましょうtest_one.py

test_one.py

from myproject.api.api import function_from_api

def test_function():
    print(function_from_api())

if __name__ == '__main__':
    test_function()

テストの実行

(venv) PS C:\tmp\test_imports> python .\myproject\tests\test_one.py
I am the return value from api.api!

*より詳細な setup.py の例については、 setuptools のドキュメントを参照してください。

** 実際には、仮想環境をハードディスクのどこにでも置くことができます。

于 2018-05-05T20:52:59.100 に答える
108

7年後

以下に回答を書いたので、変更sys.pathはまだプライベート スクリプトでうまく機能する簡単で汚いトリックですが、いくつかの改善点があります。

  • パッケージを (virtualenv 内にあるかどうかにかかわらず)インストールsetup.cfgすると、必要なものが得られますが、setuptools を直接使用する (およびメタデータを保存するために使用する)よりも、pip を使用することをお勧めします。
  • -mフラグを使用してパッケージとして実行することもできます (ただし、作業ディレクトリをインストール可能なパッケージに変換したい場合は少し面倒です)。
  • 具体的には、テストの場合、pytestはこの状況で api パッケージを見つけることができ、sys.pathハッキングを処理します。

したがって、それは本当にあなたが何をしたいかによって異なります。ただし、あなたの場合は、ある時点で適切なパッケージを作成することが目標のように見えるため、pip -eまだ完全ではありませんが、インストールを介してインストールするのがおそらく最善の策です。

古い答え

すでに他の場所で述べたように、ひどい真実は、兄弟モジュールまたはモジュールからの親パッケージからのインポートを可能にするために醜いハックをしなければならないということ__main__です。この問題はPEP 366で詳しく説明されています。PEP 3122はより合理的な方法でインポートを処理しようとしましたが、Guido はそれを拒否しました。

唯一の使用例は、たまたまモジュールのディレクトリ内に存在するスクリプトを実行しているようです。これは、私が常にアンチパターンとして見てきたものです。

(ここ)

ただし、私はこのパターンを定期的に使用しています

# Ugly hack to allow absolute import from the root folder
# whatever its name is. Please forgive the heresy.
if __name__ == "__main__" and __package__ is None:
    from sys import path
    from os.path import dirname as dir

    path.append(dir(path[0]))
    __package__ = "examples"

import api

これpath[0]は、実行中のスクリプトの親フォルダーとdir(path[0])最上位フォルダーです。

ただし、これで相対インポートを使用することはまだできませんが、最上位レベル (例apiの親フォルダー) からの絶対インポートは許可されます。

于 2011-06-24T09:48:53.887 に答える
49

testsフォルダー内の Python ファイルの先頭に挿入する別の方法を次に示します。

# Path hack.
import sys, os
sys.path.insert(0, os.path.abspath('..'))
于 2012-02-25T17:02:56.163 に答える
38

必要でない限りハッキングする必要はありませんし、ハッキングすべきsys.pathではありません。この場合はそうではありません。使用する:

import api.api_key # in tests, examples

プロジェクト ディレクトリから実行します: python -m tests.test_one.

おそらくtests内部に移動して(APIのユニットテストの場合)api実行python -m api.testして、すべてのテストを実行する(存在すると仮定__main__.py)か、代わりpython -m api.test.test_oneに実行する必要があります。test_one

__init__.pyから削除することもできますexamples(Python パッケージではありません)apiがインストールされている virtualenv でサンプルを実行することもできます。たとえば、pip install -e .virtualenvでは、api適切なsetup.py.

于 2014-05-08T13:12:48.147 に答える
10

兄弟/相対インポート ハックなしで無関係なプロジェクト間でコードを共有する意図された方法を確認するために必要な Pythonology の理解はまだありません。その日まで、これが私の解決策です。の場合examples、またはtestsからインポートする..\api場合は、次のようになります。

import sys.path
import os.path
# Import from sibling directory ..\api
sys.path.append(os.path.dirname(os.path.abspath(__file__)) + "/..")
import api.api
import api.api_key
于 2012-04-12T20:32:45.277 に答える
4

兄弟パッケージのインポートには、 [sys.path][2]モジュールの挿入または追加メソッドを使用できます。

if __name__ == '__main__' and if __package__ is None:
    import sys
    from os import path
    sys.path.append( path.dirname( path.dirname( path.abspath(__file__) ) ) )
    import api

これは、次のようにスクリプトを起動する場合に機能します。

python examples/example_one.py
python tests/test_one.py

一方、相対インポートも使用できます。

if __name__ == '__main__' and if __package__ is not None:
    import ..api.api

この場合、「-m」引数を使用してスクリプトを起動する必要があります (この場合、 「.py」拡張子を付けてはならないことに注意してください)。

python -m packageName.examples.example_one
python -m packageName.tests.test_one

もちろん、スクリプトがどのように呼び出されても機能するように、2 つのアプローチを混在させることができます。

if __name__ == '__main__':
    if __package__ is None:
        import sys
        from os import path
        sys.path.append( path.dirname( path.dirname( path.abspath(__file__) ) ) )
        import api
    else:
        import ..api.api
于 2015-01-10T17:09:24.173 に答える
1

関連するコードで import ステートメントがどのように記述されているかを確認する必要があります。examples/example_one.py次の import ステートメントを使用する場合:

import api.api

...次に、プロジェクトのルート ディレクトリがシステム パスにあると想定します。

ハックせずにこれをサポートする最も簡単な方法は、次のように最上位ディレクトリからサンプルを実行することです。

PYTHONPATH=$PYTHONPATH:. python examples/example_one.py 
于 2011-06-12T18:47:03.990 に答える
-3

まず、モジュール自体と同じ名前のファイルを持たないようにする必要があります。他のインポートを壊す可能性があります。

ファイルをインポートすると、インタープリターはまず現在のディレクトリをチェックし、次にグローバル ディレクトリを検索します。

内部examplesまたはtests電話することができます:

from ..api import api
于 2011-06-12T19:06:10.233 に答える