Python の import 文ってなに?

新しい機能を
使えるようにしてくれます。

例えば平方根の計算 sqrt は、最初から使えることはできません。 そのため math という機能を import して使えるようにします。

import math
print(math.sqrt(2))
>>> print(math.sqrt(2))
1.4142135623730951
>>> 

1. import は難しい

たかがこれだけのことなのですが、エラーに直面したとき、 意外と import が複雑なことに気づきます。 難しい原因を以下に列挙します。

1.1. パス

どこから import されているのか、わからない。

1.2. スクリプトとモジュール, パッケージ

python sample.py としたときと import sample としたときの挙動の違いが、わからない。

1.3. 絶対 import と 相対 import

相対 import はパッケージの中だけでしか使えないことことが、わからない。

1.4. この記事の目的

import に関するエラーを解消できるようになることです。

Python の import は一見簡単そうで、 エラーに直面すると結構難しかったりします。

import を実行された時に投げられる可能性のある例外は、 以下の4つです。

  1. ModuleNotFoundError
  2. AttributeError
  3. ImportError
  4. ValuError

いきなり見ても、ちょっと難しいかと思うのですが、 お急ぎの方向けに、例外ごとに対応する方法をまとめたものを ページの最後 こちら にご用意いたしました。

1.5. 誰のために書かれたの?

おそらく、このページにこられた方のほとんどが import から例外を投げられて、その対応を調べるために こられた方だと思います。

できれば、例外の対応だけを先に示す、という書き方がしたかったのですが。 結論だけ先に述べて最短で問題を解決するという書き方が、できませんでした。

なぜなら import は「パス」に関する挙動が、すこし複雑だからです。 煩雑ではありますが、どうしても手を動かさないと、 理解しにくいところがあるかなと感じています。

そのため、この記事は、すこしずつ手を動かして import の挙動を理解するような構成になっています。

2. なんで import するの?

答え: 全部 import すると重いから

import って書くのは面倒です。 それに import ってファイルの頭に書いてあるとコードが汚く見えます。

なんで import とわざわざ書かないといけないかというと 重くなるからだと 思っています

なぜ重くなるかというと import したときに Python のスクリプトが実行されているからです。

例えば、次のような sample.py ファイルを作ると import することができます。

# sample.py
print('Hello, world!')
$ python
>>>
>>> import sample
Hello, world!
>>>

ちなみに2回 import しても実行はされません。

$ python
>>>
>>> import sample
Hello, world!
>>>
>>> import sample
>>> # なにも起こらない

これらの実行文は、インポート文の中で 最初に モジュール名が見つかったときにだけ実行されます。
6. モジュール (module) - Python チュートリアル

3. import の仕組み

import すると、どのような処理が走っているのでしょうか? それは次のようなものです。

  1. パスを検索する。
  2. スクリプトを実行する。
  3. グローバル変数を束縛する。

ちょっと複雑ですが、2 > 3 > 1 の順番で説明します。

Step 2. スクリプトを実行する。

実は  import すると Python のスクリプトが、裏でひっそりと実行さています。  逆に言えば自分で作った Python のスクリプトは import することができます。

さっそく module.py を作り...

# module.py
variable = 1
print(variable)

これを import してみました。 print(variable) が実行されて 1 が表示されています。

import module
$ python
>>> import module
1  <-------------- 1 が表示されました。
>>> 

Step 3. グローバル変数を代入する。

 「変数」、「関数」、「クラス」が「モジュールオブジェクト」の属性に代入されます。 

import module
module
module.variable

>>> import module
>>> module  <------------ これはモジュールオブジェクト
<module 'module' from ... >
>>> module.variable <---- module.py のグローバル変数
1
>>> 

より正確には「グローバル変数」が代入されます。 「グローバル変数」については、以下の記事で説明させていただきました。 読んでいただかなくても、この先に進めます。

Step 1. パスを検索する。

やっと、最初のステップに戻ってきました。 標準ライブラリにオブジェクトをコピーしてくれる copy があります。

import copy

class User:
    def __init__(self, name):
        self.name = name

user_a = User('サーバルちゃん')
user_b = user_a
user_c = copy.copy(user_a)

user_b.name = 'かばんちゃん'
user_c.name = 'ラッキービースト'

user_a.name
user_b.name
user_c.name
>>> user_a.name
'かばんちゃん'
>>> user_b.name
'かばんちゃん'
>>> user_c.name
'ラッキービースト'
>>> 

user_b.name に代入したら user_a.name も変わってしまいました。 コピーと代入は違います。

そのことについては、以下の記事で説明させていただきました。 特に、読んでいただかなくても、この先に進めます。

import すると Python のコードが実行されます。 ということは copy.py がどこかにあるはずです。 では copy.py は、どこにあるのでしょうか?

import は、一体、どこを探しているのでしょうか? このあと、どこから検索されるのかを、ご説明いたします。

◯ まとめ

「検索」して「実行」して「代入」する。 これが import の大体の流れになります。

import 文は 2 つの処理を連続して行っています; ある名前のモジュールを探し、その検索結果をローカルスコープの名前に束縛します。
5. インポートシステム - Python 言語リファレンス

4. どこから import されるの?

copy.py はどこにあるのでしょうか? その答えは sys.path に保存されています。

sys.path
モジュールを検索するパスを示す文字列のリスト。

対話モード >>> に、 以下のコードをコピペして実行して見てください。

import sys
print(*sys.path, sep='\n')
>>> import sys
>>> print(*sys.path, sep='\n')

/usr/local/Cellar/python/3.7.1/Frameworks/Python.framework/Versions/3.7/lib/python37.zip
/usr/local/Cellar/python/3.7.1/Frameworks/Python.framework/Versions/3.7/lib/python3.7
/usr/local/Cellar/python/3.7.1/Frameworks/Python.framework/Versions/3.7/lib/python3.7/lib-dynload
/usr/local/lib/python3.7/site-packages
>>> 

インタプリタが命令を端末 (tty) やコマンドプロンプトから読み取っている場合、 インタプリタは 対話モード (interactive mode) で動作しているといいます。
2.1.2. 対話モード - Python チュートリアル

以下の4つは、 sys.path で表示されたもののどれかに必ず該当します。

  1. スクリプトのあるパスまたはカレントディレクトリ
  2. 標準ライブラリのコード(Python で書かれた)
  3. 標準ライブラリのコード(C 言語で書かれた)
  4. pip install したコード

こういう意味不明な長い文字列を見せられると辟易とします。 さらに問題なのは、お使いのパソコンによって、表示されるパスは異なるということです。

そのため、実際に手を動かしていただきながら、 どれに該当するのかを調べていただくと理解が速いかなと感じております。

何か皆様の手間を取らせない良い逃げ方ができないか、 いくらか考えてみたのですが、パッと思いつきませんでした...

以下、個々のものについてご説明させていただきます。 それでは、実際に sys.path を表示させて、 覗いていきたいと思います。

4.1. スクリプトのあるパスまたはカレントディレクトリ

1つ目のパスを特定する時から、早速難題です。

実は python sample.py と実行した時と、 import sample と import した時で、 sys.path に追加されるパスが異なるのです。

それぞれの場合について見ていきます。

4.1.1. 情報工学実験 第一a 対話モードから実行してみる。

4.1.1.1. 実行

以下のコードを対話モードにコピペして実行して見てください。

# ~/package/module.py
import sys
print(*sys.path, sep='\n')


4.1.1.2. 確認

空っぽの行があります。これはなんでしょうか?

$ python
>>> import sys
>>> print(*sys.path, sep='\n')
        <--------- 空っぽ
/usr/local/Cellar/python/3.7.1/Frameworks/Python.framework/Versions/3.7/lib/python37.zip
/usr/local/Cellar/python/3.7.1/Frameworks/Python.framework/Versions/3.7/lib/python3.7
/usr/local/Cellar/python/3.7.1/Frameworks/Python.framework/Versions/3.7/lib/python3.7/lib-dynload
/usr/local/lib/python3.7/site-packages
$

空文字はカレントディレクトリという意味らしいです。 カレントディレクトリとは、python コマンドを押下した時のディレクトリです。

sys.path
スクリプトのディレクトリがない (インタプリタが対話セッションで起動された時や、 スクリプトを標準入力から読み込んだ場合など) 場合、 path[0] は空文字列となり、  Python はカレントディレクトリからモジュールの検索を開始します。 

4.1.2. 情報工学実験 第一b コマンドラインから実行してみる。

コマンドライン から実行とは、 次のように実行することです。

$ python package/module.py
4.1.2.1. 作成

ご面倒ではありますが   package というディレクトリ/フォルダを作り、その配下に以下のような module.py という Python のコードを保存してください。  import の理解には、どうしても手を動かさないと厳しいところがあるかなと感じています。

# ~/package/module.py
import sys
print(*sys.path, sep='\n')



4.1.2.2. 実行

package/module.py をコマンドプロンプトから実行します。 今度は文字列が表示されました。

~/ $ python package/module.py 
/Users/ユーザ名/package  <------- module.py が存在するディレクトリ
/usr/local/Cellar/python/3.7.1/Frameworks/Python.framework/Versions/3.7/lib/python37.zip
/usr/local/Cellar/python/3.7.1/Frameworks/Python.framework/Versions/3.7/lib/python3.7
/usr/local/Cellar/python/3.7.1/Frameworks/Python.framework/Versions/3.7/lib/python3.7/lib-dynload
/usr/local/lib/python3.7/site-packages
$


4.1.2.3. 確認

「module.py が存在するディレクトリ/フォルダへのパス」が、一番上に表示されることを確認してください。 どの環境でも、このような動作になる筈です。

sys.path
起動時に初期化された後、リストの先頭 (path[0]) には  Python インタプリタを起動したスクリプトのあるディレクトリが挿入されます。 



4.1.3. 問題


パスに関するクイズです。

第 1 問

コマンドプロンプトから Python を実行したとき、追加されるパスは、どちらですか?


4.1.4. まとめ

コマンドプロンプトから実行した場合と、 対話モードから実行した場合で、追加されるパスが変化します。

sys.path

モジュールを検索するパスを示す文字列のリスト。 PYTHONPATH 環境変数と、 インストール時に指定したデフォルトパスで初期化されます。

起動時に初期化された後、リストの先頭 (path[0]) には  Python インタプリタを起動したスクリプトのあるディレクトリが挿入されます。 

スクリプトのディレクトリがない (インタプリタが対話セッションで起動された時や、 スクリプトを標準入力から読み込んだ場合など) 場合、 path[0] は空文字列となり、  Python はカレントディレクトリからモジュールの検索を開始します。 

スクリプトディレクトリは、 PYTHONPATH で指定したディレクトリの 前 に挿入されますので注意が必要です。

PYTHONPATHとは - Stackoverflow
Pythonが import 文で利用するモジュールを探す際のパスです。 以下のコードで、起動時にデフォルト設定されているパスが確認できます。

$ python
>>> import sys
>>> print(sys.path)
環境変数PYTHONPATHを設定すると、この値に追加されます。

4.2. Python で書かれた標準ライブラリへのパス

これが2つ目の難題です。

import すれば使えるものを「ライブラリ」と言います。 これは Python の公式ドキュメントの中に記載のある言葉ではありませんが、 基本的にはそのような使い方をしていいかなと思います。

ライブラリの中でも pip install しなくても 最初から使えるライブラリを「標準ライブラリ」と言います。 copy や math は pip install しなくても使えるので「標準ライブラリ」です。

標準ライブラリには2種類あり Python で書かれたものと C 言語で書かれたものがあります。 以下に GitHub のリポジトリをご紹介します。

4.2.1. 情報工学実験 第二

 sys.path で表示されたディレクトリへの一覧の中で、copy.py が保存されているディレクトリを探してください。 

自分の環境では次のパスが該当しました。

/usr/local/Cellar/python/3.7.1/Frameworks/Python.framework/Versions/3.7/lib/python3.7

4.3. C 言語で書かれた標準ライブラリへのパス

Python のライブラリなのに C 言語で書かれてるってなんだよ?って感じですが、 いまはそんなものもあるんだなくらいに押さえておいてください。

標準ライブラリ math は C 言語で記述されています。

4.3.1. 情報工学実験 第三

 sys.path で表示さしたディレクトリへの一覧の中から、math が保存されているディレクトリを探してください。 

自分の環境では次のパスが該当しました。 全く同じものがあったわけではないのですが math.cpython-37m-darwin.so というそれらしいファイルがありました。

/usr/local/Cellar/python/3.7.1/Frameworks/Python.framework/Versions/3.7/lib/python3.7/lib-dynload

so 拡張子は、以下の記事で、すこしだけ勉強しました。

4.4. pip でインストールしたライブラリへのパス

最初からはいっている標準ライブラリ以外にも pip でインストールしたライブラリが、 どこかにある筈です。 ちゃんとしたドキュメントを、まだ見つけられていないのですが、 大抵の場合 site-packages という名前のディレクトリに保存されているはずです。

4.4.1. 情報工学実験 第四

4.4.1.1. 一覧の表示

pip freeze した結果と照らしわせて見てください。

$ pip freeze
pycodestyle==2.4.0
pydocstyle==2.1.1
pyflakes==2.0.0
virtualenv==16.0.0
virtualenv-clone==0.3.0
virtualenvwrapper==4.8.2
$

自分の環境では次のパスが該当しました。

/usr/local/lib/python3.7/site-packages

5. モジュールとパッケージ

import できるものは大きくわけて2つ。 「モジュール」と「パッケージ」に分けられます。 それぞれについて見ていきます。

5.1. モジュール

Python のファイルはモジュールと呼ばれます。 例えば以下の sample.py は、モジュールです。

ひとつのファイルだけに Python のコードを書き続けると膨大になるので、 モジュールを使って書き分けます。

モジュールの中で定義された変数, 関数, クラスは、 モジュールの属性として参照することができます。

このコードは実行していただく必要はなく、 ざっくり眺めていただければと思います。

# sample.py

# 変数
a = 0

# 関数
def f():
    pass

# クラス
clas C(object):
    pass
import sample

sample.a
sample.f
sample.C
>>> sample.a
0
>>> sample.f
<function f at 0x1010441e0>
>>> sample.C
<class 'sample.C'>
>>> 

module - 用語集
(モジュール) Python コードの組織単位としてはたらくオブジェクトです。 モジュールは任意の Python オブジェクトを含む名前空間を持ちます。 モジュールは importing の 処理によって Python に読み込まれます。

◯ smtblib モジュール

例えばこれは標準ライブラリ smtplib のモジュールです。 詳細を見る必要はまったくないので、 こんなのもあるんだなくらいに思っておいてください。

問題なく import できます。

import smtplib
$ python
>>>
>>> import smtplib
>>>

繰り返しになりますが cpython/Lib/ には標準ライブラリの Python のコードがあります。

5.2. パッケージ

モジュールを組み合わせたものをパッケージと呼びます。 複数の Python のファイルを、1つのディレクトリにまとめるとパッケージになります。

package
(パッケージ) サブモジュールや再帰的にサブパッケージを含むことの出来る module のことです。 専門的には、パッケージは __path__ 属性を持つ Python オブジェクトです。

◯ email パッケージ

例えば、これは標準ライブラリ email のパッケージ構成です。
email というパッケージの中に、さらに mime というパッケージがあります。 また email も mime も __init__.py というモジュールを持っています。 これは何者でしょうか?

email ---- mime ------------------ __init__.py
       |                       |-- application.py
       |                       |-- ...
       |                       |-- text.py
       |
       |-- __init__.py
       |-- _encoded_words.py
       |-- ...
       |-- utils.py

__init__.py は import したときに、実行されるモジュールです。 import email した後に、email.mime を参照すると AttributeError になります。

import email
email.mime
>>> email.mime
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
AttributeError: module 'email' has no attribute 'mime'
>>> 

なぜ email パッケージ配下のパッケージなのになんででしょうか? それは email/__init__.py の中で mime が import されなかったからです。 逆に言えば email/__init__.py の中で mime を import してくれていれば、 email.mime は AttributeError にはならないということです。

この場合は、おとなしく import email.mime と書かないといけません。

import email.mime

6. __init__.py

email パッケージにも、その配下の mime パッケージにも __init__.py がありました。 これは何者でしょうか?

6.1. 配下のパッケージは、自動的に import されない。

再び email 配下の、さらに mime の配下の、text を参照したいとします。 このとき email だけ import してから text を参照しようとすると AttributeError になります。

import email
email.mime.text  # AttributeError
>>> import email
>>> email.mime.text
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
AttributeError: module 'email' has no attribute 'mime'
>>> 

正しくは次のように書きます。

import email.mime.text
email.mime.text
>>> import email.mime.text
>>> email.mime.text
<module 'email.mime.text' from '...'>
>>> 

6.2. __init__.py

このようなことが何故起こるのでしょうか? それは text が import されていないからです。

email パッケージを import しても email パッケージ配下のモジュールやパッケージが、 自動的に import されて使えるようになるわけでは無いということです。 mime は __init__.py の中で import されていないのです。

ではどうすれば import できるのでしょうか? このときに利用するのが __init__.py です。 email パッケージを import したときに __init__.py だけが実行されているからです。

通常のパッケージがインポートされたとき、 この __init__.py ファイルが暗黙的に実行され、 それで定義しているオブジェクトがパッケージ名前空間にある名前に束縛されます。
5. インポートシステム - Python 言語リファレンス

またサブパッケージを import すると自動的に、その先祖のパッケージの __init__ も自動的に実行されます。

例えば、以下のようなファイルシステム配置は、 3 つのサブパッケージを持つ最上位の parent パッケージを定義します:

parent/
    __init__.py
    one/
        __init__.py
    two/
        __init__.py
    three/
        __init__.py

parent.one をインポートすると暗黙的に parent/__init__.py と parent/one/__init__.py が実行されます。 その後に parent.two もしくは parent.three をインポートすると、 それぞれ parent/two/__init__.py や parent/three/__init__.py が実行されます

Python 言語リファレンス - 5. インポートシステム

6.3. 動かして確認してみよう

言葉で説明されてもわからないので、実際に触って動作を確認して見ましょう。

Step 1. ダウンロード

サンプルコードをご用意させていただきました。 GitHub から以下のディレクトリをダウンロードしてください。

ホームディレクトリ ~/ 配下に展開されたものと仮定して、 お話を続けさせていただきます。

Step 2. カレントディレクトリの移動

absolute フォルダ/ディレクトリに移動してください。

~/ $ cd ~/import/absolute
~/import/absolute/ $

Step 3. Python を起動

Python を起動してください。

~/import/absolute/ $ python

Step 4. package を import して sub_package を使う。

対話モード >>> で次のコマンドを実行してください。

import package
package.sub_package.module_b

Step 5. 結果を確認する。

長大ななにかが表示されましたが、これらについては、あとで詳細を説明させていただきます。

いま、ここで確認していただきたいことは、 package.sub_package.module_bAttributeError にならなかったことです。

import mail だけでは email.mime.text と 参照できなかったことを思い出してください。

>>> import package
---

/Users/user/import/absolute/package/__init__.py
package
package
---

/Users/user/import/absolute/package/module_a.py
package
package.module_a
---

/Users/user/import/absolute/package/sub_package/__init__.py
package.sub_package
package.sub_package
---

/Users/user/import/absolute/package/sub_package/module_b.py
package.sub_package
package.sub_package.module_b
>>> 
>>> package.sub_package.module_b  <------ AttributeError にならない。
<module 'package.sub_package.module_b' ... >
>>> 

なぜ import package だけでも、その配下のモジュールを参照できたのでしょうか? それは ./package/__init__.py の中で sub_package を import して、 さらに ./package/sub_package/__init__.py の中でも module_b を import していたからです。

# ~/import/absolute/package/__init__.py
import sys
print('---')
print(sys.path[0])
print(__file__)
print(__package__)
print(__name__)

import package.module_a
import package.sub_package  # <--- いまはここだけ注目してください。

それ以外の表記については、この次の章で説明させていただきます。

6.4. まとめ

パッケージは __init__.py の中で import した「モジュール」や、 定義した「変数」、「関数」、「クラス」だけが使えるようになります。

ちなみにここでご紹介させていただいた smtplib モジュールと email パッケージを組み合わせると Gmail を送信するコードが書けます。

簡単なウェブアプリケーションのユーザ登録時の メールアドレスの認証などで試験的に使えたりします。

7. __main__.py

import したときには __init__.py が実行されますが、 コマンドラインから起動されたときは __main__.py が実行されます。

$ python sample_package  # __main__.py が実行される。

5.1. スクリプトとして起動してみよう

Step 1. スクリプトを実行

引き続き ~/import/absolute/ 配下にいるものとします。

~/import/absolute/ $ python script

実行結果は次のようになるかと思います。

~/import/absolute/ $ python script
---
script
script/__main__.py

__main__
---
script
script/module_a.py

module_a
---
script
script/sub_package/__init__.py
sub_package
sub_package
---
script
script/sub_package/module_b.py
sub_package
sub_package.module_b
~/import/absolute/ $ python script

前章で >>> import package した時と 今回 $ python script した時の出力結果と比較してみると、 いくつか違うところがあります。

5.2. __init__.py と __main__.py の違いを比較する

より正確には「スクリプトとして実行した時と、 パッケージとして import した時の違い」の方が、表現としては正しいかもしれません。

では、違いはなんでしょうか? 恐らく大きく異なるのは「パス」が異なります。 これは「4. どこから import されるの?」の復習になります。

出力結果を見ると全体的に違うのですが、まず注目していただきたいのは 実行時のパス sys.path[0] の結果です。

$ python script した場合は 'script' が出力されました。 これは起動したスクリプトがあるディレクトリが保存されています。

sys.path
起動時に初期化された後、 リストの先頭 (path[0]) には Python インタプリタを起動したスクリプトのあるディレクトリが挿入されます。

それに対して >>> import package した場合は、空の文字列 '' が出力されました。

sys.path
スクリプトのディレクトリがない (インタプリタが対話セッションで起動された時や、 スクリプトを標準入力から読み込んだ場合など) 場合、 path[0] は空文字列となり、  Python はカレントディレクトリからモジュールの検索を開始します。 

「パッケージとして import した時」、 「パス」には ~/import/absolute/ が追加されます。 Python インタープリタを起動したディレクトリのパスが設定されます。 例えば module_b を import する場合は、それぞれ次のようになります。

# ~/import/absolute/package/__init__.py
import package.sub_package.moduel_b

それに対して「スクリプトとして実行した時」、 「パス」には ~/import/absolute/script が追加されます。 スクリプトが置かれているディレクトリのパスが設定されます。 例えば module_b を import する場合は、それぞれ次のようになります。

# ~/import/absolute/package/__main__.py
import sub_package.module_b

そのため、実行時のパスが異なるため import する時の指定の仕方が異なって来ます。

5.3. モジュール関連の属性

何を出力しているか簡単にご紹介させていただきます。 全てのモジュールに次のようなコードを書き込んでいます。

import sys
print('---')
print(sys.path[0]) # <-- 追加された検索対象のパス
print(__file__)    # <-- ファイルのパス
print(__package__) # <-- 所属しているパッケージの __name__
print(__name__)    # <-- 所属している最上位のパッケージからの位置

だいたいここから引っ張って来ました。 __file__ は PEP まで入り込まないと説明がなかった。
5.4.4. インポート関連のモジュール属性

__file__ - PEP 3147
Python 3 では モジュールを import したとき __file__ 属性は Python ファイルへのパスを指します。 (Python 2 においてはバイトコンパイルされたファイルパスを指します。)
In Python 3, when you import a module, its __file__ attribute points to its source py file (in Python 2, it points to the pyc file).

__package__
モジュールの __package__ 属性は設定されなければなりません。 値は文字列でなければなりませんが、__name__ と同じ値でも構いません。 モジュールがパッケージの場合、__package__ の値はその __name__ でなければなりません。 モジュールがパッケージでない場合、トップレベルのモジュールでは __package__ 空の文字列、サブモジュールでは親のパッケージ名でなければなりません。 詳細は PEP 366 を参照してください。

__name__
__name__ 属性はモジュールの完全修飾名に設定されなければなりません。 この名前を利用してインポートシステムでモジュールを一意に識別します。

よく使うこの書き方の __main__ が出力されていますね。 いままで曖昧な理解だったのですが、すこし理解できました。

if __name__ == "__main__":
    # execute only if run as a script
    main()

__main__
トップレベルのコードが実行されるスコープの名前です。 モジュールが、標準入力から読み込まれたとき、 スクリプトとして実行されたとき、 あるいはインタラクティブプロンプトのとき、 __name__ には '__main__' が設定されます。

8. -m オプション

またコマンドラインから -m を指定して起動した場合、 sys.path を探索してパッケージ、またはモジュールをスクリプトとして実行します。 __main__.py だけが実行されるわけではなく、 __main__.py が実行されたのちに __init__.py が実行されるという挙動をしていました。

$ # timeit を使って 1/2 の実行時間を計測
$ python -m timeit -s "a,b=1,2" "a/b"
10000000 loops, best of 5: 38.6 nsec per loop
$ 

-m <module-name>
sys.path から指定されたモジュール名のモジュールを探し、 その内容を __main__ モジュールとして実行します。

5. インポートシステム - Python 言語リファレンス
PEP 338 はモジュールをスクリプトとして実行するときの仕様を定めています。

PEP 338 - Executing modules as scripts
This PEP defines semantics for executing any Python module as a script, either with the -m command line switch, or by invoking it via runpy.run_module(modulename).

9. sys.moduels

sys.modules に束縛されます。そのためこれを消すと import も削除されます。 びっくりしました笑

# 対話モードにコピペして見てください
# ~/ $ cd ~/import/absolute/
# ~/import/absolute/ $ python
# >>>

import package
# 色々表示される。

import package
# 2回目は何もおこらない。
# import は2回目以降は名前束縛だけして実行はしません。

import dl
dl.exe()

import package
# 再び色々と表示される。

sys.modules
既に読み込まれているモジュールとモジュール名がマップされている辞書です。 これを使用してモジュールの強制再読み込みやその他のトリック操作が行えます。 ただし、この辞書の置き換えは想定された操作ではなく、 必要不可欠なアイテムを削除することで Python が異常終了してしまうかもしれません。

10. 相対 import と 絶対 import

これまでの import の書式は次のようなものでした。

import sys.pathからパッケージへのパス
import sys.pathからモジュールへのパス

パッケージ内のモジュール-モジュール モジュール-パッケージの参照の仕方として、 パスからの位置を指定する以外にも、相対関係の位置を指定する方法があります。

from .ファイルの位置からパッケージへのパス import オブジェクト
from .ファイルの位置からモジュールへのパス import オブジェクト

これを「相対 import」と呼びます。 いままで見てきたのは全て「絶対 import」になります。

相対 import が導入された背景などは PEP 328 に書かれていました。 翻訳しました。

10.1. 相対 import の2つの約束

10.1.1. パッケージの中でしか使えない。

相対 import は、パッケージの中でしか使えません。 これ、超重要です。 よく直接呼び出すスクリプトの中で $ python sample 相対 import を使って引っかかっている方を見かけます。

$ python
>>> import package  # <-- これは「パッケージ」として認識される。
$ python script  # <-- これは「スクリプト」として認識される。

10.1.2. from ... import ... としか書けない。

相対 import は from ... import ... だけしかかけず import ... とだけは書けません。 何故なら import を許してしまうと、式として使えなくなるからです。

10.2. やってみよう

10.2.1. 情報工学実験 a

~/import/absolute/package 配下のスクリプトを、 相対 import に書き換えて、対話モードから import できるようにしてください。

~/import/absolute $ python
>>> import package

10.2.2. 情報工学実験 a 解説

解答は ~/import/relative/package にあります。

パッケージとして利用 >>> import package する時は、 次のように規則性に則って書き換えていきます。

# 変更前
import package.sub_package.module_b

# 変更後
from .package.sub_package import module_b

10.2.3. 情報工学実験 b

~/import/absolute/script 配下のスクリプトを、 相対 import に書き換えられるものは書き換えて、 コマンドラインから実行できるようにしてください。

~/import/absolute $ python script

10.2.4. 情報工学実験 b 解説

スクリプトとして実行する python script 場合は、 全てを相対 import に書き換えることはできません。

なぜなら __main__.pymoduel_a.py がパッケージの中には無いからです。 繰り返しになりますが 相対 import は、パッケージの中でしか使えません。


11. 出力されるエラー

最後に improt 関係で発生するエラーを元に知識を再整理したいと思います。

11.1. ModuleNotFoundError の場合

そもそも Module が存在しないときに発生します。 「パッケージ」や「モジュール」をパスから見つけられなかった時に投げられる例外です。

ここで大事なのは、「パッケージ」や「モジュール」が、どこから検索されるのか、と言うことです。 import の書き方は、大きく分けて2種類あります。「絶対 import」と「相対 import」です。 絶対 import は sys.path からの位置を記述します。

◯ その1

email.mime.text を import しようとして import text としても MoudleNotFoundError になります。 これはパスを理解する必要があります。 詳細は こちら からどうぞ。

import text             # ModuleNotFoundError

import email.mime.text  # エラーにならない

◯ その 2

 相対 import  スクリプト  から実行しようとしています。 相対 import というのは、以下のように先頭にドット . のついた import 形式です。

from . import module
from .package import moduel

ここで大事なのは相対 import は  パッケージの中だけでしか使えない  ということです。

(1) スクリプトとして実行した時と (2) パッケージとして import した時の 動作の違いを理解する必要があります。 以下 teratail で同じものを見つけました。

詳細は こちら からどうぞ。

11.2. AttributeError の場合

モジュールはあったけど、その配下に属性が存在しなかった場合に発生します。 パッケージと、 パッケージとして import, 読み込まれたときに 最初に __init__.py が実行されていることを理解する必要があります。 詳細は こちら からどうぞ。

# 1)
import email
email.mime.text  # AttributeError

# 2)
import email.mime.text
email.mime.text  # エラーにならない

11.3. ImportError の場合

パッケージの外側から内側を覗こうとして、相対 import を使用すると発生します。

$ # script.py は「パッケージ」ではなく「スクリプト」である。
$ python script.py

__main__.py から相対 import をしようとして、 パッケージの外からパッケージの中を覗こうとしています。 ひとつ前の 0.3 は、__main__.py 以外のスクリプトから 相対 import をしようとする時にでるエラーです。 ややこしいので、統一してほしいです。

(1) スクリプトとして実行した時と (2) パッケージとして import した時の 動作の違いを理解する必要があります。 詳細は こちら からどうぞ。

11.4. ValueError の場合

パッケージの内側から外側を覗こうとして相対 import を使用すると発生します。 相対 import をしようとして、 パッケージの中から外を覗こうとしています。 スクリプトとして実行した時と、パッケージとして import された時の 動作の違いを理解する必要があります。 詳細は こちら からどうぞ。

11.5. import は難しい。

3つの理由があると思います。 簡単にご紹介いたします。

まず、第一に  モジュール  パッケージ  で2種類あるからです。 パッケージはモジュールを集め、ディレクトリでひとまとめにされたものです。

また、第二に  パス  の概念を知る必要があります。 パスというのは、import する時にライブラリを検索する、ディレクトリへのパスのことです。 検索対象のディレクトリのパスは、1つだけではなく複数個あります。

そして、第三にコマンドラインから python sample.py と書いて  スクリプトとして実行した時 と、 他の Python スクリプトから import sample と書いて  ライブラリとして読み込んだ時 で、 import の挙動が変わってくるからです。

この文章では、これら3つの引っ掛かりどころ、 それぞれ解消していきました。

12. おわりに

import が呼ばれると「検索」して「実行」して「代入」しています。