Python:サブパッケージまたはサブモジュールのインポート


90

すでにフラットパッケージを使用しているので、ネストされたパッケージで発生した問題は予想していませんでした。ここは…

ディレクトリレイアウト

dir
 |
 +-- test.py
 |
 +-- package
      |
      +-- __init__.py
      |
      +-- subpackage
           |
           +-- __init__.py
           |
           +-- module.py

内容のinitの.py

package/__init__.pypackage/subpackage/__init__.pyは両方とも空です。

の内容 module.py

# file `package/subpackage/module.py`
attribute1 = "value 1"
attribute2 = "value 2"
attribute3 = "value 3"
# and as many more as you want...

test.py(3バージョン)の内容

バージョン1

# file test.py
from package.subpackage.module import *
print attribute1 # OK

これは、物をインポートする(すべてをまとめてインポートする)悪い方法であり、安全ではありませんが、機能します。

バージョン2

# file test.py
import package.subpackage.module
from package.subpackage import module # Alternative
from module import attribute1

アイテムごとにインポートするより安全な方法ですが、失敗します。Pythonはこれを望んでいません:「モジュールという名前のモジュールはありません」というメッセージで失敗します。しかしながら …

# file test.py
import package.subpackage.module
from package.subpackage import module # Alternative
print module # Surprise here

…と言い<module 'package.subpackage.module' from '...'>ます。これはモジュールですが、モジュールではありません/ -P 8-O ...ええと

バージョン3

# file test.py v3
from package.subpackage.module import attribute1
print attribute1 # OK

これは動作します。それで、あなたは常にオーバーキルプレフィックスを使用することを余儀なくされるか、バージョン#1のように安全でない方法を使用し、Pythonによって安全で便利な方法を使用することを許可されていませんか?安全で不必要な長いプレフィックスを回避するより良い方法は、Pythonが拒否する唯一の方法ですか?これはimport *、それが長すぎるプレフィックスを愛しているためですか(これはこの慣行を強制するのに役立ちません)?

難しい言葉で申し訳ありませんが、それは私がこの愚かなような行動を回避しようとしている2日間です。どこかで完全に間違っていない限り、Pythonのパッケージとサブパッケージのモデルで何かが本当に壊れているような気がします。

ノート

  • 私はsys.path、グローバルな副作用を回避するために、または同じグローバルな効果で*.pth遊ぶための単なる別の方法であるファイルに依存したくありませんsys.path。ソリューションをクリーンにするには、ローカルのみである必要があります。どちらのPythonもサブパッケージを処理できますが、そうではありませんが、ローカルのものを処理できるようにするためにグローバル構成で遊ぶ必要はありません。
  • でimportsを使用しようとしましたがpackage/subpackage/__init__.py、何も解決せず、同じことを行い、subpackageモジュールであるprint subpackageと言っているのに、既知のモジュールではないと文句を言います(これも奇妙な動作です)。

私は完全に間違っているかもしれませんが(私が好むオプション)、これは私がPythonに非常に失望していると感じさせます。

私が試した3つ以外の既知の方法はありますか?私が知らないことは?

(はぁ)

-----%<-----編集----->%-----

これまでの結論(人々のコメントの後)

すべてのパッケージ参照はグローバルディクショナリにのみ送信されるため、Pythonの実際のサブパッケージのようなものはありません。つまり、ローカルディクショナリがないため、ローカルパッケージ参照を管理する方法がありません。

フルプレフィックスまたはショートプレフィックスまたはエイリアスを使用する必要があります。のように:

フルプレフィックスバージョン

from package.subpackage.module import attribute1
# An repeat it again an again
# But after that, you can simply:
use_of (attribute1)

短いプレフィックスバージョン(ただし、繰り返しプレフィックス)

from package.subpackage import module
# Short but then you have to do:
use_of (module.attribute1)
# and repeat the prefix at every use place

または、上記のバリエーション。

from package.subpackage import module as m
use_of (m.attribute1)
# `m` is a shorter prefix, but you could as well
# define a more meaningful name after the context

因数分解バージョン

複数のエンティティを一度にまとめてインポートすることを気にしない場合は、次のことができます。

from package.subpackage.module import attribute1, attribute2
# and etc.

私の最初の好みではありませんが(インポートされたエンティティごとに1つのインポートステートメントを使用することを好みます)、個人的に好むものかもしれません。

更新(2012-09-14):

最後に、レイアウトに関するコメントを除いて、実際には問題ないようです。上記の代わりに、私は使用しました:

from package.subpackage.module import (

    attribute1, 
    attribute2,
    attribute3,
    ...)  # and etc.

「from.importmodule」を「/package/subpackage/__init__.py」に書き込むとどうなりますか?
Markus Unterwaditzer 2012

あなたの「ファクタリングされたバージョン」はあなたがやりたいことにぴったりのようです。attribute1とattribute2に対して別々のインポート行を実行する場合(「好み」に応じて)、意図的に作業を増やすことになります。それをする理由はありません。
BrenBarn 2012

申し訳ありませんが、私はあなたが望むものを手に入れません。質問をより明確に言い換えることができますか?正確に何をしたいですか?つまり、機能しないものを何に書きたいのでしょうか。また、どのように機能すると思いますか。私が読んだことによると、インポートのセマンティクスがJavaやCのようなものであると思います。最後に、スターインポート__all__時にエクスポートする必要のある名前のリストを含む変数を追加して、モジュールを安全に「スターインポート」することができます。編集:さて、BrenBarnの回答を読んで私はあなたが何を意味するのか理解しました。
バクリウ2012

回答:


68

importモジュールの検索方法を誤解しているようです。importステートメントを使用すると、常に実際のモジュールパス(および/またはsys.modules検索されます。以前のインポートのために存在するローカル名前空間のモジュールオブジェクトを利用しません。あなたがするとき:

import package.subpackage.module
from package.subpackage import module
from module import attribute1

2行目は、というパッケージを探し、そのパッケージからpackage.subpackageインポートmoduleします。この行は3行目には影響しません。3行目は、呼び出されたモジュールを検索するだけで、モジュールmoduleが見つかりません。module上記の行から取得したと呼ばれるオブジェクトを「再利用」することはありません。

つまりfrom someModule import ...、「以前にインポートしたsomeModuleという名前のモジュールから...」という意味ではなく、「sys.pathにあるsomeModuleという名前のモジュールから...」という意味です。モジュールにつながるパッケージをインポートして、モジュールのパスを「段階的に」構築する方法はありません。インポートするときは、常にモジュール名全体を参照する必要があります。

何を達成しようとしているのかは明確ではありません。特定のオブジェクトattribute1のみをインポートする場合は、それを実行from package.subpackage.module import attribute1して実行します。必要なpackage.subpackage.module名前をインポートしたら、長い間心配する必要はありません。

後で他の名前にアクセスするためにモジュールにアクセスたい場合は、それを行うことができfrom package.subpackage import moduleます。これまで見てきたmodule.attribute1ように、好きなだけ行うことができます。

両方が必要な場合---つまり、attribute1直接アクセス可能であり、アクセス可能である場合moduleは、上記の両方を実行します。

from package.subpackage import module
from package.subpackage.module import attribute1
attribute1 # works
module.someOtherAttribute # also works

package.subpackage2回入力したくない場合は、attribute1へのローカル参照を手動で作成できます。

from package.subpackage import module
attribute1 = module.attribute1
attribute1 # works
module.someOtherAttribute #also works

あなたのコメントは、Ignacio Vazquez-Abramsからのコメントと同じ方向に進みます(私は彼のメッセージにコメントしました)。最後に、使用module.attribute1については私が考えていることですが、どこでもプレフィックスの必要性を回避する方法があると思います。そのため、どこでもプレフィックスを使用するか、名前を繰り返してローカルエイリアスを作成する必要があります。私が期待していたスタイルではありませんが、方法がない場合(結局のところ、名前の変更宣言と同様の何かを必要とするAdaに慣れています)。
hibou57 2012

@ Hibou57:「バージョン2」で何を達成しようとしているのかはまだはっきりしていません。それが不可能なことをしたいですか?パッケージ/モジュール/属性名のどの部分も再入力せずに、モジュールとその属性の両方をインポートしたいですか?
BrenBarn 2012

ローカルオブジェクト参照を作成するのと同じように、ローカルパッケージ参照を作成したかったのです。最終的に本当にローカルモジュール参照があるようですが、これらからインポートすることはできません。それはローカルとグローバルのミックスで面白い味がします(ローカルのものもあれば、グローバルでなければならないものもありますが、私はそれが好きではありませんが、それがどのように機能するかをよく理解していれば大丈夫です)。ちなみにメッセージありがとうございます。
hibou57 2012

1
それがどのように機能するかをまだ理解しているかどうかはわかりません。または、いずれにせよ、2012年に行ったこと。
hejazzman 2017

1
6か月の一時解雇後にPythonに戻るたびに、私はここに行き着きます。このページにアクセスするたびに賛成できれば!「モジュールにつながるパッケージをインポートして、モジュールのパスを「段階的に」構築する方法はありません」という文で巨大なポスターを作成します。
PatrickT

10

#2が失敗する理由sys.modules['module']は、存在せず(インポートルーチンには独自のスコープがあり、moduleローカル名を表示できない)、moduleディスク上にモジュールまたはパッケージがないためです。インポートされた複数の名前はコンマで区切ることができることに注意してください。

from package.subpackage.module import attribute1, attribute2, attribute3

また:

from package.subpackage import module
print module.attribute1

sys.modules['name']私が今まで知らなかったあなたの参照は、私が恐れていたものだと思いました(そしてBrenBarnは確認します):Pythonの実際のサブパッケージのようなものはありません。sys.modules、その名前が示すように、グローバルであり、モジュールへのすべての参照がこれに依存している場合、モジュールへのローカル参照のようなものはありません(Python 3.xに付属している可能性がありますか?)。
hibou57 2012

そこにある「参照」の使用はあいまいです。import#2の最初のものは、にpackage.subpackage.moduleバインドされるローカル参照を生成しmoduleます。
Ignacio Vazquez-Abrams 2012

はい、しかしそれは私がインポートできない「モジュール」です;-)
Hibou57 2012

0

グローバル名前空間でattribute1を取得するだけの場合は、バージョン3で問題ないようです。なぜそれは過剰なプレフィックスですか?

バージョン2では、代わりに

from module import attribute1

できるよ

attribute1 = module.attribute1

attribute1 = module.attribute1付加価値のない名前を繰り返すだけです。私はそれが機能することを知っていますが、私はこのスタイルが好きではありません(それは私があなたの返事が好きではないことを意味しません)。
hibou57 2012

2
私は、ここでコメントしているすべての人のように、あなたが何をしたいのか理解していないと思います。あなたが与えるすべての例では、名前空間のサブパッケージからのシンボルで終わりたいように見えます。動作しない例(例2)は、パッケージからサブモジュールをインポートし、次にそのサブモジュールからシンボルをインポートすることによってそれを実行したいと考えています。なぜあなたがそれを1つではなく2つのステップでやりたいのか分かりません。たぶん、あなたの理想的な解決策が何であるか、そしてその理由をもっと説明してください。
Thomas Vander Stichele 2012
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.