何度もハマるので頭に刻み込む様に調べて習得するよ
前提
検証環境
- 実行は全て
tree
コマンドを実行したパスと同パスでREPLを起動して行っている - Pythonは2.7.5
パッケージとは
- Pythonでは
__init__.py
を含むディレクトリをパッケージと言う - ただのディレクトリでは再帰的にモジュールを検索しないため、基本的には
__init__.py
が必要
読み込み時に検索する範囲
- 実行ディレクトリと同ディレクトリ
- カレントディレクトリ
- 環境変数「PYTHONPATH」に列挙したディレクトリ
- sys.pathに含むディレクトリ
sys.path
は絶対パスの文字列リストであり、import sys; print sys.path
等の方法で確認できる
実例
同ディレクトリのメソッドをimportする
$ tree --charset=C
.
`-- greeting.py
def daytime():
print 'hey'
>>> import greeting
>>> greeting.daytime() # 呼ぶ
hey
importしたものを理解する
>>> 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を使って書く
>>> from greeting import daytime
>>> daytime() # 呼ぶ
hey
from-importしたものを理解する
from
を使ってimport
したものも理解しよう
>>> 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
>>> import greeting
>>> print type(greeting.daytime)
<type 'function'>
>>> from greeting import daytime
>>> print type(daytime)
<type 'function'>
どちらもfunction
オブジェクトだから、括弧を付ければ呼べるというわけだ
>>> import greeting
>>> greeting.daytime()
hey
>>> from greeting import daytime
>>> daytime()
hey
同ディレクトリにあるパッケージのメソッドをインポートする
module
オブジェクトとfunction
オブジェクトを知ったところで、別パッケージのimportを試してみる
$ tree --charset=C
.
`-- subdir
`-- greeting.py
>>> 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
もう一度
>>> import subdir
>>> print dir(subdir) # が、greeting属性がない
['__builtins__', ..., '__path__']
どうやらパッケージをインポートしても、内包するファイルがすべて勝手に使える訳ではない様だ
greeting
をインポートしたい場合は、 subdirのgreeting をインポートする必要がある
>>> import subdir.greeting
>>> subdir.greeting.daytime()
hey
importしたものを理解する
少し前置きが長かったけど、要はやったのはimport subdir.greeting
だけだ
今度はこれを理解したいと思う
>>> 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 greeting
をfrom greeting import daytime
と書けたことを考えると、
fromにはmodule
オブジェクトを書くと考えれば良さそう
なので以下の書き方ができる
>>> from subdir import greeting # from部分にmoduleを書く
>>> greeting.daytime() # こう呼べる
hey
また、fromにはmodule
を階層構造に則って連結して書くことが出来る
>>> from subdir.greeting import daytime # from部分にmoduleを連結して書く
>>> daytime() # こう呼べる
hey
from-importしたものを理解する
もうなんとなくわかる気もするけど、この例も理解したいと思う
>>> 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'>
>>> 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例は全て同じものを指している
>>> 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
class Person():
def daytime(self):
print 'hey'
>>> import person
>>> person.Person().daytime()
hey
importしたものを理解する
ここまでが理解できていれば簡単だった
>>> 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を使って書く
>>> from person import Person
>>> Person().daytime()
hey
ファイル名とクラス名が同じ場合は多いと思う
from xxx Import Xxx
というインポート文が頻出するのはこういうことだったんだ
from-importしたものを理解する
もうわかった気がするけど、一応
>>> from person import Person
>>> print type(Person) # やはりclassobjオブジェクトで
<type 'classobj'>
>>> print type(Person.daytime) # 持っているdaytime属性がinstancemethodだから
<type 'instancemethod'>
>>> Person().daytime() # こう呼べる
hey
整理
クラスの場合もメソッドの場合が理解できていれば大差ない様だ
function
ではなくclassobj
を扱えば良いだけみたいだ
まとめ
importするものが何で、使うものが何か
メソッド
>>> import greeting # ファイル名をimportする
>>> print type(greeting) # importしたものはmodule
<type 'module'>
>>> print type(greeting.daytime) # 実際に使えるメソッドはfunction
<type 'function'>
クラス
>>> import person # ファイル名をimportする
>>> print type(person) # importしたものはmodule
<type 'module'>
>>> print type(person.Person) # 実際に使えるクラスはclassobj
<type 'classobj'>
できること、できないこと
fromを使わない場合はimportに書けるのはmoduleのみ
>>> 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
であること - 使う時は
function
かclassobj
であること
fromを使う場合はimportにはなんでも書ける
>>> from greeting import daytime # functionをインポート
>>> from person import Person # classobjをインポート
>>> from subdir import greeting # moduleをインポート
>>> from subdir.greeting import daytime # functionをインポート
ただし、fromを使う場合はimportに.を使えない
>>> 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という同名変数があってもぶつからない
- いろいろなモジュールをインポートした場合に関係がわかりやすい
>>> import math
>>> print math.sin(0)
0.0
>>> print math.cos(0)
1.0
fromありのメリット
- 短い
- モジュールの名前やパッケージ構成が変わった場合の修正がfromだけで良い
>>> from math import sin,cos
>>> print sin(0)
0.0
>>> print cos(0)
1.0
fromによるデメリット
>>> from os.path import join
>>> from random import shuffle
>>> join('root', 'sub') # joinってなんのモジュールのメソッド?リスト系?パス系?
>>> shuffle(range(100))
>>> abspath('current') # abspathはimportに追記しないと使えない
from xxx import *
def morning():
print 'hello'
def daytime():
print 'hey'
def evening():
print 'good night'
以下の様にやると全てのfunction
を読み込むことが出来る
>>> from greeting import *
>>> print dir()
['__builtins__', ..., 'daytime', 'evening', 'morning']
が、モジュール側の改修により、いつの間にかhelloもインポートする様になってしまっていて、
ローカル変数のhelloが上書きされたことにより、改修していないのにバグった...
なんてことにならない様に、from xxx import *
は避けた方が良いと思う
ちなみに、fromを用いない場合は出来ない
import出来るのはmodule
だけだと理解していれば間違えないと思う
>>> 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 --'
def commons():
print 'commons'
from .. import commons
def listutils():
commons.commons()
print 'list utils'
>>> import lib.util.listutils
>>> lib.util.listutils.listutils()
commons
list utils
from
に..
と書くことで、そのモジュールから見た上への相対パスを書くことが出来る
ただし、実行したパスより上を指定することは出来ない 次の例で確かめる
実行パスより上になる場合
$ tree --charset=C ..
..
|-- __init__.py
|-- lib.py <---.
`-- main ----' # REPLはここで起動する
def lib():
print 'lib'
>>> 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
を追加すれば良い
>>> 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とか使うとどうなるのかは知らんす
けど、いつか試してみたい