Pythonの基礎#

in English or the language of your choice.

# 警告メッセージを非表示
import warnings
warnings.filterwarnings("ignore")

=の意味と「オブジェクト」#

変数\(x\)に値10を「代入」するには等号=を使う。

x = 10

また\(x\)の値を表示すには、次のように\(x\)を書いたセルを評価するだけである。

x
10

ここで「代入」に使った=について少し説明する。実は、=は「代入」ではない。更には、x10は別物なのである。これを理解するために、多くの品物が保管されている大きな倉庫を考えてみよう。倉庫の管理者はどの品物がどこに保管されているかを記録する在庫リスト(記録帳やコンピューター・ファイル)を作成し、そこに品物が保管されている棚を示す記号を記入しているとしよう。この例を使うと、

  • 10 → 倉庫の棚に保管されている品物

  • x → 在庫リストに記載されている棚の記号

となる。品物と棚の記号は別物なのである。Pythonでは、品物である10がコンピューター内のメモリーの所定の場所に保存され、その場所を示すのが変数xとなる。即ち、xは品物10の実態とは異なる単なる「参照記号」なのである。

  • 10 → PCのメモリーに保存されている情報

  • x → 参照記号

この点を明確にするために、上のコードは「x10を代入する」と考えるのではなく、「10を記号x割り当てる」と考える。ここで、式を右から左に読んでいることに注意しよう。左から右に読んで「記号x10に割り当てる」と考えないことを推奨する。意味は同じだが、=を右から左に読む(考える)ことを習慣づけることが、今後Pythonを勉強する上で重要となるからである。この点を示すために次のコードを考えてみよう。

x = x + 1

「?」と思うかもしれない。暗に方程式として考えるためであろう(私がそうだった)。これを右から左に読むとスッキリする。

  1. 一番上のコードで10xに割り当てたが、問題のコードの右辺のxがその10である。101を加えたものが11であり、それが右辺である。

  2. =を使い右辺の11を左辺のxに割り当てている。この時点で、10の参照記号であったx11の参照記号に変更される。

実際にxを表示してみよう。

x
11

この例では、記号x10を指していたが11に変更されている。これは、同じ記号を複数の「品物」の参照記号に使えないためである。一方で、同じ「品物」を複数の参照記号に割り当てる事は可能である(例えば、y=x)。いずれにしろ「品物と参照記号の関係」は今の段階ではそれ程重要ではないが,先に進むにつれて重要性が増してくるので,今のうちにこのようなイメージを持つと良いだろう。

更にもう一点付け加える。Pythonを習うと「オブジェクト」という単語が必ず出てくる。今の内にイメージをつかむために自転車をオブジェクトの例として考えてみよう。通常の自転車には車輪が2つあり、サドルが1つあり、左右にペダルが2つある。これらの数字が自転車に関するデータである。またペダルを踏むことにより前に動き、ハンドルを右にきると右方向に進むことになる。即ち、あることを実行すると、ある結果が返されるのである。これは数学の関数と同じように理解できる。\(y=x^2\)の場合、\(x\)2であれば\(y\)の値として4が返される。このように自転車はデータと関数が備わっているオブジェクトとして考えることができる。また、車輪の数やペダルを踏むことは自転車特有のデータと関数であり、他のオブジェクト(例えば、冷蔵庫)にはない。即ち、世の中の「オブジェクト」にはそれぞれ異なるデータと関数が存在していると考えることができる。

Pythonの世界でも「すべて」をこれと同じように考え、データを属性と呼び、関数をメソッドと呼ぶ。上のコードの10を例にあげると、単なる数字に見えるが、実は様々な属性とメソッドから構成されるオブジェクトなのである。

  1. 属性(attributes)は10が持つ様々なデータ(例えば、10という値や整数という情報)

  2. メソッド(methods)は10の特有の関数(例えば、加算、除算のように10というデータに働きかける関数)

自転車と冷蔵庫は異なるデータと関数を持つように、整数10と文字列神戸大学は異なるデータと関数を備えるオブジェクトなのである。この考え方はPythonのすべてに当てはまる。即ち、Everything is an object in Python.

(メソッドも属性の一種と考えることもできるが、以下では上の分け方に沿ってPythonの使い方を説明する。)

4つの基本データ型(Data Types)#

Pythonでは全てがオブジェクトと話したが,オブジェクトの種類は無数にある。まず,その中心であるデータ型(data types)を紹介する。基本となる次の4つを考える。

  • 整数型(int)

  • 浮動小数点型(float)

  • 文字列型(str)

  • ブール型(bool)

整数型とは上の例で使ったもので,

157
157

も整数型である。Pythonには様々な関数が用意されており,その1つがtype()である。それを使うとデータ型を確認できる。

type(157)
int

intはintegerの略で整数型を表す。

浮動小数点型は

10.24
10.24

のように小数点がついた数字である。1は整数型だが,1.0は浮動小数点型となる。

文字列型はシングルクォート'かダブルクォート"の間に文字を入力する。

"apple"
'apple'
'pear'
'pear'

どちらを使っても同じだが,クォートの中でクォートを使う必要がある場合は,それぞれ違ったものを使う。例えば,

'He said "Good luck!" to me.'
'He said "Good luck!" to me.'

また数字をクォートで囲むと文字列型になる。

'100'
'100'
type('100')
str

ブール型(boolean)にはTrue(真), False(偽)の2つ(だけ)がある。

True
True
type(True)
bool

ブール型の例として、110が等しいかどうかを調べるコードを考えてみる。ここで==を使うが、==を挟んで左右の変数のが正しければTrueを、間違っていればFalseが返される。

1 == 10
False

この結果を変数に割り当てることも可能である。(次のコードでは()1 == 10を囲んでるが、読みやすくするためであり、無くてもエラーは出ない。)

s = (1 == 10)
s
False

また,True1, False0として計算される。

True == 1
True
False == 0
True
True + True
2

Pythonには「クラス(class)」という概念がある。オブジェクトを生成するための設計図となるもので,この授業で説明はおこなわないが「型(types)」と同義と考えて良い。ちなみにクラス自体もオブジェクトである。

上のコードでは関数type()を使ってデータ型を確認したが,引数を画面上に表示する関数であるprint()を使うとクラス名としてデータ型が表示される。

type(10)
int
print(type(10))
<class 'int'>
type(10.0)
float
print(type(10.0))
<class 'float'>
type('10')
str
print(type('10'))
<class 'str'>
type(True)
bool
print(type(True))
<class 'bool'>

コードのかなで想定されたデータ型と異なるデータ型を使うとがエラーが出たり,間違った結果につながる場合があるので,自信がない時はtype()を使って確認すること。

この他類似する型にcomplex type(複素数型)とNoneTypeも存在する。次のNoneとは「無」という意味である。

type(None)
NoneType
print(type(None))
<class 'NoneType'>

コレクション系データ型#

コレクション系とは上で説明した基本データ型の集まりとなるデータ型で,ここでは以下を簡単に説明する。

  • リスト(list)

  • タプル(tuple)

  • 辞書(dict)

  • 集合(set)

リストは[]を使う。

list0 = [10, 3 , 2]
list0
[10, 3, 2]

以下もリストの一例である。

list1 = ['A', True, 100]
type(list1)
list
print(type(list1))
<class 'list'>

上で説明したように、print()は定義したリストを画面上に表示する関数であり,print()を使うとlistというクラス(class)であることも確認できる。


タプルは()を使って作成する。

tuple0 = ('A', True, 100)
print(tuple0)
('A', True, 100)

リストと変わりないように見えるが,大きな違いは要素を変更できるかできないかという点である。

  • リストの要素は変更可能

  • タプルの要素は変更不可能

リストの要素の変更方法は以下で説明する。

<コメント1>

上で通常タプルは()を使って作成できると説明したが、実は、コンマ,によってタプルは定義されるため()は必須ではない。例えば、次のコードでもタプルとなる。従って、()はタプルを明確にするためと考えて良い。

tuple1 = 'B', False, -100
print(tuple1)
print(type(tuple1))
('B', False, -100)
<class 'tuple'>

<コメント2>

1つの要素からなるタプルを作成する場合、コンマ,が必ず必要となる。

tuple2 = (10,)
print(tuple2)
print(type(tuple2))
(10,)
<class 'tuple'>

コンマ,がないとタプルとはならない。

tuple3 = (10)
print(tuple3)
print(type(tuple3))
10
<class 'int'>

辞書はキー(key)と値(value)のペアとなって定義され,:を挟んで1つのペアとなる。全てを{}で囲み辞書を定義する。

dict0 = {'a':10, 'b':'Kobe'}

dict0には2つのペアがある。aのキーには値10が対応しており,bには'Kobe'が設定されている。今の段階では辞書を使う目的が不明確でしっくりこないと思うが,勉強を進めるととてもパワフルなツールだと気づくだろう。

type(dict0)
dict
print(type(dict0))
<class 'dict'>

集合は使う機会がないので説明は割愛する。

要素のアクセス方法#

まずリストの要素の数え方を説明する。次の図のように左から012…,右からは-1-2-3と数える。

   0   1   2   3   4   5  (左から数える) 
 +---+---+---+---+---+---+
 | P | y | t | h | o | n |
 +---+---+---+---+---+---+
  -6  -5  -4  -3  -2  -1 (右から数える)

例えば

my_list = ['A', 'B', 'C', 'D', 'E', 'F']

の場合,'A'は0番目,'B'1番目,'C'は2番目と数える。例えば,Aを抽出したい場合,

my_list[0]
'A'

最後の要素にアクセスするには次のようにする。

my_list[-1]
'F'

連続する複数の要素を選択する場合(スライシング)は:を使う。:の左側が選択する最初の要素で,:の右側が選択する最後の次の番号である(即ち,:の右側の番号の要素は含まれない。

my_list[1:4]
['B', 'C', 'D']

この例では1番目から3番目を選択している。:の左側の番号を省略すると0と解釈され,:右側を省略すると最後まで全てと解釈される。

my_list[:4]
['A', 'B', 'C', 'D']
my_list[1:]
['B', 'C', 'D', 'E', 'F']

タプルも同じように選択でる。

my_tuple = ('A', 'B', 'C', 'D', 'E', 'F')
my_tuple[2]
'C'
my_tuple[:3]
('A', 'B', 'C')
my_tuple[2:]
('C', 'D', 'E', 'F')

文字列も同じ方法でアクセスできる。

my_text = 'University'
my_text[1:4]
'niv'

辞書の場合はキーで指定する。複数指定する場合は,for loopなどの複雑な手法が必要となる。

my_dict = {'a':10, 'b':'apple', 'c':[1,2,5]}
my_dict['a']
10
my_dict['c']
[1, 2, 5]

変数名に使う記号について#

上の例ではmy_listなど好きなアルファベットの組み合わせを使いわかりやすい変数名にしている。しかし,変数の名前を作る上で守らなくてはならないルールがある。

  • (a-z, A-Z)もしくは_(アンダースコア)で始める

  • 最初の文字以外であれば(a-z, A-Z)_に加え数字も可

  • 長さに制限はない

  • 小文字と大文字は異なる記号としてあつかう

  • 次の単語は特定の目的のために事前に定義されているため,変数名としては使えない。

import keyword
keyword.kwlist
['False',
 'None',
 'True',
 'and',
 'as',
 'assert',
 'async',
 'await',
 'break',
 'class',
 'continue',
 'def',
 'del',
 'elif',
 'else',
 'except',
 'finally',
 'for',
 'from',
 'global',
 'if',
 'import',
 'in',
 'is',
 'lambda',
 'nonlocal',
 'not',
 'or',
 'pass',
 'raise',
 'return',
 'try',
 'while',
 'with',
 'yield']

これらに加え,

  • 変数の頭文字は小文字とする

というのが慣例(エラーにはならない)であり,大文字で始まる変数はclassと呼ばれるオブジェクトに使う。

また#は実行されないコメントを書くときに使われる。以下の例では,1+2は実行されるが#で始まる行は無視される。

# この行はコメント
1+2
3

算術演算子#

  • +(加算; addition)

  • -(減算; subtraction)

  • *(乗算; multiplication)

  • /(除算; division)

  • //(切り捨て除算; 実数の整数部分; floor division)

  • %(剰余演算; 余りを取得する演算; modulo)

  • **(累乗; exponentiation)

加算(addition)

type(1+1)
int
type(1+1.0)
float
'I' + 'like' + 'Kobe'
'IlikeKobe'
'I' + ' like' + ' Kobe'
'I like Kobe'
[1, 2] + ['apple']
[1, 2, 'apple']

減算(division)

1-0.5
0.5

乗算(multiplication)

type(2*2)
int
type(2*2.0)
float
[1,2,3]*3
[1, 2, 3, 1, 2, 3, 1, 2, 3]

除算(division)

type(10/5)
float

切り捨て除算(floor division)

  • 以下では分子・分母が正の値の場合だけを考える

5//2 # 5/2=2.5
2

剰余演算(modulo)

  • 以下では分母が正の値の場合だけを考える

5%2 # 5÷2=2 余り 1
1

累乗(exponentiation)

type(2**2)
int
type(2**2.0)
float

関係演算子#

  • ==(等号)

  • !=(等号不成立)

  • <(小なり)

  • >(大なり)

  • <=(小なりイコール)

  • >=(大なりイコール)

10 == 10
True
10 != 10
False
10 > 5
True
10>=10
True
10<=10
True

理論演算子#

  • a & b  (aとbの両方)

  • a | b   (a又はb又は両方)

  • ~a      (aではない)

if 文#

if文を使うと,あるブール型(真偽)の条件のもとでコードを実行することが可能となる。例えば,Xの値が正の場合,

print(X, 'は正です')

を実行したいとしよう。

X = 10
if X > 0:
    print(X, 'は正です')
else:
    pass
10 は正です

<注意点>

  • ifelse で始まる条件を示す行の最後は:となる。入れ忘れるとエラーとなる。

  • print()の行は4つの半角スペースのインデントを入れること。(半角スペースが入っていれば、スペースの数は関係なしにエラーは発生しい。しかし可読性向上のために4つの半角スペースを使うことが慣例となっている。)

  • elseとは「X>0以外の場合」という意味。

  • passは「何もしない」という意味。

ここでelse以下を省略してもエラーにはならない(結果も変わらない)。即ち,else以下がない場合は,それが省略されていると考えれば良い。

X = -1
if X > 0:
    print(X, 'は正です')
else:
    pass
if X > 0:
    print(X, 'は正です')

両方とも何も表示されない。


次に,複数の条件を導入するために次の3つprint関数を使おう。

  1. print(X, 'は正です')

  2. print(X, 'はゼロです')

  3. print(X, 'は負です')

Xの値が正の場合は1を,ゼロの場合は2を,負の場合は3を表示したいとしよう。

X = -1
if X == 0:
    print(X, 'はゼロです。')
elif X > 0:
    print(X, 'は正です。')
else:
    print(X, 'は負です。')
-1 は負です。

注意点

  • if, elif, else で始まる条件を示す行の最後は:となる。入れ忘れるとエラーとなる。

  • print()の行は4つの半角スペースのインデントが入る。

  • elseの行にX<0は不要(残りの可能性はX<0しかないため)

  • elifelse ifの省略形であり,2つ目の条件を定義する。

  • elififelseの間に複数入れることが可能

for ループと内包表記#

forループは同じコードを複数回リピートして実行したい場合に有効な方法である。例えば,次のリストにある名前を表示したいとしよう。

name_list = ['太郎', '次郎', '三郎', '四郎', '五郎']
for name in name_list:
    print(name)
太郎
次郎
三郎
四郎
五郎

説明と注意点

  • forがある一行目は:で終わる。

  • namename_listにあるそれぞれの要素を示すダミー記号。nameではなくisなど使いやすい記号を使って構わない。

  • name_listにある要素を最初から一つずつ実行する。

  • 2行目は4つの半角スペースを使ってインデントしている。

forループでよく使うパターンして次の例を考える。この例では,リストに要素を追加する.append()を使うが,これについては以下でより詳しく説明する。

まず次のリストを定義する。

var_list = [1,2,3,4,5]

それぞれの要素の2倍からなるリストを作成したいとしよう。

my_list = []  # 1

for i in var_list:  # 2
    my_list.append(2*i)  # 3

このループの考え方:

  1. 空のリストの作成(ここに2倍にした数字を格納する)

  2. ここからforループの始まりとなる。iはループを回していく際に[1,2,3,4,5]の要素を割り当てる変数である。var_listには5つの要素があるので5回同じ作業を繰り返す。1回目のループでは,var_listの0番目の要素1iに割り当てる事になる。次の行に進んで,その行の評価が終わると2回目のループが始まり,var_listの1番目の要素2iに割り当てる。そして次の行に進み,その評価が終わると3回目のループが始まり,同じ作業が続く。ループは5回目で終わることになる。

  3. .append()2*imy_listに追加するリストのメソッド(関数と同義であり,後ほど違いを説明する)であり,この行が評価される毎にmy_list2*iが追加されて行くことになる。

このように,forループとは上のコードの2〜3を5回繰り返している。my_listを表示しよう。

print(my_list)
[2, 4, 6, 8, 10]

Note

上の例ではforループの1行目にリストを使って(name_listvar_list),0番目の要素から順に関数・メソッドを使った。これはリストが要素を1つずつ返すことができる反復可能なオブジェクトであるため可能となる。そのようなオブジェクトは**iterableと呼ばれ,タプルや文字列,そして後で説明するNumpyarray`も含まれる。

次の例では文字列を一文字ずつ表示する。

my_string = 'Kobe'    # 文字列を作成しmy_stringに割り当てる

for s in my_string:   # forループの開始
    print(s)          # 文字を表示
K
o
b
e

上で使った例では,3行のコードを書きforループを使いリストを作成した。内包表記(list comprehension)を使うと同じ結果を1行のコード(one linerと呼ばれる)で得ることもできる。例としてvar_listの要素を2倍にするforループをもう一度考えてみる。

[2*i for i in var_list]
[2, 4, 6, 8, 10]

色分けすると,上のforループと内包表記は以下のような対応関係にある。

forループ

my_list = []
for i in val_list:
    my_list.append(expression)

内包表記

my_list = [expression for i in val_list]

forループにif文を組み込むことができる。次の例では,var_listの数字が偶数であれば2倍にして新たなリストを作成する。

my_list = []    # 空のリストの作成

for i in var_list:            # forループの開始
    
    if i % 2 ==0:             # 余りが0の場合
        my_list.append(2*i)   # 二乗してmy_listに追加する
                              # 余りが0でない場合はpass
        
print(my_list)    # my_listの表示
[4, 8]

上の例を内包表記にすることも可能である。

[2*i for i in var_list if i % 2 == 0]
[4, 8]

改行を入れるとより分かりやすくなる。

# コード1

[2*i
 for i in var_list
 if i % 2 == 0]
[4, 8]

次の例では偶数を10倍にし,奇数を1/10にしている。

my_list = []    # 空のリストの作成

for i in var_list:    # forループの開始
    
    if i % 2 ==0:     # 余りが0の場合
        x = 10*i      # 10倍にしてxに割り当てる
    else:             # 余りが0でない場合
        x = i/10      # 10分の1にしてxに割り当てる
        
    my_list.append(x) # xをmy_listに追加
        
print(my_list)    # my_listの表示
[0.1, 20, 0.3, 40, 0.5]

内包表記を使うと次のようになる。

# コード2

[10*i
 if i%2 == 0
 else i/10
 for i in var_list]
[0.1, 20, 0.3, 40, 0.5]

コード1とコード2でif文の位置が異なることに注意しよう。コード1ではif文はforの後にくるが,コード2ではforの前に来ている。

内包表記の応用例として,次のリストの要素抽出について考えてみよう。

lst = [10, 11, 12, 13, 14, 15, 16, 17, 18, 19] 

新たに02, 7, 9番目の要素からなるリストを作成したいとする。この場合,

[lst[0],lst[2],lst[7],lst[9]]
[10, 12, 17, 19]

とすることも可能だが,内包表記を使うと次のコードになる。

idx = [0,2,7,9]
[lst[i] for i in idx]
[10, 12, 17, 19]

何番目の要素にアクセスしているのかはidxで確かめる方が見やすい事がわかる。

while ループ#

forループと同様,whileループもコードを複数回実行したい場合に使う。2つは以下の点で違いがある。

  • forループは与えられたリストなどのiterableの要素数の数だけループが実行される。

  • whileループはある条件が満たされるまでループが実行される。

例として,上で定義したname_listを表示するループを考える。

i = 0 # iの値を0に設定
while i <= 4:
    print(name_list[i])
    i = i + 1
太郎
次郎
三郎
四郎
五郎

説明と注意点

  • whileがある一行目は:で終わる。

  • iは条件を表すカウンターの役割とname_listの要素のインデックスの役割がある

  • 1行目の意味:\(i<=4\)の条件が満たされ散る限り,下に続くコードを反復実行する(タイプする文字数を少なくするために\(i<5\)と書く場合が多い

  • 2行目の意味:name_listi番目の要素を表示

  • 3行目の意味:カウンターiの値を1増やす

    • 先に右辺を評価した後,それを左辺のiに割り当てる考える。(方程式としては考えない!)

  • 2行目と3行目は4つの半角スペースがインデントとして入る。

  • whileループは無限ループになる場合があるので注意しよう。

関数#

コードを書くうえで関数は非常に重要な役割を果たす。Pythonには組み込み関数(事前に準備された関数; built-in functions)が数多くあり,上で使ったprint()type() も組み込み関数である。組み込み関数は様々なものが存在するが、ここではプログラマー自身が作成する関数について説明する。

(注意)

関数には引数が設定されるが(省略される場合もある)、複数の種類がある。ここでは基本となる引数のみを考えるが,引数の位置=が重要な役割を果たすことになる。

最初の例は数字の2乗を計算する関数である。

def func_0(x):
    return x**2

説明:

  • 1行目:

    • defで始まり(defはdefinitionの省略形):で終わる。

    • func_0が関数名,xが第1引数(ひきすう)であり唯一の引数である。

  • 2行目:

    • returnは評価した値を「返す」という意味。必ずreturnの前には4つの半角スペースが必要である。

    • x**2という返り値(戻り値)の設定をする

関数を評価するには,引数に数字を入れて実行する。

func_0(2)
4

(注意)

  • 関数を定義する場合の引数(上の例ではx)は「仮引数」(parameter)と呼ぶ。

  • 関数を評価する際に関数に引き渡す引数(上の例では2)は「実引数」(argument)と呼ぶ。

  • 本書では両方を「引数」と呼んでいる。


引数が無い関数を定義することを可能である。

def func_kobe():
    return 'I love Kobe :)'
    

func_kobe()
'I love Kobe :)'

引数の位置が重要なケース#

上の例では引数が1つしかないが,引数が複数ある場合にその位置が重要になってくる。次の例を考えよう。

def func_1(a, b):
    return a/b
func_1(10, 2)
5.0

aが第1引数、bが第2引数である。引数の順番を間違えると意図しない結果につながる。

func_1(2, 10)
0.2

もちろん,3つ以上の位置引数も設定可能である。次の例で使うsum()は,数字の合計を計算する関数であり,()の中にはタプルやリストが入る。即ち,タプルやリストの要素の合計を返す関数となる。

def func_2(a, b, c, d):
    return sum((a, b, c, d))
func_2(10, 20, 30, 40)
100

実行する際に=を使う#

関数を実行する際に、引数に=を使って値を指定することも可能である。

func_1(a=10, b=2)
5.0

この場合,引数の順番を変えることが可能となる。それが関数を実行する際に=を使う利点である。

func_1(b=2, a=10)
5.0

この場合,全ての引数に=を使わないとエラーとなる。

func_1(10,a=2)
---------------------------------------------------------------------------
TypeError                                 Traceback (most recent call last)
Cell In[115], line 1
----> 1 func_1(10,a=2)

TypeError: func_1() got multiple values for argument 'a'

理由は最初の10aの値と解釈されるためである。一方で,引数の位置が揃っていれば,全ての引数に=を付ける必要はない。

func_2(10, 20, d=40, c=30)
100

ここでも引数の位置が重要な役割を果たしている。

定義する際に=を使う#

関数を定義する際=を使って引数のデフォルトの値を設定することができる。即ち,引数を入力すると入力された数値を使うが,引数を入力しない場合は引数を予め設定した値(デフォルトの値)が使われて評価される。次の例ではcのデフォルトの値が10に設定されている。

def func_3(a, b, c=10):
    return sum([a,b])*c

cの値を与えずに評価してみる。

func_3(2, 3)
50

次にcにデフォルトと異なる値を設定してみる。

func_3(2, 3, 100)
500

(Note)

  • 関数を実行する際に=無しで関数に渡される引数は,その位置が重要であるため「位置引数」と呼ばれる。

  • 関数を実行する際に=付きで関数に渡される引数は「キーワード引数」と呼ばれる。

*args#

func_0()の引数の数は1に,func_1()の引数は2に,func_2()の引数は4に設定されている。関数を実行する際,引数の数が指定された数と合わないとエラーが発生する。では,位置引数の数が事前に決まっていない場合はどうするのか。例えば,func_2(1,2)と書いてもfunc_2(1,2,3,4,5,6)と書いても引数の合計を計算したいとしよう。このような場合に*argsを使う。

def func_4(*args):
    return sum(args)
func_4(1,2)
3
func_4(1,2,3,4,5,6)
21

*argsは何を返しているのだろうか。実は,argsはタプルを返し,*argsはタプルから取り出した全ての要素を返している。これを理解するために次の例を考えよう。

print((1,2,3))
(1, 2, 3)

ここではタプル(1,2,3)を表示している。次に、*を加えてみる。

print(*(1,2,3))
1 2 3

これは次のコードの返り値と同じである。

print(1,2,3)
1 2 3

即ち、*はタプル(1,2,3)の全ての要素を取り出して1,2,3に置き換えている。このような置き換えを「展開する」(unpack)と呼ぶ。この*の役割を念頭に次の関数を使い*argsの役割を分けて考えてみる。

def func_5(*args):
    print(args)

func_5(1,2,3)
(1, 2, 3)

args自体は引数のタプルであることがわかる。次にprint()args*を加える。

def func_5(*args):
    print(*args)

func_5(1,2,3)
1 2 3

ここから分かることは、func_5(*args)args自体はタプルであり,*argsはタプルを展開したものである。print(*args)*argsもタプルを展開したものである。このことを念頭にもう一度func_4(*args)を考えてみよう。def func_4(*args)のargsは引数になる数字のタプル(例えば,(1,2,3))であり,*argsはそれを展開したものである。return sum(args)argsはもちろんタプルである。この性質を使うことにより、関数に任意の数の引数を指定することが可能となる。

<コメント>

  • *argsargsはarguments(引数)の省略形である。

  • *argsの代わりに*aaa*abcとしても同じ結果をえることができる。しかし*argsと書くのが慣例である。

次の例は*argsと引数を一緒に使うケースである。

def func_6(a, *args):
    return a*sum(args)
func_6(10,2,2,2)
60

<注意>

必ず*argsは位置引数の後に置くこと。これは*argsが「その他全ての位置引数」という意味であるためであり、全ての位置引数の後に位置してこそ意味があるからである。言い換えると、*argsは位置引数の終わりを意味する。

lambda関数#

上の例ではdefを使う方法を紹介したが,複雑な関数を定義する場合によく使われる。一方で単純な関数の場合,より簡単な方法がある。それがlambda関数である。例として,\(x^2\)を計算する関数を考えよう。

func_7 = lambda a: a**2

func_7(2)
4

複数の引数の場合

func_8 = lambda a, b: a*b
func_8(2,3)
6

引数が無い場合

func_9 = lambda : 'I love Kobe ;)'
func_9()
'I love Kobe ;)'

関数とメソッド#

Pythonには「関数(functions)」と「メソッド(methods)」があり,基本的には同じ働きをする。ではどう異なるのか。これを理解するためには,数字の1を含めて「全てがオブジェクト」というPythonの根底にある考え方を思い出そう。簡単に説明すると,オブジェクトにもともと備わっているかどうかで「関数」と「メソッド」に区別されると思って十分である。

例えば、上で定義したfunc_0()func_7()は関数であり,特定のオブジェクトに働きかけるものではない。単に引数から計算した返り値を出力しており,計算できる引数である限りどのようなオブジェクトでも構わない。一方,メソッドは元々オブジェクトに備わっている関数である。例として,文字列I love 神戸!を考えよう。I love 神戸!というオブジェクトには様々なメソッドが用意されており,そのリストをdir()という関数を使うことにより表示できる。

moji = 'I love 神戸!'
dir(moji)
['__add__',
 '__class__',
 '__contains__',
 '__delattr__',
 '__dir__',
 '__doc__',
 '__eq__',
 '__format__',
 '__ge__',
 '__getattribute__',
 '__getitem__',
 '__getnewargs__',
 '__gt__',
 '__hash__',
 '__init__',
 '__init_subclass__',
 '__iter__',
 '__le__',
 '__len__',
 '__lt__',
 '__mod__',
 '__mul__',
 '__ne__',
 '__new__',
 '__reduce__',
 '__reduce_ex__',
 '__repr__',
 '__rmod__',
 '__rmul__',
 '__setattr__',
 '__sizeof__',
 '__str__',
 '__subclasshook__',
 'capitalize',
 'casefold',
 'center',
 'count',
 'encode',
 'endswith',
 'expandtabs',
 'find',
 'format',
 'format_map',
 'index',
 'isalnum',
 'isalpha',
 'isascii',
 'isdecimal',
 'isdigit',
 'isidentifier',
 'islower',
 'isnumeric',
 'isprintable',
 'isspace',
 'istitle',
 'isupper',
 'join',
 'ljust',
 'lower',
 'lstrip',
 'maketrans',
 'partition',
 'removeprefix',
 'removesuffix',
 'replace',
 'rfind',
 'rindex',
 'rjust',
 'rpartition',
 'rsplit',
 'rstrip',
 'split',
 'splitlines',
 'startswith',
 'strip',
 'swapcase',
 'title',
 'translate',
 'upper',
 'zfill']

アルファベット順に並んでいることが分かる。例として,upperを使ってみる。

moji.upper()
'I LOVE 神戸!'

upperはアルファベットを大文字に変換するメソッドであり,mojiにもともと備わっている関数である。

Note

  • mojiの後に.upper()が来ており()に引数がないように見えるが,実は先にあるmojiを引数としてupper()を実行しているのである。

  • upperはメソッドの名前であって,実行するには()が必要となる。つけ忘れるとオブジェクトの型が表示されることになる。これは関数と同じである。

_はアンダースコア(underscore)と呼ぶが,2つ連続した場合__となりダブル・アンダースコア(double underscore)と呼ぶ。長いのでダンダー(dunder)と省略する場合が多々ある。上のリストにはこのダンダーに挟まれたメソッドが含まれている(ダンダー・メソッドと呼ばれる)。これらは対応するコードを実行するとPythonが裏で使うメソッドであるため,直接コードに書く必要はない。例えば,次のコードを考えてみよう。

moji + ' (^o^)/'
'I love 神戸! (^o^)/'

+を使い文字列を結合しているが,この裏でPythonが使っているメソッドが__add__である。

moji.__add__(' (^o^)/')
'I love 神戸! (^o^)/'

+__add__の便利な省略形を考えれば良いだろう。「全てがオブジェクト」なのでリストもそうである。

list_0 = [4,3,9,0,1]
dir(list_0)
['__add__',
 '__class__',
 '__class_getitem__',
 '__contains__',
 '__delattr__',
 '__delitem__',
 '__dir__',
 '__doc__',
 '__eq__',
 '__format__',
 '__ge__',
 '__getattribute__',
 '__getitem__',
 '__gt__',
 '__hash__',
 '__iadd__',
 '__imul__',
 '__init__',
 '__init_subclass__',
 '__iter__',
 '__le__',
 '__len__',
 '__lt__',
 '__mul__',
 '__ne__',
 '__new__',
 '__reduce__',
 '__reduce_ex__',
 '__repr__',
 '__reversed__',
 '__rmul__',
 '__setattr__',
 '__setitem__',
 '__sizeof__',
 '__str__',
 '__subclasshook__',
 'append',
 'clear',
 'copy',
 'count',
 'extend',
 'index',
 'insert',
 'pop',
 'remove',
 'reverse',
 'sort']

この中にappendとあるが,forループの説明で使ったメソッドである。

list_0.append(100)
list_0
[4, 3, 9, 0, 1, 100]

他にsortとあるがこれは要素を昇順に並び替えるメソッドである。

list_0.sort()
list_0
[0, 1, 3, 4, 9, 100]

是非他のメソッドも試して欲しい。またオブジェクトのメソッドを調べる場合,dir()の出力は見にくいので,py4macroモジュールに含まれるsee()関数を使うのがおすすめである(モジュールについては次のセクションを参照)。もちろん,Pythonやモジュールの説明書(docs)をチェックするのも必要である。

パッケージとモジュール#

Pythonには組み込み関数が多く用意されている。例えば,このリンクを参照。しかし組み込み関数には計量経済学用の便利な関数は用意されていない。そこで活躍するのがモジュール(modules)やパッケージ(package)と呼ばれるもである。もちろん計量経済学以外のモジュールやパッケージが無数にあり,使う用途(例えば,グラフを描く)に沿って読み込むことになる。2つの違いを簡単にいうと

  • モジュールは1つのファイル(.py)にまとめられた関数群であり,

  • パッケージは複数つのファイル(.py)で構成され,フォルダーにまとめられているもの

となる。従って,モジュール全体を読み込んだり,あるパッケージの1つのモジュールだけを読み込むということも可能である。

まず例としてmathモジュールを考える。名前が示すように数学用のモジュールである。使うためにはimportを使って読み込む必要がある。

モジュールの全てを読み込むとモジュール内の全ての関数が使用可能となる。

import math

上のコードを実行すると,mathモジュールに含まれる全ての関数などを読み込むことになる。

Hint

通常,モジュールやパッケージをインポートする上のようなコードは,ファイルの一番上に書くのが慣例となっているので,それに従うことにしよう!

math.sqrt(4)    # sqrt()とはルート
2.0

この場合モジュール名が必要となり,「mathモジュールのsqrt」を指定しているという意味である。これは他のモジュールとバッティングしないようにするためである。

モジュール名が長い場合は,短い名前で読み込むことも可能である。

import math as m
m.sqrt(9)
3.0

モジュール内の特定の関数だけを読み込むことも可能である。

from math import sqrt, log   # logは自然対数で, sqrtの両方を読み込む

mathモジュールからsqrtlogを読み込む」と理解すれば良いだろう。

sqrt(10)
3.1622776601683795

この読み込み方法の利点はモジュール名を省略できることである。しかし他のパッケージやモジュールと同じ関数がある場合は,後でimportしたものが優先されるので注意が必要だ。

import文はファイルの最初に書き,どのモジュールが導入されるかを明示的に示すとわかりやすいだろう。

前のセクションでdir()を使いオブジェクトの属性を調べたが,その際py4macroモジュールのsee()関数を使うとリストが見やすくなると説明した。それを使ってみよう。まず,モジュールをインポートする。

import py4macro

使ってみよう。

py4macro.see(moji)
.capitalize         .casefold           .center             .count
.encode             .endswith           .expandtabs         .find
.format             .format_map         .index              .isalnum
.isalpha            .isascii            .isdecimal          .isdigit
.isidentifier       .islower            .isnumeric          .isprintable
.isspace            .istitle            .isupper            .join
.ljust              .lower              .lstrip             .maketrans
.partition          .removeprefix       .removesuffix       .replace
.rfind              .rindex             .rjust              .rpartition
.rsplit             .rstrip             .split              .splitlines
.startswith         .strip              .swapcase           .title
.translate          .upper              .zfill

このコードは,「.」を「の」と読んで,「py4macrosee()関数」と読むことができる。see()関数を使うと,ダンダー・メソッドは省略され,メソッドには()が追加され見やすくなっている。

Note

py4macroモジュールはAnacondaに含まれていないので,TerminalもしくはGit Bashで以下を実行して事前にインストールする必要がある。

$ pip install py4macro

よく使う組み込み関数#

# 表示
print('私は神戸大学の学生です。')
私は神戸大学の学生です。
# 様々なものからリストを返す関数
list()  # 空のリスト
[]
# 0から9までの整数を用意する関数 
range(10)
range(0, 10)
list(range(0,10))
[0, 1, 2, 3, 4, 5, 6, 7, 8, 9]

エラー#

エラー(Errors)は以下の2つに分けられる。

  1. 構文エラー(Syntax Errors)

    • 構文自体が間違っている場合に発生するエラー(例えば,スペル間違い)。

  2. 例外(Exceptoins)

    • 構文は間違っていなくてもコードの実行中に発生するエラー(例えば,数字を0で割る)


<<コメント>>

  • エラーが発生するとエラー・メッセージが表示されるが,多くの場合,エラー・メッセージにエラーの理由のヒントがあるので確認することを強く推奨する。はじめは意味が分からないかも知れないが,パターンがあるので慣れると直ぐに理解できるケースも多くあるだろう。

  • 例外の場合,最初にエラーが発生するとそれに付随して他の場所でもエラーが誘発される場合がある。Pythonはエラーを追跡し,最後に確認したエラーをメッセージの一番最後に表示するので覚えておこう。

構文エラー#

例1#

(正)print
(誤)primt

primt('hello')
---------------------------------------------------------------------------
NameError                                 Traceback (most recent call last)
Cell In[157], line 1
----> 1 primt('hello')

NameError: name 'primt' is not defined
  • 矢印(—->)でエラー箇所が示されている。

  • NameErrorとして最終行にスペル間違いであるprimtが示されている。

例2#

次のセルの3行目の終わりに:が抜けている。

a = 3

for i in range(0,3)
    print(i)
  Cell In[158], line 3
    for i in range(0,3)
                       ^
SyntaxError: expected ':'
  • line 3はセル内の3行目を示している。

  • SyntaxErrorとして最後の行で:が足りない箇所を^で指し示している。

例3#

括弧を閉じていない。

(2.1 + 1.5)/(10.0 + 1.0 + 3.2 
  Cell In[159], line 1
    (2.1 + 1.5)/(10.0 + 1.0 + 3.2
                                  ^
SyntaxError: incomplete input

この場合,Pythonはプログラマーがどこに)を入れようとしたかは分からない。従って,最後に)が入ると想定して^を文末に置いている。


<<コメント1>>
()の場合,改行しても構わない。一行が長くなる場合,()の間で改行することができる。

(10, 20, 30,
 40, 50, 50)
(10, 20, 30, 40, 50, 50)

<<コメント2>>
文字列を改行する場合は次の例のように\を使う。

'神戸大学\
経済学部'
'神戸大学経済学部'

例外#

例1#

0が分母にある。

2.0 + 1/0
---------------------------------------------------------------------------
ZeroDivisionError                         Traceback (most recent call last)
Cell In[162], line 1
----> 1 2.0 + 1/0

ZeroDivisionError: division by zero

例2#

定義されていない変数xxが使われている。

10 + xx * 2
---------------------------------------------------------------------------
NameError                                 Traceback (most recent call last)
Cell In[163], line 1
----> 1 10 + xx * 2

NameError: name 'xx' is not defined

例3#

文字列とfloatを足している。

'3' + 10
---------------------------------------------------------------------------
TypeError                                 Traceback (most recent call last)
Cell In[164], line 1
----> 1 '3' + 10

TypeError: can only concatenate str (not "int") to str

ヘルプ#

組み込み関数help()を使うと関数やモジュールなどのDocstringと呼ばれる説明を表示させることができる。例えば,print()を例として挙げる。

help(print)
Help on built-in function print in module builtins:

print(...)
    print(value, ..., sep=' ', end='\n', file=sys.stdout, flush=False)
    
    Prints the values to a stream, or to sys.stdout by default.
    Optional keyword arguments:
    file:  a file-like object (stream); defaults to the current sys.stdout.
    sep:   string inserted between values, default a space.
    end:   string appended after the last value, default a newline.
    flush: whether to forcibly flush the stream.

引数は関数名であり()は付いていないことに注意しよう。()を付けるとprint()を評価した結果に対しての説明が表示されることになる。英語での説明だがパターンを理解すればこれだけでも有用に感じることだろう。

help()の代わりに?を使うこともできる。

print?

Pythonの基本のまとめ(日英対訳)#

日本語

英語