トップページに戻る

Pythonの基礎2

目次

  • プログラムの流れ
    • 条件分岐
    • 繰り返し
  • プログラムの再利用
    • 関数
    • モジュール
  • その他
    • コメント
    • 継続行
  • 参考文献

プログラムの流れ

条件分岐: if, elif, else

if文による条件分岐処理の基本文法は次のとおりです。

if [論理型]:
    [処理]         # ifの条件がTrueの場合
elif [論理型]: 
    [処理]         # ifの条件がFalseで、elifの条件がTrueの場合
else:
    [処理]         # ifもelifもFalseの場合

end ifに相当する(ブロックの終わりを示す)文が無いことがわかると思います。これがPythonの見た目上の大きな特徴で、インデントによってブロックを判別しているのです。インデントはスペースでもタブでもよいのですが、プログラムを通して統一されている必要があります。通常、スペース4つがインデントに用いられます。ifなどの行末のコロンを忘れないようにしましょう。

if文の構造は必ずしもこの形である必要はなく、ifブロックのみや、if-elif、if-else、if-elif-elif-...などの形ももちろん可能です。

In [1]:
time_now = 15

if (time_now < 7) or (23 < time_now): # False 
    print("Sleeping")
elif time_now < 12:                   # False
    print("Good morning!")
elif time_now < 17:                   # True
    print("Good afternoon!")
else:
    print("Good evening!")
Good afternoon!

繰り返し: for, while

繰り返しは、for文やwhile文を用います。

for文の基本文法は次のとおりです。

for n in [配列型]:
    [処理]

例によってインデント記法です。forの行のコロンに気をつけましょう。 ここでnはダミー変数(文字はなんでもよい)で、配列型の要素を順に取ります。配列型としてはlist,tuple,strなどを用いるほか、range関数を用いることも多いです。range関数は次のような文法によって、等差数列を順に生成します(厳密には、range関数は「等差数列を順に返すオブジェクト」を生成しています。これをrange型のオブジェクトと呼ぶことができます)。

range(start,end,interval)   # startからendまで(endは含まない)、間隔はinterval
range(start,end)            # 省略するとinterval = 1
range(end)                  # start = 0, interval = 1

rangeはPython 2までは数列の入ったlistを返していたのですが、Python 3からrangeオブジェクトを返すようになりました(生成される数をメモリ上に格納しないため、メモリの節約になります)。使い方は同じです。

In [2]:
my_str = "orange"
for n in my_str:
    print(n)
o
r
a
n
g
e
In [3]:
for i in range(10):
    cube = i**3
    print(cube)
0
1
8
27
64
125
216
343
512
729

For文においてもうひとつ便利な関数として、enumerateがあります。enumerate関数は次のような文法で、インデックスnと配列要素a[n]のtupleを返してくれます。

for (n,a) in enumerate([配列型]):
    [処理]
In [4]:
for (n,a) in enumerate("apple"):
    print(str(n)+", "+a)
0, a
1, p
2, p
3, l
4, e

while文の基本文法は次のとおりです。

while [論理型]:
    [処理]

条件式(論理型の部分)がTrueである限り、処理を繰り返します。条件式がどこかでFalseになるようにするか、break文によってループから出ることでループを終わらせます。

In [5]:
i = 10.0
while i > 1:
    print(i)
    i = i / 2
10.0
5.0
2.5
1.25

プログラムの再利用

関数

Pythonでは、Fortranにおけるsubroutineとfunctionの差はなく、まとめてfunction(関数)として扱われます。関数は次のように定義されます。

def [関数名](引数):
    [処理]
    return [戻り値]

これもインデント記法が必須です。コロンを忘れないようにしましょう。 例を使って詳しく見ていきましょう。

In [6]:
# 引数を2乗して返す関数
def square(x):
    return x**2

y = square(3) + square(4) # 3**2 + 4**2 = 25
print(y)
25
In [7]:
# 引数のうち大きい方を返す関数
def greater(x,y):
    if x >= y:
        return x
    else:
        return y
    
print(greater(4/3, 1))
1.3333333333333333
In [8]:
# 引数なしやreturnなしの関数もできます
def bark():
    print("Bow-wow!")

bark()
Bow-wow!
In [9]:
# オプション引数は、関数定義の際に[引数=デフォルト値]とすることでセットできます(通常の引数より後ろにおくこと)
def lunch(time,vegetarian=False):
    if time < 11:
        print("Too early for lunch!")
    elif time > 16:
        print("Too late for lunch!")
    else:
        if not vegetarian:
            print("Hamburger!")
        else:
            print("Vegetarian hamburger!")
            
lunch(15)
lunch(15, vegetarian=True)
Hamburger!
Vegetarian hamburger!

関数は基本的に値渡しです。

In [10]:
n = 10
def plusone(x):
    x = x + 1
    return x
print(plusone(n))
print(n)
11
10

リストやディクショナリなどはすべて参照として扱われるため、関数に渡した場合でも参照渡しのようになります。

In [11]:
# lis には参照が入る
lis = [10]

# 代入文でも参照が渡される
newlis = lis

# newlis を変更すると、その内容は lis にも伝播する
newlis[0] = 11
print("newlis", newlis)
print("lis", lis)

# 配列の中身を変更する関数
def plusone(x):
    x[0] = x[0] + 1
    
# この関数を呼ぶと、関数の中で行われた変更は、呼び出し元の変数 lis に伝播する
plusone(lis)
print("plusone", lis)
newlis [11]
lis [11]
plusone [12]

詳細は述べませんが、関数内で変数を書き換える場合、その影響は関数内にとどまります(ローカルスコープ)。関数外で定義された変数を書き換えたい場合は、global文を用いてその変数がグローバル変数であることを宣言する必要があります。

In [12]:
x = 10

def try_to_rewrite_1():
    global x
    x = x + 10

def try_to_rewrite_2():
    x = x + 10
    
print(x)
try_to_rewrite_1() # successful
print(x)
try_to_rewrite_2() # raises Error
10
20
---------------------------------------------------------------------------
UnboundLocalError                         Traceback (most recent call last)
Input In [12], in <cell line: 13>()
     11 try_to_rewrite_1() # successful
     12 print(x)
---> 13 try_to_rewrite_2()

Input In [12], in try_to_rewrite_2()
      7 def try_to_rewrite_2():
----> 8     x = x + 10

UnboundLocalError: local variable 'x' referenced before assignment

モジュール

汎用的な関数や定数群などを異なる複数のスクリプトで用いるときは、モジュールを用いるのが便利です。 モジュールの作り方は簡単で、基本的には独立したスクリプト(my_module.pyとします)の中に関数や定数を順に書いていくだけです。

たとえば、次のような内容のmy_module.pyを作ったとしましょう。

pi = 3.141592

def area(r):
    return pi * r * r

このモジュールを使うには、my_module.pyがあるディレクトリで(あるいは後述のようにmy_module.pyへのパスを通して)、次のようにします。

In [13]:
import my_module

radius = 5.0
S = my_module.area(radius) # my_module内のarea関数

pi = 0
print(pi)                  # このスクリプト内の定数pi
print(my_module.pi)        # my_module内の定数pi
0
3.141592

モジュールで定義された変数や関数は、このスクリプト内の名前空間ではmy_module.[オブジェクト名]として扱われます。そのため、スクリプト内のオブジェクトとモジュール内の(しばしば把握しきれていない)オブジェクトとが干渉することが避けられます。

次のようなimportの方法もあります。

In [14]:
import my_module as mod   # 別名をつける
print(mod.pi)

from my_module import pi  # モジュール内の特定のオブジェクトのみをインポート
print(pi)                  # この場合、モジュール内の選ばれたオブジェクトがこのスクリプトの名前空間内に定義される
3.141592
3.141592

モジュール内のすべてのオブジェクトをスクリプトの名前空間にインポートする、

from my_module import *

という方法もありますが、モジュール内の把握しきれないオブジェクトがスクリプト内の変数と干渉するおそれがあるため、使わないことをお勧めします。

ここではmy_module.pyをスクリプトと同じディレクトリに置いた状態での使い方を紹介しましたが、色んな所から同じモジュールを呼び出したいときにはimportが参照するパスにmy_module.pyの場所を追加することができます。たとえばLinuxの場合は環境変数PYTHONPATHを用いたり、Pythonスクリプト上からsysモジュールを用いて次のようにパスを通すことができます。

from sys import path  # pathは通っているパスが格納されているリストです
path.append("/home/username/where/module/is/located") # リストのappendメソッドで要素を追加  

処理の高速化のため、一度読まれたモジュールはimport文があっても再び読まれません。 モジュールを修正して再び読み込んでほしい時には、セッションを再スタートする(IPythonなどを一度閉じる)か、importlibモジュール内のreload関数を用います。Python 2では組み込み関数としてreload()が定義されているため、importlibモジュールをインポートする必要はありません。

In [15]:
import my_module
# この時点でmy_module.pyを書き換えたとする
import my_module
# ここではモジュールの更新が適用されていない
from importlib import reload
my_module = reload(my_module)
# モジュールの更新が適用される

Python組み込みの便利なモジュールとして、ファイル管理などを通してシェルスクリプトを代替する機能を得られるosshutil、正規表現を扱うre、日付を扱えるdatetimeなどがあります。numpyscipymatplotlibといったサードパーティのライブラリもモジュールの形で利用することになります。scipyなどの大きなモジュールはサブモジュールという階層構造をとっていることも多く、メモリ節約のために必要なサブモジュールだけをインポートすることも覚えておくとよいでしょう。

In [16]:
import numpy as np    # NumPyはまるごとインポートしても大丈夫です
print(np.pi)           # piやexp, cosなどの重要関数はnumpy直下にあります
x = np.random.rand(10) # randomサブモジュール内のrand関数
y = np.cos(x)          # numpyモジュール内のcos関数

from scipy import special as sp # 特殊関数関連のサブモジュール
z = sp.erf(x)          # 誤差関数
3.141592653589793

その他

コメント

1行のコメントアウトならば # (シャープ)が用いられ、#以後改行までの内容が無視されます。
複数行のコメントアウトはシングルクォーテーション3つまたはダブルクォーテーション3つをコメントの前後に配置します。
特に関数の定義(def func():)の次の行から始まる複数行コメントアウトはdocstringと呼ばれ、そこに関数の説明を加えておくことが推奨されています。

In [17]:
def func(x):
    """
    This is an example docstring.
    """
    sq = x*x # コメント
    return sq

継続行

行を継続するときは、行の終わりに** \ **(バックスラッシュ)を入れます。
ただし、関数の引数が数多くある場合など、(), [], {}のカッコの中で改行するときには\を省略できます。

また、継続行の行頭の開始位置に関して制約はありません。

In [18]:
x = 3
y = 1 + 2*x + 3*np.sin(x) \
  + 4*np.cos(x) + 5*np.exp(x)

some_list = [1, 2, 3, 4, 5,
            6, 7, 8, 9, 10]

基礎編

応用編