関数,文字列,リスト,タプル,辞書,クラス,内包表記,エラー処理,モジュール

関数

プログラムが長くなると,部品を作って何度も再利用したくなる.

Pythonでは,そのようなプログラムの部品のことを関数(function)とよび,def で定義する.

基本的な構文は以下の通り.

def 関数名(引数:

  関数本体

  return 返値

関数は入力を出力に変換する.

入力 => 関数 => 出力

入力を「引数(ひきすう)」,出力を「返値(かえりち)」と呼ぶ.

return文はなくても良い. その場合には返値はなしになり, Noneという特別なオブジェクトが返される.

● 例題7-1

整数nを引数として受け取り, print(“#”, end='') を n回繰り返し,長さnの#でできた棒を表示する関数を作れ.

例えば,10 を渡すと

##########

が表示され,25を渡すと,

#########################

が表示されるような関数である.

def sub(n):
    for i in range(n):
        print("#", end="")


sub(25)
#########################

● 例題7-2

2つの数字を引数とし,その和を返値として返す関数を記述し,その関数を用いて 3と5の和を計算せよ.また,100と200の和も計算せよ.

複数の引数はカンマで区切って関数に渡す. 関数の値は返値になる.

def computeSum(x, y):
    return x + y


print(computeSum(3, 5))
print(computeSum(100, 200))
8
300

● 例題7-3(再帰)

フィボナッチ数 $F_n$ は

$$ F_1 =1 $$$$ F_2 =1 $$$$ F_n= F_{n-1} +F_{n-2} $$

と再帰的に定義される数列である. $F_5$ を計算せよ.

これは関数 $F$ の中で同じ関数 $F$ を呼び出す再帰的なプログラムで記述する.

def F(n):
    if n == 1 or n == 2:
        return 1
    else:
        return F(n - 1) + F(n - 2)


print(F(5))
5

● 例題7-4(再帰による組合せの列挙) (難)

「1円玉1枚,10円玉1枚,100円玉1枚の組合せでできる8通りの金額を全て表示する」というプログラムを,再帰呼び出しを使って作ろう.

まず第1ステップとして,1円,10円,100円の全て使った組合せの金額を,「再帰呼び出し」を使って計算するプログラムを作る(単に足せば良いが,再帰を使って計算する).

計算を行うための関数payの引数は,使うかどうか試したコインの数(i),合計を記録する補助変数(total) とリスト coin = [1, 10, 100] である.

再帰の終了条件は,すべてのコインを使い終わったら(つまりiが3になったら)合計金額を出力する.

合計金額は,コイン数を1増やし(iを1増やし),合計金額totalを coin[i] だけ増やして,payを再帰的に呼ぶことによって計算できる.

def pay(i, total, coin):
    if i == len(coin):
        print("total=", total)
    else:
        pay(i + 1, total + coin[i], coin)


coin = [1, 10, 100]
pay(0, 0, coin)
total= 111

次に,全ての組合せを列挙するプログラムを作る.

全ての組合せの金額を表示するには,各硬貨について,「その硬貨を使う場合」と「その硬貨を使わない場合」の場合分けが必要である.

そこで,i番目の硬貨を使う組合せ,使わない組合せに対応した再帰呼び出しを行う. 以下ように8通りの支払い金額が列挙できる.

def pay(i, total, coin):
    if i == len(coin):
        print("total=", total)
    else:
        pay(i + 1, total, coin)  # コイン iを使わない場合
        pay(i + 1, total + coin[i], coin)  # コイン iを使う場合


coin = [1, 10, 100]
pay(0, 0, coin)
total= 0
total= 100
total= 10
total= 110
total= 1
total= 101
total= 11
total= 111

● 問題 7-1

3つの整数を引数として渡すと,その積を返す関数を記述せよ.その関数を用いて,$2*5*7$ と $123*456*234$ を計算せよ.

●問題 7-2

例題7-1の関数を,入力した3つの数を引数として3回呼び出す関数を作れ. 例えば8,13,4と引数としたら,

########
#############
####

と棒グラフを表示する関数である.

●問題 7-3

例題7-1の関数を使って,#でできた三角形を表示するプログラムを作れ. 例えば,このようなものを表示するプログラムである.

#
##
###
####
#####
######

●問題7-4

数aを渡すと,aが正なら1を,負なら -1を,0ならば0を返す関数を作れ.

●問題7-5

数aを受け取り,数bをキーボードから入力し,a+bの値を返す関数を作れ.

次に,この関数を使って,数を10個入力してその合計を表示するプログラムを作れ.

●問題7-6

$n$ の階乗 $n!$ は

$ 1! =1 \\ n!= n \times (n-1)! $

と再帰的に定義される.$n!$ を再帰的に計算する関数 factorial(n) を記述し,$5!$ を計算せよ.

●問題7-7

2つの整数mとn (ただし,m>nで正を仮定) が与えられたとき,mとnの最大公約数は,nとm%n (mをnで割った余り)の最大公約数になる. ただしnが$0$のときには,最大公約数はmと定義する. これを利用して,入力した2つの整数の最大公約数を求めるプログラムを,再帰呼び出しを使って作れ.

●問題7-8 (難)

回文(palindrome)とは前から読んでも後ろから読んでも同じ文字列を指す.引数として与えた文字列が回文であるか否かを判定する関数を,再帰を用いて記述せよ.例として 'たけやぶやけた' と 'たけたぶやけてない' を入れて試せ.

(ヒント:文字列 s の最初の文字はs[0],最後の文字はs[-1], 最初と最後を除いた文字列は s[1:-1] である.)

文字列の操作

文字列は' ' もしくは " "もしくは''' '''もしくは""" """で挟むと生成できる. 文字列は s ='Hello'は H e l l o が順番に箱に入ってると考えれば良い. 前から順番に 0,1,2, ... と番号付けがされている. これを添え字とかインデックスと呼ぶ. 添え字はrange関数と同じように操作できる.

文字 'H'を取り出すには,s[0] とする.

文字 'o' を取り出すには s[4] とする.

文字の範囲はスライス表記 [開始番号: 終了番号 -1] で取り出すことができる. たとえば,文字列 'He' を取り出すには,s[0:2] とする.

スライス表記の開始番号と終了番号は省略できる. 省略すると開始番号は 0 ,終了番号は文字列の長さ len(文字列) になる.

また後ろから番号付けも可能で,最後の文字列の番号は -1,その前の文字列は -2 となる.

$0,2,4$ の添え字の部分から成る文字列 'Hlo' を取り出すには,スライス表記のステップに $2$ を使って s[::2] とする. 開始番号と終了番号を省略したので,最初から最後まで $2$ つ跳びで取り出していることに注意せよ.

●例題8-1

文字列 s = 'Hello World' から'World'を取り出せ.

s = "Hello World"
s[6:]
'World'

文字列は加算演算子 $+$ で結合することができる. たとえば,

'Hello' + 'World' は 'HelloWorld' になる (空白は挿入されない).

また乗算演算子 $*$ も使うことができる. たとえば, $3*$ 'Hello' とすると 'Hello' +'Hello' +'Hello' と同じ結果が得られる.

●例題8-2

5回 "I'm sorry!" と出力せよ. (文字列の中に'記号を入れたいときには, '' ではなく " "で囲む.)

print(5 * "I'm sorry!")
I'm sorry!I'm sorry!I'm sorry!I'm sorry!I'm sorry!

for文を用いた反復はrange関数によって生成された数列だけでなく,文字列に対しても使える.

たとえば,

for x in 'Hello': 
   print( x )

と書くと,H, e, l, l, o が順番に変数 x に代入されて,print関数で出力される.

●例題8-3

文字列 s='Kitty' の各文字の後ろに '!' を入れた文字列 x を出力せよ.

s = "Kitty"
x = ""
for i in s:
    x = x + i + "!"
print(x)
K!i!t!t!y!

●問題8-1

文字列を入力し,最初と最後が同じ文字なら 'Same!' と出力するプログラムを作れ.

●問題8-2

文字列を入力し,その中に 'a' が含まれているときに 'In!' と出力するプログラムを作れ (実は in という演算子があるので「 'a' in 文字列」で簡単に 'a'を含むかどうかの判定はできるが,ここでは反復を用いて作成せよ.)

●問題8-3

文字列を引数とすると,各文字の間と最後に空白を入れた文字列を返値として返する関数を作れ. さらに,文字列を入力し,その関数を呼び出して空白を入れた文字を出力するプログラムを書け.

● 問題8-4

p='Pen', P='Pineapple', a='Apple' を用いて 'PenPineappleApplePen'という文字列を作れ. また,それを逆から並べた'nePelppAelppaeniPneP'とう文字列を作るプログラムを作成せよ.

● 問題8-4

以下の変数 p, P, a, p の最初の文字(たとえばpなら'P')を連結した文字列を生成せよ.

p = "Pen"
P = "Pineapple"
a = "Apple"

リスト

Pythonではオブジェクトを保管するための幾つかの便利な機能が(最初から)準備されている.

ここではその代表であるリストの使い方を説明する.

文字列が文字を箱に順番にしまっていたのと同じように,リストは何でも(Pythonではオブジェクトといいます)箱にしまっておくことができる.

リストは[ ]の中に ,(カンマ)で区切ってものをしまいます.

L = [ 1, 2, 3, 4]               # range(1,5)と同じように数列を保管
L = ['H', 'e', 'l', 'l', 'o']   # 文字列 'Hello' 同じようにう文字を保管
L = [ 'AAA', 235, ['BBB',1.3] ] # 文字列,整数,リストを保管

リストに対しても文字列と同じように,足し算,乗算,添え字(インデックス)による抽出や切り出し,for文による反復を行うことができる.

リストは文字列と違って可変(mutable)である. (ちなみに,文字列は不変(immutable)と呼ばれる). つまり中身を変えることができる.

●例題9-1

リスト L=[6, 5, 4, 3] の3番目の数字(Pythonでは0番から始まるので添え字2の数字)を'Hello'に変えよ.

L = [6, 5, 4, 3]
L[2] = "Hello"
print(L)
[6, 5, 'Hello', 3]

●例題9-2

長さ 10の変数のリスト $a$ を用意し,0番目に0,1番目に10,2番目に20,…と代入し,最後にそれらを表示する,というプログラムを作成したい.

iが0から9まで1ずつ大きくなるforループを作り,a[i]に順々に数値を代入すれば良い.

a[0]には0,a[1]には10,a[2]には20,...と代入したいので,a[i]に $i*10$ を代入する.

たとえば,以下のプログラムは,最初に0を入れたリストを10倍することによって,[0,0,0,0,0,0,0,0,0,0]というリストを準備をし, 次のforループで数字の代入を行えば良い.

a = 10 * [0]
for i in range(10):
    a[i] = i * 10
a
[0, 10, 20, 30, 40, 50, 60, 70, 80, 90]

リストはいろいろな メソッド を適用できる.ここで,メソッドというのはリストに適用される関数のことである.

リストの後に . (ピリオド)を打った後に,「メソッド名(引数)」と書くと,関数のように出力が返ってくる.

たとえば,リストの最後に要素を追加するための append メソッドがある.L.append( 'a' ) と書くと,リスト L の最後に文字列 'a' が追加される.

上の例題9.2は,最初に空のリストを準備して,1つずつ数字をappendメソッドで追加していく方法でも解くことができる.

a = []
for i in range(10):
    a.append(i * 10)
a
[0, 10, 20, 30, 40, 50, 60, 70, 80, 90]

例題9.2では,[0,0,...,0] のリストを準備するために $10 *[0]$ と乗算を用いた.

これは以下のリスト内包表記と同じだが,リスト内包表記の方が推奨される.(内包表記については,後で詳しく説明する.)

a=[0 for i in range(10)]

これは for i in range(10) で iを0から9まで変えた反復を行い,反復ごとに 0 を入れたリストを生成する,という意味である.

上のリスト内包表記の 0 のかわりに $i*10$ と書けば問題は 1行で解ける.

a = [i * 10 for i in range(10)]
a
[0, 10, 20, 30, 40, 50, 60, 70, 80, 90]

リストの要素を小さい順に並べ替えたいときには,sort()メソッドを使う. たとえば,

L =[ 6, 8, 3, 1] 
L.sort()

とすると,Lは [1, 3, 6, 8] と小さい順に並べ変わる.

逆順(大きい順)に並べ替えたいときには,sort()メソッドの引数reverseをTrueにするか, sort()を使った後に,リストを逆順にするメソッドreverse()を使う.

●例題9-3

最初の行にimport randomと宣言した上で,x=random.randint(a,b)と書くことで,xにaからbまでのランダムな整数を代入してくれる.

ランダムに生成した1から100までの整数から成る長さ 10 のリストを作り,それを小さい順,大きい順に並べ替えたリストを出力せよ.

import random

L = []
for i in range(10):
    L.append(random.randint(1, 100))
print("元のリスト ", L)
L.sort()
print("小さい順 ", L)
L.reverse()
print("大きい順 ", L)
元のリスト  [71, 40, 52, 78, 11, 40, 60, 89, 7, 3]
小さい順  [3, 7, 11, 40, 40, 52, 60, 71, 78, 89]
大きい順  [89, 78, 71, 60, 52, 40, 40, 11, 7, 3]

●問題9-1

キーボードから10個の数を入力し,それらを入力された順番と逆の順番で表示するプログラムを作れ.

●問題9-2

まず,長さ10の全部 0 が入ったリストを作れ.

次に,0から9までの整数 a,bを入力し,リストのa番目からb番目までの要素を1大きくする,という作業を3回繰り返すプログラムを作れ.

このとき,毎回リストの0番目から9番目までを表示せよ.

(ヒント: iをaからbまでの1ずつ大きくするforループを使い,リストのi番目を1大きくする.)

●問題9-3

リストと整数iを受け取り,リストのi番目の値を1大きくする関数を作れ.また,この関数を試すプログラムを作れ.

●問題9-4

“Taro”, “Jiro”,”Saburo”,”Shiro”,”Goro” がこの順番で座っている. この4人を名前を入れたリスト L は

L=[“Taro”, “Jiro”,”Saburo”, ”Shiro”, ”Goro”]

となる.これを参考にして,自分の座っている列にいる友人の名前のリストを作成せよ.(オンライン講義の場合は,適当な友人を適当な順番で並べよ.)

●問題9-5

上で作成したリストに対して,最初に座っている人と最後に座っている人を表示せよ.

また,名前の(アルファベット順の)昇順リストを作成せよ.

●問題9-6

100個の1以上10000以下の整数から成るの一様整数乱数のリストを作成し,次にそれらの和と平均を求めよ.

(ヒント:リスト L の和は関数 sum(L) で,要素数は関数 len(L) で求めることができる.)

また,平均に最も近い数を求め,それが何番目の数字(最初の数字を0番目とする)なのかを計算するプログラムを作成せよ.

(ヒント: 数の絶対値は abs()関数で求めることができる.)

タプル

タプルはリストとほとんど同じであるが,リストと違って中身が変更できない不変型(immutable)である. 文字列と同様に(不変型なので)以下で説明する辞書のキーとして使うことができる.

タプルを生成するには () の中に , で区切って入力する.

T =(6,4,7,'Hello')

タプルはリストと同様に添え字(インデックス)や切り出しができるが,中身を変えることができない.

例題10-1

上のタプルTの4番目の要素(Pythonの添え字は0から始まるので添え字3の要素)を出力せよ.

また,4番目の要素を数字の6に変更してみよ.(エラーが発生するはずだ.)

T = (6, 4, 7, "Hello")
print(T[3])
# T[3]=6   #この文を生かすとエラーが発生する。
Hello

例題10-2

a = 'Hello' と b='World' の2つの変数の中身を1行で取り替えよ.

(aとbから成るタプル (a,b) に (b,a)を代入すると1行で取り替えることができる.

また,() は省略して書くこともできる. ただし1つの要素から成るタプルの場合には省略できず,(a,) とカンマ , を入れて書く必要がある. )

a = "Hello"
b = "World"
a, b = b, a
print(a, b)
World Hello

問題10-1

変数 a,b,c に代入された文字列を変数 x,y,z に代入せよ(ただし1行で).

a = 'Mickey' 
b = 'Minny'
c = 'Kitty'

辞書

リストやタプルの他に辞書という便利な機能もある. 辞書は,キー を入れると (あたい)が返ってくる写像(マップ)である. 順番は任意なのでマップ型と呼ばれる. (ちなみに, リストやタプルは順番に保管するので,順序型と呼ばれる.)

これは通常の辞書で,"Hello" をひくと「こんにちわ」が書いてあるのと似ている.

辞書は {} をの中に「キー:値」を , (カンマ)で区切って入れる.英語を日本語に変換する辞書を作ってみよう.

D ={ 'Hello' : 'こんにちわ',  'Good Morning': 'おはよう', 'Good Night': 'おやすみ' }

D['Good Morning']

最初の行で辞書 D を作り,次の行で辞書Dからキー'Good Morning'を引きます.これは対応する値 'おはよう' を返す.

D = {"Hello": "こんにちわ", "Good Morning": "おはよう", "Good Night": "おやすみ"}
D["Hello"]
'こんにちわ'

3人の女の子の身長を入れた辞書を準備する.

D={ 'Mary': 126, 'Jane': 156, 'Sara': 170}

キーのリストは D.keys() で, 値のリストは D.values() で得ることができる.

キーになれるのは不変型(immutable)だけである. したがって,リストや辞書や(後述する集合)などの可変型(mutable)のオブジェクトはキーにはなれない.

キーには数字,文字列,タプルなどを使う.

D = {"Mary": 126, "Jane": 156, "Sara": 170}
D.values()
dict_values([126, 156, 170])

値は任意のオブジェクトを入れることができる.リストや辞書でも何でも入れることができる.

たとえば,値に身長と体重のリストを入れた辞書は以下のようになる.

D={ 'Mary': [126,30], 'Jane': [156,55], 'Sara': [170,60]}

辞書 D の中にキーがあるかどうかは「キー in D」とin 演算子を用いて判定することができる.

たとえば,上で作った身長の辞書Dに対して

  'Kitty' in D

は False を返し,

  'Mary' in D

は True を返す.

D = {"Mary": [126, 30], "Jane": [156, 55], "Sara": [170, 60]}
D["Taro"] = [190, 200]
D["Jane"] = [180, 55]
for x in D:
    print(x, D[x])
Mary [126, 30]
Jane [180, 55]
Sara [170, 60]
Taro [190, 200]

辞書に新しい要素を追加したり,値を変更したいときは,単に「D[キー]=値」と書く. たとえば,身長が 10cmの'Kitty'を追加するには

D['Kitty'] =10

と書き,'Jane'の身長を180に変更したいときには,

D['Jane'] =180

と書く.

辞書 D に対してもfor文を使うことができる. 構文は

for x in D:
    反復内の処理

である. このとき x に代入されるのはキーである.

●例題11-1

上で作成した身長の辞書から身長が160cm以上の人の名前を出力せよ.

D = {"Mary": 126, "Jane": 156, "Sara": 170}
for i in D:
    if D[i] >= 160:
        print(i)
Sara

●例題11-2

上で作成した身長の辞書を,小さい順に並べたときの名前を出力せよ.

  1. まず,身長と名前のタプルを入れたリストをfor文を用いて作る.

  2. 次にそれを sort()メソッドで小さい順に並べ替える (並べ替えはタプルの前の要素(この場合には身長)が優先される).

  3. 並べ替えた後のリストをfor文を用いて反復し,名前を順に出力する.

D = {"Mary": 126, "Jane": 156, "Sara": 170}
L = []  # 空のリストを準備しておく
for i in D:
    L.append((D[i], i))  # (身長,名前)のタプルをリストに追加
print("並べる前のリスト ", L)
L.sort()
print("身長順のリスト ", L)
for (height, name) in L:
    print(name)
並べる前のリスト  [(126, 'Mary'), (156, 'Jane'), (170, 'Sara')]
身長順のリスト  [(126, 'Mary'), (156, 'Jane'), (170, 'Sara')]
Mary
Jane
Sara

●例題11-3

辞書に対するfor文はキーを返しますが,辞書.items() とすると,キーと値のタプルを返す.

これを用いて,辞書D={ 'Mary': 126, 'Jane': 156, 'Sara': 170} のキーと値を入れ替えた辞書(身長を入れると名前を返す辞書)を作成せよ.

D = {"Mary": 126, "Jane": 156, "Sara": 170}
Reverse = {}
for key, value in D.items():
    Reverse[value] = key
print(Reverse)
{126: 'Mary', 156: 'Jane', 170: 'Sara'}

●問題11-1

名前をキー,体重を値とした辞書 D を作成せよ.ただし,友人は3人以上記入し, 名前には友人の名前を文字列として,体重には友人の理想体重を整数値として入力せよ.

●問題11-2

起動すると,友人の名前の入力を求め,上で作成した辞書Dから体重を調べて画面上に表示するプログラムを作成せよ.

●問題11-3

上のプログラムを,何度も友人の名前の入力を求めて体重を表示するように変更せよ.ただし,辞書にない名前を入力すると終了するものとする.

●問題11-4

空の辞書Dを準備し,その後で名前と体重の入力を何度もユーザーに要求して, 名前をキー,体重を値としてDに保管していくプログラムを作成せよ. ただし,ユーザー名に空白 ”” を入力すると,画面に作成した辞書Dを表示してから,終了するものとする.

●問題11-5

友人の名前と体重を保管した辞書Dを準備し,その後で,友人の名前と体重の入力を求め,名前が一致する友人がいたならば, 体重を新しい値に変更し,いなければ辞書に追加するプログラムを作成せよ. ただし,ユーザー名に空白 ”” を入力すると,画面に作成した辞書Dを表示してから,終了するものとする.

●問題11-6

問題11-1で作成した友人の名前と体重を保管した辞書Dに対して,友人の体重を名前のアルファベット順に画面に出力するプログラムを作成せよ.

●問題11-7

好きな都道府県を3つ選択した後,都道府県名をキーとし,その名産品(1つ以上入力する)のリストを値とした辞書Dを作成せよ.

また,作成した辞書に対して,名産品の多い順に都道府県名を表示するプログラムを作成せよ.

(ヒント:リストLの長さ(要素数)は関数len(L)で計算できる.)

クラス

Pythonでは,数やリストや辞書などはすべてオブジェクトである. これらのオブジェクトは元から準備されたものなので組み込みオブジェクトと呼ばれる.

Pythonでは,自分で新しいオブジェクトを作ることができる. それがクラス(class)である.

構文は関数と似ていて, 関数を定義するキーワード def の代わりに class と書く.

class クラス名():
    クラス本体

クラスとは,オブジェクトの属性やオブジェクトに対する操作(これをメソッドと呼ぶ)をまとめた雛形(テンプレート)である.

たとえば,ピカチュウというクラスは,「ほっぺた」や「しっぽ」などの属性と,「しっぽを振る」とか「リンゴを食べる」といったメソッドをもつ.

クラスという雛形(テンプレート)から実体を生成することができる. これをインスタンス(instance)とよぶ.

class Pikacyu:  # class Pikacyu: でも良い
    pass  # なにもしない


pika1 = Pikacyu()  # pika1というインスタンスを生成

pika1.color = "Yellow"  # pika1のcolor属性を黄色(yellow)に設定
pika1.color
'Yellow'

pass は何もしないことを表す(何も入れないとエラーするので). クラス名は Pikacyu で,pika1 = Pikacyu()でpika1というインスタンスを生成する.

pika1 というインスタンスには,(たとえば色という)属性 (attribute)をもたせることができる.

属性はインスタンスの後ろに . を書いた後に記述する.

pika1.color = "Yellow"

これでpika1に"Yellow"という色属性をもたせることに成功したことになる.これは,ピカチュウ「の」色という意味で,.は「の」を表すと思えると良い.

次に,インスタンスの動作(たとえば「泣く」とか「食べる」とか)を表すメソッド (method) を記述する. これは,ピカチュウ「が何々する」に相当する.

メソッドは,クラス定義ので関数と同様にdefと書くことで定義できる.

ただし,メソッドの最初の引数には必ず「自分自身」を表すselfを入れる. メソッドの呼び出しの際には,selfは省略される.

クラス内で属性にアクセスするときには,前に自分自身(self)を入れてself.属性名と記述する.

class Pikacyu:
    def cry(self):
        print("Pika!")


pika2 = Pikacyu()
pika2.cry()
Pika!

クラスには__ (アンダースコア2つ)で挟んだ特殊メソッドを定義することができる. ちなみに、__は、double underscoreの略で"dunder(ダンダー)と読む. 例えば、以下で説明する__init__は、「ダンダー・イニット」と読む.

クラスの初期化を行うメソッド__init__を定義すると,インスタンスを最初に生成したときに「自動的に」呼ばれ,インスタンスの初期化を行うことができる. ただし点クラスのインスタンスを生成するときには,selfは書かずに,以下のように記述する.

pika3 = Pikacyu("Satoshi")
class Pikacyu:
    def __init__(self, owner):
        self.name = f"{owner}'s Pikacyu"


pika3 = Pikacyu("Satoshi")
print(pika3.name)
Satoshi's Pikacyu

他にも多くの特殊メソッドが準備されている. たとえば, 文字列を返す__str__を定義すると,クラスのインスタンスをprintしたときに,その文字列が表示されるようになる.

class Pikacyu:
    def __init__(self, owner):
        self.name = f"{owner}'s Pikacyu"

    def __str__(self):
        return self.name + f" whose color is {self.color}"  # 名前と色を返す


pika4 = Pikacyu("Mikio")
pika4.color = "yellow"
print(pika4)  # printだけで名前と色を返す!
Mikio's Pikacyu whose color is yellow

もう1つの例として「点」を表すクラス Point を作ってみよう.

class Point():
    pass

p1=Point()

pass は何もしないことを表す(何も入れないとエラーするので). クラス名は Point で,p1 = Point()でp1というクラスの実体(インスタンスと呼ぶ)を生成する.

p1 という点を表すインスタンスには,x座標とy座標という属性 (attribute)をもたせることができる.

属性はインスタンスの後ろに . を書いた後に記述する.

p1.x = 100
p1.y = 200

これで点p1にx,y座標の属性100,200をもたせることに成功したことになる.

次に,点を移動させるとか,座標をまとめて出力するといったメソッド (method) を記述する.

メソッドは,クラス定義ので関数と同様に def と書くことで定義できる.

ただし,メソッドの最初の引数には必ず自分自身を表すselfを入れる. メソッドの呼び出しの際には,selfは省略される.

クラス内でx属性にアクセスするときには,self.xと記述する.

たとえば,点のインスタンスを生成するときに「必ず」x,y座標を与えて,x,y属性に代入するには,

def __init__(self, x, y):
        self.x=x
        self.y=y

と記述する(最初の引数にselfを入れるのを忘れないよう).

ただし,点クラスのインスタンスを生成するときには,selfは書かずに,以下のように記述する.

p1=Point(100,200)

●例題12-1

点を表すクラス Point を作成し,x座標とy座標を表す属性に100,200を代入せよ

次に,座標の出力を行うメソッドprintMeを用いて座標の出力を行え.

また,座標の移動を行うメソッドmoveを定義し,座標を右に10,上に20移動させてから座標の出力を行え.

class Point:
    def printMe(self):
        print("<", self.x, ",", self.y, ">")

    def move(self, right, up):
        self.x += right
        self.y += up


p1 = Point()
p1.x = 100
p1.y = 200
p1.printMe()
p1.move(10, 20)
p1.printMe()
< 100 , 200 >
< 110 , 220 >

●例題12-2

初期化をするためのメソッド__init__を用いて点を表すクラス Point を作成し,x座標とy座標を表す属性に100,200を代入せよ.

また,文字列を返す__str__を用いて座標の出力を行え.

さらに,他の点インスタンスの座標を加える特殊メソッド__add__(これは + 演算子を適用したときに呼ばれる)を 使って座標 (10,20) をもつ点インスタンスとの足し算をせよ.

class Point:
    def __init__(self, x, y):
        self.x = x
        self.y = y

    def __str__(self):
        return "<" + str(self.x) + ", " + str(self.y) + ">"

    def __add__(self, other):
        return Point(self.x + other.x, self.y + other.y)


p1 = Point(100, 200)
print(p1)
p2 = Point(10, 20)
print(p1 + p2)
<100, 200>
<110, 220>

●問題12-1

Pikacyuクラスを作り, 何かを食べさせるメソッド eat を追加せよ. メソッドは,食べたもの(引数の文字列)に対して f"{引数の文字列} is delicious!" を出力するものとする.

たとえば, eat("Apple") とすると, "Apple is delicios!" を表示するようにせよ.

●問題12-2

コンストラクタで(name)と色 (color) を与えるPikacyuクラスを作り, クラスのインスタンスをprintすると, I'm 名前. My color is 色. と表示するようにせよ.

たとえば,

pika1 = Pikacyu("Pee-kun", "ellow")
print( pika1 )

とすると,

I'm Pee-kun, my color is yellow.

を表示するようにせよ.

●問題12-3

身長と体重の2つの属性をもつHumanクラスを作成せよ.

その後に,「体重/身長の2乗」で定義されるボディマス指数を計算するメソッド bmi を追加せよ. (ただし,体重はキログラム,身長はメートルで入れる.)

次に,以下の表に基づいて,痩せ型から肥満(4度)までのいずれに属するかを文字列として返すメソッド am_i_fat を追加せよ.

                 BMI値
痩せ型         18.5未満
普通体      18.5以上、25未満
肥満           25以上

自分の身長と体重(公表するのが嫌な場合には,友人の身長と体重の推定値)を属性としたHumanのインスタンス me を作成して,bmiメソッドと am_i_fatを呼び出せ.

●問題12-4

例題で作った点を表すクラスを参考にして,ベクトルを表すクラスを定義し,2つのベクトルの内積を乗算 $*$ で行うようなメソッド __mul__を記述せよ.

内包表記

リストのところで内包表記 (comprehension) について簡単に触れたが,ここでは,内包表記を一般的に説明する.

  • リスト内包
  • 集合内包
  • 辞書内包
  • ジェネレータ内包

リスト内包

1から9までの整数のリストを生成するには,リスト[ ] の中にfor文を入れて,以下のように記述する.

a = [i for i in range(1,10)]
print(a) 
>>> 
[1, 2, 3, 4, 5, 6, 7, 8, 9]

if文をfor文の後ろに入れて条件付きで反復することもできる.

a = [i for i in range(10) if i%2==1]
print(a)
>>>
[1, 3, 5, 7, 9]
a = [i * j for i in range(1, 10) for j in range(4)]
print(a)
# a = [i for i in range(10) if i%2==1]
# print(a)
[0, 1, 2, 3, 0, 2, 4, 6, 0, 3, 6, 9, 0, 4, 8, 12, 0, 5, 10, 15, 0, 6, 12, 18, 0, 7, 14, 21, 0, 8, 16, 24, 0, 9, 18, 27]

●問題

20から100の整数で3で割り切れるものだけから成るリストを,リスト内包を用いて生成せよ.

●問題

"aa", "bb", "cc", "dd", "ee" から成るリストを,文字列 s= "abcde" からリスト内包を用いて生成せよ.

集合内包

リストの[ ] のかわりに{ }を用いることによって,集合を生成する集合内包ができる.

たとえば, $i$ が $j$ で割り切れるような $i,j = 1,\ldots,4$ の整数の組の集合は,以下のように生成できる.

S = {(i, j) for i in range(1, 5) for j in range(1, 5) if i % j == 0}
S
{(1, 1), (2, 1), (2, 2), (3, 1), (3, 3), (4, 1), (4, 2), (4, 4)}

●問題

1から20までの偶数の集合を,集合を用いて生成せよ.

辞書内包

{ } 内でfor文を書いて,キー:値を入れることによって辞書を生成できる.これを辞書内包と呼ぶ.

1から10の整数をキーとし,その2乗を値とした辞書は,以下のように辞書内包を用いて生成できる.

{i:i**2  for i in range(1,11)}
> >>
{1:1, 2: 4, 3: 9, 4: 16, 5: 25, 6: 36, 7: 49, 8: 64, 9: 81, 10: 100}

●問題

1から10までの整数をキーとし,"#"をキー個並べた文字列を値とした辞書を,辞書内包を用いて生成せよ.

●問題

辞書 D= {1:"one", 2:"two",3:"three",4:"four" } から,英字の数字文字列("one"とか"two"とか)をキーとし,数字(1とか2とか)を値とした辞書を辞書内包を用いて生成せよ.

ジェネレータ内包

リストの[],集合や辞書の{}のかわりに( ) を用いると,要素を順に「生成する」オブジェクトが得られる.これをジェネレータ内包と呼ぶ. ()の前にsum関数などを書くと,内包で要素をすべて生成せずに,順に生成しながら合計がとれる.

たとえば,1から10までの数字の和は以下のようにジェネレータ内包で記述できる.

sum( i for i in range(1,11))
>>>
55

最小値を求めたい場合には min, 最大値を求めたい場合には max 関数をジェネレータ内包の前にかけば良い. たとえば, $x^2+5x$ の $[-10,10]$ の範囲での最小値は,以下のように計算できる.

min(x**2+5*x for x in range(-10,11))
>>>
-6

●問題

1から100までの奇数の和を計算せよ.

●問題

$x$ を -100から100まで1づつ増やしたとき,$x^2 + 2x -5 $ の最小値の値を,ジェネレータ内包を用いて計算せよ.

zip の使い方

2つ以上のリストの要素をfor文で順に1つずつ取り出したいときには,zip関数が便利だ.

zip はzipper(ジッパー)のように,2つのものを1つにまとめてくれる.

たとえば,体重のリストweightと名前のリストnameがあったとき,順に取り出して出力するには,以下のようにする.

weight = [50,60,70]
name =[ "Taro", "Hanako", "Jimmy"]
for w,n in zip(weight,name):
     print(w,n)
>>>
50 Taro
60 Hanako
70 Jimmy

●問題

身長と名前を入れた2つのリストがあったとき,これらを順に反復して,名前をキーとし身長を値とした辞書を生成せよ.できれば,辞書内包を用いて1行で記述せよ.

height = [150, 160, 170]
name = ["Taro", "Hanako", "Jimmy"]

●問題

身長,体重,名前の3つのリストがあったとき,zip関数を用いて,名前・身長・体重を順に出力せよ.

height = [150, 160, 170]
weight = [50, 60, 70]
name = ["Taro", "Hanako", "Jimmy"]

エラー処理

エラーが発生したときに,どのような処理をするかは例外処理とよばれ,これをあらかじめ組み込んでおくことによって,頑強なプログラムを作成することができる.

構文はif文と類似していて,以下のように書く.

try:
    エラーが試される文
except エラー名:
    エラー発生時の例外処理
else: 
    エラーが発生しないときの処理

●例題

10/xを計算しyに代入してから出力するプログラムを xが0のときに発生するエラー(ZeroDivisionError)の処理を入れて記述せよ.

x = 0
# x = 1
try:
    y = 10 / x
except ZeroDivisionError:
    print("0では割れないよ!")
else:
    print(y)
0では割れないよ!

どのようなエラー名なのかは,実際にエラーを発生させてみると分かる.

x = [1,2,3]
print(x[3])

と入力すると IndexError と出力される.

x = [1,2,3]
print(x[3])
---------------------------------------------------------------------------
IndexError                                Traceback (most recent call last)
/var/folders/69/5y96sdc94jxf6khgc8mlmxrr0000gn/T/ipykernel_72546/3061129955.py in <module>
      1 x = [1,2,3]
----> 2 print(x[3])

IndexError: list index out of range

●問題 x =[1,2,3] に対して, iを0から5まで1づつ増やしながら反復させてx[i]を出力させたとき, 対応するインデックスがない場合には,「そんな添え字はないよ」と出力するプログラムを作れ.

モジュール

Pythonでは、別のファイルに保存されているプログラムを読み込んで使うことができる. このように、他の場所にある .py ファイルを、モジュール(いくつかのモジュールを合わせたものはパッケージ)と呼ぶ.

モジュールの呼び出しは、以下のいずれかの書式で行う。

import モジュール名
import モジュール名  as 別名
from モジュール名 import オブジェクト名もしくはすべてを表す*

数学モジュール math

例として、数学モジュールmathから色々な関数や定数を読み込んで使ってみよう。

モジュール内のオブジェクトを使う場合には、モジュール名(もしくは別名).オブジェクト名とドット(.)記法を用いる。

import math

print(math.sqrt(2))  # 2の平方根
print(math.tan(math.pi))  # 円周率πの正接(tan)

from math import log, e

print(log(e))  # 自然対数の底eの対数 (log)
1.4142135623730951
-1.2246467991473532e-16
1.0

問題

数学モジュールmathには、整数a,bの最大公約数を求めるgcd(a, b)が準備されている. これを用いて、好きな(ある程度大きな)数字の最大公約数を求めよ.

日付時間モジュール datetime

時間や日付を簡単に取り扱うためのモジュールとして、datetimeが準備されている。 これを、別名dtで読み込んで使ってみる。

datetimeモジュールには、日付時刻を表すクラスdatetimeが含まれている。

import datetime as dt

today = dt.datetime.today()  # 今日の日付
print("Today=", today)
birthday = dt.datetime(1879, 3, 14)  # 誕生日
print("Birthday", birthday)
print("Time delta=", today - birthday)  # 誕生日から本日までの経過時間
Today= 2022-03-18 19:27:27.232865
Birthday 1879-03-14 00:00:00
Time delta= 52234 days, 19:27:27.232865

問題

自分の誕生日から本日まで何日経過したかをdatetimeモジュールを用いて調べよ。

名前付きタプル namedtuple

collectionモジュールには、便利な関数やクラスが準備されている。

collectionモジュールのnamedtuple(名前付きタプル)を使うと、簡易的にクラスもどきを生成できる。

注意: きちんと書かれたモジュールにおいては、小文字で始まるものが関数で、大文字で始まるものがクラスである. これを破ってもエラーは出ないが、分かりやすさのために遵守すべき規則(というか礼儀)である. したがって、namedtupleは関数であるが、新しいインスタンスを生成するための関数であるので、工場(ファクトリ)関数と呼ばれる.

以下では、名前、身長、体重の属性をもつ名前付きタプル Person を定義する。

from collections import namedtuple

Person = namedtuple(
    "Person", ["name", "weight", "height"]
)  # 名前付きタプルオブジェクトの生成(属性は名前、重さ、高さ)
kitty = Person("Kitty", 30, 10)  # 名前付きタプルオブジェクトの生成(コンストラクタの引数は、名前、重さ、高さ)
daniel = Person("Daniel", 40, 20)
print(kitty.name, kitty.height, kitty.weight)  # 名前付きタプルのオブジェクトには、ドット記法で属性にアクセスできる。
Kitty 10 30
print(daniel[0], daniel[1], daniel[2])  # 名前付きタプルのオブジェクトには、番号でアクセスできる。
Daniel 40 20

問題

名前、成績(gpa)、英語テストの成績(例えばtoeic)、趣味(hobby)などの属性をもつ名前付きタプル Profile を定義し、自分の属性を定義せよ。