33

私はそのような「標準的なファイル構造」を持っています(読みやすくするために賢明な名前を付けています):

mainpack/

  __main__.py
  __init__.py 

  - helpers/
     __init__.py
     path.py

  - network/
     __init__.py
     clientlib.py
     server.py

  - gui/
     __init__.py
     mainwindow.py
     controllers.py

この構造では、たとえば、各パッケージに含まれるモジュールはhelpers、次のような相対インポートを介してユーティリティにアクセスすることができます。

# network/clientlib.py
from ..helpers.path import create_dir

__main__.pyプログラムは、次の方法でファイルを使用して「スクリプトとして」実行されます。

python mainpack/

私がこれらの行に入れたPEP 366に従おうとしています:__main__.py

___package___ = "mainpack"
from .network.clientlib import helloclient 

しかし、実行時:

$ python mainpack 
Traceback (most recent call last):
  File "/usr/lib/python2.6/runpy.py", line 122, in _run_module_as_main
    "__main__", fname, loader, pkg_name)
  File "/usr/lib/python2.6/runpy.py", line 34, in _run_code
    exec code in run_globals
  File "path/mainpack/__main__.py", line 2, in <module>
    from .network.clientlib import helloclient
SystemError: Parent module 'mainpack' not loaded, cannot perform relative import

どうしたの?相対インポートを処理して効果的に使用する正しい方法は何ですか?

現在のディレクトリを PYTHONPATH に追加しようとしましたが、何も変わりません。

4

4 に答える 4

45

PEP 366で与えられた「ボイラープレート」は不完全なようです。変数を設定し__package__ますが、実際にはパッケージをインポートしません。これは、相対インポートを機能させるためにも必要です。 extraneonのソリューションは正しい軌道に乗っています。

モジュールを含むディレクトリを単に に配置するだけでは十分ではないことに注意してくださいsys.path。対応するパッケージを明示的にインポートする必要があります。以下は、どのように呼び出されても (通常の 、または with 、または withを介して任意の場所から) python モジュールを確実に実行できるようにするために、 PEP 366で指定されたものよりも優れたボイラープレートのようです。importpython -mpython

# boilerplate to allow running as script directly
if __name__ == "__main__" and __package__ is None:
    import sys, os
    # The following assumes the script is in the top level of the package
    # directory.  We use dirname() to help get the parent directory to add to
    # sys.path, so that we can import the current package.  This is necessary 
    # since when invoked directly, the 'current' package is not automatically
    # imported.
    parent_dir = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))
    sys.path.insert(1, parent_dir)
    import mypackage
    __package__ = str("mypackage")
    del sys, os

# now you can use relative imports here that will work regardless of how this
# python file was accessed (either through 'import', through 'python -m', or 
# directly.

スクリプトがパッケージ ディレクトリのトップ レベルになく、トップ レベルの下にモジュールをインポートする必要がある場合は、トップ レベルを含むディレクトリos.path.dirnameになるまで を繰り返す必要parent_dirがあります。

于 2011-07-11T19:03:41.370 に答える
7

ここでのextraneonとtaherhの回答に触発されたのは、ファイルがなくなるまでファイルツリーを実行し__init__.pyて完全なパッケージ名を構築するコードです。これは間違いなくハックですが、ディレクトリ ツリー内のファイルの深さに関係なく機能するようです。絶対輸入が大いに奨励されているようです。

import os, sys
if __name__ == "__main__" and __package__ is None:
    d,f = os.path.split(os.path.abspath(__file__))
    f = os.path.splitext(f)[0]
    __package__ = [f] #__package__ will be a reversed list of package name parts
    while os.path.exists(os.path.join(d,'__init__.py')): #go up until we run out of __init__.py files
        d,name = os.path.split(d) #pull of a lowest level directory name 
        __package__.append(name)  #add it to the package parts list
    __package__ = ".".join(reversed(__package__)) #create the full package name
    mod = __import__(__package__) #this assumes the top level package is in your $PYTHONPATH
    sys.modules[__package__] = mod  #add to modules 
于 2013-03-20T18:51:10.363 に答える
7

読み込みコードは次のようになります

    try:
        return sys.modules[pkgname]
    except KeyError:
        if level < 1:
            warn("Parent module '%s' not found while handling "
                 "absolute import" % pkgname, RuntimeWarning, 1)
            return None
        else:
            raise SystemError, ("Parent module '%s' not loaded, cannot "
                                "perform relative import" % pkgname)

これにより、モジュールがsys.pathにない可能性があると思います。Python を (通常どおり) 起動し、プロンプトで「import mainpack」と入力すると、どうなりますか? それを見つけることができるはずです。

私はそれを自分で試しましたが、同じエラーが発生しました。少し読んだ後、次の解決策を見つけました。

# foo/__main__.py
import sys
mod = __import__('foo')
sys.modules["foo"]=mod

__package__='foo'
from .bar import hello

hello()

私には少しハックに思えますが、うまくいきます。トリックはfoo、インポートが相対的になるようにパッケージがロードされていることを確認しているようです。

于 2010-05-31T14:03:16.587 に答える
0

これは、他のほとんどの回答に基づく最小限のセットアップであり、そのようなパッケージ レイアウトで python 2.7 でテストされています。runme.pyまた、どこからでもスクリプトを呼び出すことができ、正しいことをしているように見えるという利点もあります-より複雑なセットアップでまだテストしていないため、emptorに注意してください...など.

これは基本的に、他の人が説明した sys.path への挿入を伴う上記のブラッドの答えです。

packagetest/
  __init__.py       # Empty
  mylib/
    __init__.py     # Empty
    utils.py        # def times2(x): return x*2
  scripts/
    __init__.py     # Empty
    runme.py        # See below (executable)

runme.py次のようになります。

#!/usr/bin/env python
if __name__ == '__main__' and __package__ is None:
    from os import sys, path
    d = path.dirname(path.abspath(__file__))
    __package__ = []
    while path.exists(path.join(d, '__init__.py')):
        d, name = path.split(d)
        __package__.append(name)
    __package__ = ".".join(reversed(__package__))
    sys.path.insert(1, d)
    mod = __import__(__package__)
    sys.modules[__package__] = mod

from ..mylib.utils import times2

print times2(4)
于 2015-12-08T11:52:30.373 に答える