Pythonのimportについてまとめる

  • 144
    いいね
  • 1
    コメント

何度もハマるので頭に刻み込む様に調べて習得するよ

前提

検証環境

  • 実行は全てtreeコマンドを実行したパスと同パスでREPLを起動して行っている
  • Pythonは2.7.5

パッケージとは

  • Pythonでは__init__.pyを含むディレクトリをパッケージと言う
  • ただのディレクトリでは再帰的にモジュールを検索しないため、基本的には__init__.pyが必要

読み込み時に検索する範囲

  1. 実行ディレクトリと同ディレクトリ
  2. カレントディレクトリ
  3. 環境変数「PYTHONPATH」に列挙したディレクトリ
  4. sys.pathに含むディレクトリ

sys.pathは絶対パスの文字列リストであり、import sys; print sys.path 等の方法で確認できる

実例

同ディレクトリのメソッドをimportする

$ tree --charset=C
.
`-- greeting.py
greeting.py
def daytime():
    print 'hey'
REPL
>>> import greeting

>>> greeting.daytime() # 呼ぶ
hey

importしたものを理解する

REPL
>>> import greeting

>>> print type(greeting)         # importしたgreetingはmoduleというオブジェクトらしい
<type 'module'>

>>> print dir(greeting)          # daytime属性を持っているのがわかる(一部略)
['__builtins__', ..., 'daytime']

>>> greeting.daytime()           # 呼ぶ時はこう
hey

>>> print type(greeting.daytime) # 呼んだものはfunctionというオブジェクトらしい
<type 'function'>

fromを使って書く

REPL
>>> from greeting import daytime

>>> daytime()                    # 呼ぶ
hey

from-importしたものを理解する

fromを使ってimportしたものも理解しよう

REPL
>>> from greeting import daytime

>>> print type(daytime)          # こうしてimportしたdaytimeはfunctionというオブジェクトらしい
<type 'function'>

>>> print dir(daytime)           # 特に自分で定義した属性は無い(一部略)
['__call__', ..., 'func_name']

>>> daytime()                    # 呼ぶ時はこう
hey

>>> print type(daytime)          # 呼んだものとimportしたものは同じ
<type 'function'>

整理

この2つの例を見ると、以下の2つは同じものを指していることが理解できる

  • fromを用いていない方の例に出てきたgreetingが持っているdaytime属性
  • fromを用いた方の例のdaytime
REPL
>>> import greeting

>>> print type(greeting.daytime)
<type 'function'>


>>> from greeting import daytime

>>> print type(daytime)
<type 'function'>

どちらもfunctionオブジェクトだから、括弧を付ければ呼べるというわけだ

REPL
>>> import greeting

>>> greeting.daytime()
hey


>>> from greeting import daytime

>>> daytime()
hey

同ディレクトリにあるパッケージのメソッドをインポートする

moduleオブジェクトとfunctionオブジェクトを知ったところで、別パッケージのimportを試してみる

$ tree --charset=C
.
`-- subdir
    `-- greeting.py
REPL
>>> import subdir
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
ImportError: No module named subdir

冒頭で述べた通り、__init__.pyを置かないとsubdirはパッケージとして認識されない

$ touch subdir/__init__.py

もう一度

REPL
>>> import subdir

>>> print dir(subdir)             # が、greeting属性がない
['__builtins__', ..., '__path__']

どうやらパッケージをインポートしても、内包するファイルがすべて勝手に使える訳ではない様だ
greetingをインポートしたい場合は、 subdirのgreeting をインポートする必要がある

REPL
>>> import subdir.greeting

>>> subdir.greeting.daytime()
hey

importしたものを理解する

少し前置きが長かったけど、要はやったのはimport subdir.greetingだけだ
今度はこれを理解したいと思う

REPL
>>> import subdir.greeting

>>> print type(subdir.greeting)         # やはりimportしたものはmoduleオブジェクト
<type 'module'>

>>> print dir(subdir.greeting)          # daytime属性がある
['__builtins__', ..., 'daytime']

>>> subdir.greeting.daytime()           # だから、こう呼べる
hey

>>> print type(subdir.greeting.daytime) # そして、やはり呼んだものはfunctionオブジェクトだ
<type 'function'>

fromを使って書く

先ほどのimport greetingfrom greeting import daytimeと書けたことを考えると、
fromにはmoduleオブジェクトを書くと考えれば良さそう
なので以下の書き方ができる

REPL
>>> from subdir import greeting  # from部分にmoduleを書く

>>> greeting.daytime()           # こう呼べる
hey

また、fromにはmoduleを階層構造に則って連結して書くことが出来る

REPL
>>> from subdir.greeting import daytime  # from部分にmoduleを連結して書く

>>> daytime()                            # こう呼べる
hey

from-importしたものを理解する

もうなんとなくわかる気もするけど、この例も理解したいと思う

REPL
>>> from subdir import greeting

>>> print type(greeting)          # importしたものはmoduleオブジェクト
<type 'module'>

>>> print dir(greeting)           # daytime属性を持っているので
['__builtins__', ..., 'daytime']

>>> greeting.daytime()            # こう呼べる
hey

>>> print type(greeting.daytime)  # やはり呼んだものはfunctionオブジェクトだ
<type 'function'>
REPL
>>> from subdir.greeting import daytime

>>> print type(daytime)                  # こちらはimportしたものはfunctionオブジェクト
<type 'function'>

>>> daytime()                            # こう呼べる
hey

>>> print type(daytime)                  # 呼んだものとimportしたものは同じ
<type 'function'>

整理

いままで5つくらいの例を試したけど、実行したオブジェクトは全てfunctionのオブジェクトだ
importしたものがmoduleであれば、持っているfunction属性を
importしたものがfunctionであれば、そのfunctionオブジェクトを呼び出すことが出来る

だから、下の3例は全て同じものを指している

REPL
>>> import subdir.greeting
>>> print type(subdir.greeting.daytime)
<type 'function'>

>>> from subdir import greeting
>>> print type(greeting.daytime)
<type 'function'>

>>> from subdir.greeting import daytime
>>> print type(daytime)
<type 'function'>

同ディレクトリのクラスをimportする

いままでの例はメソッドだったが、クラスだとどうなるかも理解したい

$ tree --charset=C
.
`-- person.py
person.py
class Person():
    def daytime(self):
        print 'hey'
REPL
>>> import person

>>> person.Person().daytime()
hey

importしたものを理解する

ここまでが理解できていれば簡単だった

REPL
>>> import person

>>> print type(person)                 # おなじみのmoduleオブジェクト
<type 'module'>

>>> print dir(person)                  # Person属性がある
['Person', ..., '__package__']

>>> print type(person.Person)          # メソッドと違いclassobjというオブジェクトになるらしい
<type 'classobj'>

>>> print dir(person.Person)           # daytime属性がある
['__doc__', ..., 'daytime']

>>> person.Person().daytime()          # クラスなのでインスタンス生成をしてから呼び出す
hey

>>> print type(person.Person.daytime)  # functionとは違いinstancemethodと言うらしい(staticmethodとかはクラスの話になるので省略)
<type 'instancemethod'>

fromを使って書く

REPL
>>> from person import Person

>>> Person().daytime()
hey

ファイル名とクラス名が同じ場合は多いと思う
from xxx Import Xxxというインポート文が頻出するのはこういうことだったんだ

from-importしたものを理解する

もうわかった気がするけど、一応

REPL
>>> from person import Person

>>> print type(Person)          # やはりclassobjオブジェクトで
<type 'classobj'>

>>> print type(Person.daytime)  # 持っているdaytime属性がinstancemethodだから
<type 'instancemethod'>

>>> Person().daytime()          # こう呼べる
hey

整理

クラスの場合もメソッドの場合が理解できていれば大差ない様だ
functionではなくclassobjを扱えば良いだけみたいだ

まとめ

importするものが何で、使うものが何か

メソッド

REPL
>>> import greeting              # ファイル名をimportする

>>> print type(greeting)         # importしたものはmodule
<type 'module'>

>>> print type(greeting.daytime) # 実際に使えるメソッドはfunction
<type 'function'>

クラス

REPL
>>> import person                # ファイル名をimportする

>>> print type(person)           # importしたものはmodule
<type 'module'>

>>> print type(person.Person)    # 実際に使えるクラスはclassobj
<type 'classobj'>

できること、できないこと

fromを使わない場合はimportに書けるのはmoduleのみ

REPL
>>> import greeting.daytime
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
ImportError: No module named daytime    # daytimeはmoduleではなくfunctionのため、確かにNo module

>>> import person.Person
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
ImportError: No module named Person     # Personもmoduleではなくclassobjのため、確かにNo module

fromなしimportの要点は2つ!

  • import対象がmoduleであること
  • 使う時はfunctionclassobjであること

fromを使う場合はimportにはなんでも書ける

REPL
>>> from greeting import daytime         # functionをインポート

>>> from person import Person            # classobjをインポート

>>> from subdir import greeting          # moduleをインポート

>>> from subdir.greeting import daytime  # functionをインポート

ただし、fromを使う場合はimportに.を使えない

REPL
>>> import subdir.greeting               # importだけなら.が使えるが

>>> from subdir import greeting.daytime  # fromがある場合はimportに.を使えない
  File "<stdin>", line 1
    from subdir import greeting.daytime

おまけ

ここから下は検証不足だったりPythonらしさ不足だったりする可能性が高いです

fromを使うか使わないか

pythonはとてもよく書くのだけど、独学だし仕事で使ったことが無いので慣例の様なものがわからない
判断材料としては以下の様なものがあるのかな?
少なくともプロジェクト単位では統一するべきだと思うけど、どうすべきなのかはいまいちわからない...

fromなしのメリット

  • いちいちimport文に追記をしなくてもmath.tan等も使える
  • 万が一sinやcosという同名変数があってもぶつからない
  • いろいろなモジュールをインポートした場合に関係がわかりやすい
REPL
>>> import math

>>> print math.sin(0)
0.0

>>> print math.cos(0)
1.0

fromありのメリット

  • 短い
  • モジュールの名前やパッケージ構成が変わった場合の修正がfromだけで良い
REPL
>>> from math import sin,cos

>>> print sin(0)
0.0

>>> print cos(0)
1.0

fromによるデメリット

REPL
>>> from os.path import join
>>> from random import shuffle

>>> join('root', 'sub')  # joinってなんのモジュールのメソッド?リスト系?パス系?
>>> shuffle(range(100))
>>> abspath('current')   # abspathはimportに追記しないと使えない

from xxx import *

greeting.py
def morning():
    print 'hello'

def daytime():
    print 'hey'

def evening():
    print 'good night'

以下の様にやると全てのfunctionを読み込むことが出来る

REPL
>>> from greeting import *

>>> print dir()
['__builtins__', ..., 'daytime', 'evening', 'morning']

が、モジュール側の改修により、いつの間にかhelloもインポートする様になってしまっていて、
ローカル変数のhelloが上書きされたことにより、改修していないのにバグった...
なんてことにならない様に、from xxx import *は避けた方が良いと思う

ちなみに、fromを用いない場合は出来ない
import出来るのはmoduleだけだと理解していれば間違えないと思う

REPL
>>> import daytimeing.*
  File "<stdin>", line 1
    import daytimeing.*
                    ^
SyntaxError: invalid syntax

上にあるモジュールをインポートする

実行パスよりは上にならない場合

$ tree --charset=C         # REPLはここで起動する
.
`-- lib
    |-- __init__.py
    |-- commons.py         <-.
    `-- util                 |
        |-- __init__.py      |
        `-- listutils.py   --'
lib/commons.py
def commons():
    print 'commons'
lib/util/listutils.py
from .. import commons

def listutils():
    commons.commons()
    print 'list utils'
REPL
>>> import lib.util.listutils

>>> lib.util.listutils.listutils()
commons
list utils

from..と書くことで、そのモジュールから見た上への相対パスを書くことが出来る
ただし、実行したパスより上を指定することは出来ない 次の例で確かめる

実行パスより上になる場合

$ tree --charset=C ..
..
|-- __init__.py
|-- lib.py             <---.
`-- main               ----'  # REPLはここで起動する 
../lib.py
def lib():
    print 'lib'
REPL
>>> from .. import lib
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
ValueError: Attempted relative import in non-package

実行パスより上になる場合はfrom .. import xxxは出来ない様だ
厳密に言うと、sys.pathより上はだめってことだと思う
だから、どうしてもインポートしたければsys.pathを追加すれば良い

REPL
>>> import sys

>>> sys.path.append('..')                             # sys.pathを追加する

>>> from .. import lib                                # from ..が出来る様になる訳ではない
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
ValueError: Attempted relative import in non-package

>>> import lib                                        # 出来る様になるのはimport lib

>>> lib.lib()
lib

sys.path(とか)以下のパッケージを検索対象とする
ということが理解できていれば大丈夫だと思う

ただ、sys.pathを追加して無理矢理ほかのモジュールをインポートしたりすると
インポートされる側が把握しきれなくなったりするし、
なによりパッケージ構成としておかしいと思うので個人的にはやるべきではないと強く思う

おしまい

なんかすっごい行数になってしまった!
何日も隙を見つけてちまちま書いていたので、全体的な統一感が無い気もするけど、そんなことは良い

type()dir()を確認しながら理解するというのはとても良かったと思う
もうimportではまったりはしないはず...!

PyCharmとか使うとどうなるのかは知らんす
けど、いつか試してみたい