##なぜPythonに興味を持ち、学ぼうとしているのか?
A.
今一番ホットな言語であるのと機械学習、AI、深層学習(ディープランニング)などなど十全に使いこなすにはおそらく根っからの文系である自分では無理だろうと感じてはいるものの、やることの規模とやはり流行るものはある程度は身につけておきたい(なぜ流行るのか?ということを知る意味を含めて)なぁと感じたから。
しかし、これはあくまでも興味関心という意味での話でそもそも今まで本筋として学んでいたPHP(Laravel)の手を止めてまで、今新しい言語を学んでいるのかというと
Twitter上でSaku731さん のこういう企画 を目にしたからです。
独学でPHP(Laravel)を学び、一応曲がりなりにも成果物を1つ要件定義から実装まで完走した経験上
どのようなサービスを作ればいいのか
こういう処理はどういう具合がベターなのか?
データベース設計って他の人はどういうリレーションとかしているのだろう?
セキュリティは? リファクタリングは? トランザクションは?
……etc と道中様々なことで壁にぶつかり、結局、妥協やあまりベターとはいえないようなコードを書いてしまったりと悩みはつきませんでした。
もちろん、やってみたからこそわかった課題・これからやらなきゃいけないことなどもはっきりと見えてきて、おそらく、参考書等での学習にしても1からLaravelの教本を順にやって……みたいなことをやるよりか、実際にフレームワークを使ってみて開発した今の段階で、PHPフレームワークLaravel入門のようなものを流し読みし足りないところをPUしていくというほうがより効率が上がるのではと思っています。
この間、現にちょっと書店でどんなものか確認してみましたが思いの外書いてあることがすっと入ってきましたので。就活中につき、お金が足りなくて変えてないのですが……
しかし結局そういった個人でのブラッシュアップはこの時代Techpitにも3000円程度で良い教材があり、もっというとインターネットの海を漁ればいくらでもできてしまいますが、実際に未経験でのエンジニアの就職をと現在就活している中で、思いの外そういう経験って重宝されません、ないよりは数倍マシなのですけど。
これは、通常の就職活動における「学歴」フィルターのようなものですね。
企業も採用に関しては安くないコストを支払っているわけで、エージェント経由だと斡旋された人の年収の3~6割程度をマージンとして取られるのは常識なんて話も就活を初めてから知りました。
そんな中で、社会人経験すらなく、かつ「実際の業務で案件をクローズさせた経験=本番レベルのコーディングや開発経験」がない人を果たして中途で取るというメリットってないのですよね。なにせ、採用にコストを払ったあとは今度は教育に投資しないといけないわけですし。
そういう人材を取るところというと、その人材にポテンシャルを感じてそれを見込んでくれるところか自社でそういう人材を取って教育したい、あるいは教育できる体力があるところか、または、未経験でもいれなきゃまらわないような超ブラックなところかの2択になるのは火を見るより明らかですよね。
自分で言ってて悲しくなるんですが、ド正論なので現在泣きながら就職活動中です。
ちなみに自分は感触からいって後者の方へ行きそうな流れです。
閑話休題、さてそういった背景がある中で結局「独学での開発」に限界を覚えていたところ、幸運にも上述の企画を目にし、
企画を立ち上げた背景:
転職・部署移動したくてプログラミングを勉強しても、「実務経験が無いと結局チャンスが貰えない」といった事が非常に多いです。
しかし、周囲を説得する材料(=実際に動くサービス)があれば「実務経験」と同等の評価を受けることが出来ます。
これまで頑張って勉強してきた方に「実際に動くサービス」と「チーム開発の経験」をノーリスクで得られる場を作りたい。これが企画を立ち上げた背景です。
必要なのは『プログラミングで人生を変える意思』だけです。
という企画意図を読んで、今自分が求めている経験をさせていただけるのでは? と思い、問い合わせてみると未経験でもとりあえず最低限、BootstrapとPythonを調べながら書くことができれば参加OK(ただし、あくまで最低限)ということだったのでこの機会にPythonの学習を始めたというのが今回の経緯です。
本当はPHP(Laravel)のブラッシュアップをするのが筋ではありますし、成果物を作ってみて他にもクローンでも構わないのでどんどんアプリを作る練習を兼ねてそれをやっていきたいのですが、せっかくある興味をそのままにしていおくのかという疑問と何より**「実際に動くサービス」と「チーム開発の経験」をノーリスクで得られる場**が目の前にあるのに、それをスルーすることがどうしてもできなかったのです。
目移りしてるだけだと思われても致し方ないとは思っていますが、一応目的意識はしっかりあることだけははじめにご理解頂けると幸いです。
##私の現状
これくらいのものを作ったことがある程度のレベルです
PHPにおける関数・クラス・データベース設計の基本~やや発展くらいが理解できていて、自分で書くには難儀はするけれど、何が書いてあるかとか読み解くことはできるという感じでしょうか。
ビギナーは卒業し、PHP初中級~から完全なPHP中級者に足を踏み入れるかどうかというところかな? と思っています。
つまるところ、まだまだペーペーというわけです。
##何を書いていくか
ひとまず5月までにPHPで今までやってきたことがPythonでできるようになるレベルかつDjangoのチュートリアルくらいは終わらせたいので、あと1ヶ月強でやってきたことを復習を兼ねて、雑にアウトプットしていきます。
参加者のレベルがどのくらいになるかはわかりませんが、間違いなく私は最下層の方にいますし、任される役割としても実装部分でしょうが、少しでもチームの負担にならず貢献できるようになりたいのでなんとか頑張っていきたいと思います。
この経験で逆にPHPに還元できることもあるかもしれませんしね。
今回は、Pythonの基礎中の基礎的な部分を列挙していくことになります。
importとモジュール
現在のところPHPと1番違うなと思ったのがこのimportとモジュールという仕組みです。
モジュールとはPythonにおいて関数やクラスをまとめて書いたファイルのことであり、いわゆるライブラリのようなものである。
当然、備え付けのものもあれば自作することもできる。
また、モジュール及び__init__.py
ファイルを含むディレクトリをパッケージと呼ぶ。
__init__.py
には初期化コードを記述できる。
__init__.py
を含まない名前空間パッケージという仕組みもあるが、ここではパッケージとはこういうものだという理解に留めておく。
おそらく、PHPやLaravelにおけるnamespaceの仕組みと似たようなものだと推察する。
実際の使い方としては
import datetime
#または
from datetime import datetime, timedelta, timezone
このように記述する。
複数モジュールをインポートする際は後者の例のようにカンマで区切る。
前者はモジュールをmodule型のオブジェクトとしてインポートする。
こうすることで、モジュール名.関数名
やモジュール名.変数名
のようにしてモジュールに定義されている関数や変数を利用できる。
後者はさらに踏み込んで直接モジュールのオブジェクトを個別にインポートすることができる。
よって、前者はdatetimeモジュール全体をインポート、後者はdatetimeモジュールのうちdatetimeオブジェクト、timedeltaオブジェクト、timezoneオブジェクトのみインポートするという意味になる。
ちなみdatetimeモジュールはPythonにおいて日付時刻を扱うことができるモジュール。
if文
import random
number = random.randint(1,3)*100
print("あなたの得点は" + str(nunmber) + "ポイントです")
if number == 300:
print("おめでとう")
else:
print("はずれ")
randomモジュールをインポートし、randint関数を使ってそれをもとに条件分岐の処理を書いたのが上記の例。
randint関数は指定した引数の範囲の中から任意の値を返す関数でそれを100倍したものをnumber変数に代入し、それをstrで文字列に直して出力する。
PHPだとif文はif(条件式){}
の形で表現したが、Pythonでは{}の部分をインテンドの上げ下げで表現しているのがこれを見るとわかる。
なお、PHPにおけるセミコロンの類は必要ないが、必ず条件文などにはコロンをつけるのを忘れないようにする。
ループ
for i in range(0,16):
print(str(i))
# whileの場合
i = 0
while i <= 5:
print(str(i))
i = i + 1 #カウンタ変数を進める
配列(リスト)
Pythonでは配列をリストと呼称する。
#リスト
monster = ['スライム','ウルフ','ゾンビ','吸血鬼','ゴースト']
#リストの末尾に要素を追加
monster.append('ゴーレム')
print(monster)
##出力結果
['スライム','ウルフ','ゾンビ','吸血鬼','ゴースト','ゴーレム']
#リストの要素を削除
monster = ['スライム','ウルフ','ゾンビ','吸血鬼','ゴースト']
monster.pop(1) # インデックスを指定
print(monster)
##出力結果
['スライム','ゾンビ','吸血鬼','ゴースト']
#リストの繰り返し
numbers = [12,34,56,78,90]
total = 0
for num in range(0,len(numbers))
total = total + numbers[nums]
print(total)
## 出力結果
270
# 要素を分割してリスト化する
team_str = "勇者,戦士,忍者,魔法使い"
print(team_str.split(","))
##出力結果
['勇者', '戦士', '忍者', '魔法使い']
str = "One cold rainy day when my father was a little boy he met an old alley cat on his street"
print(len(str.split(" ")))
##出力結果
20 # 分割されてリストにされた要素の数、この場合は何単語あるかを数えたことになる。
url_str = input().rstrip()(入力がhttps://www.yahoo.co.jp/exampleだった場合)
print(url_str.split("/"))
##出力結果
['https:', '', 'www.yahoo.co.jp', 'example']
len()は文字数や要素の数を返してくれる関数。
str.split()で引数に指定した記号で文字列などを区切り、さらにそれをリストに格納していく関数。
辞書(連想配列)
辞書とはキー:値
の関係、つまりプロパティを格納したリストのことであり、いわゆる連想配列のことである。
# 辞書の表現
{'red':'apple','blue':'sea','green':'leaf','yellow':'lemon'}
# 辞書から要素を削除
enemies = {"ザコ":"スライム", "中ボス":"ドラゴン", "ラスボス":"魔王"}
del enemies['ザコ'] #キーを指定
→enemies = { "中ボス":"ドラゴン", "ラスボス":"魔王"}
# 辞書のループ(*1)
enemies = {"ザコ":"スライム", "中ボス":"ドラゴン", "ラスボス":"魔王"}
for rank in enemies:
print(enemies[rank])
## 出力結果
スライム
ドラゴン
魔王
# キーと値を同時にループさせる場合(*2)
for(rank,enemy) in enemies.items():
print(str(rank) + 'は' + str(enemy) + 'です')
# リストのループでインデックスも同時に取得する
team = ['勇者','戦士','魔法使い',]
for(i,person) in enumerate(team):
print(str(i+1) + '番目の' + person) # iがインデックス、personが要素となる。
##出力結果
ザコはスライムです
中ボスはドラゴンです
ラスボスは魔王です
ここまでで分かる通り、Pythonのfor文はPHPとは違ってfor(i = 0; i < 10; i++)
といったようにカウンタ変数を別途定義せず、foreach文のようにリストや辞書などのオブジェクトの要素が順番に変数に代入されて処理が行われる。
上記の例の1や2をみればどういう処理が行われてるかよく分かる。
つまり、1の場合はenemies内の要素を逐一、rank変数に代入されインテンドが下がったところの処理が行われるという寸法になるのだが、この時辞書の場合はrank変数に代入されるのはキーの部分になるので、上述のような出力結果になる。
また2の場合はforのあとの変数を2つ指定することでそれぞれrank→キー、enemy→値と代入させて同時にループさせている。
値を変数に代入したいときはitems()
を使うのを忘れないようにする。
ここで混同しないようにしたいのはitems()
は辞書において要素のキーと値を同時に取得、あるいは値のみ取得する場合に用いるが、enumerate()
はあくまでリストなどの要素とインデックス(リストの格納されている順番)を同時に取得するというところである。
#ソート
# リスト
apples = [1,100,1000,10000]
print(sorted(apples))
##出力結果
[1, 100, 1000, 10000]
example = ['1.apple','4.apple','3.lemon','2.apple']
print(sorted(example))
##出力結果
['1.apple','2.apple','3.lemon','4.apple']
## reverse=Trueを指定すると逆順にソートされる
print(sorted(example, reverse=True))
##出力結果
['4.apple', '3.lemon', '2.apple', '1.apple']
# 辞書
japan = {"kanagawa" : 10, "osaka" : 20, "okinawa":30}
print(sorted(japan))
## 出力結果
['kanagawa', 'okinawa', 'osaka']
## 値も一緒に出力
値も一緒に出力する場合は
print(sorted(japan.items()))
##出力結果
[('kanagawa', 10), ('okinawa', 30), ('osaka', 20)]
数値の場合は数値の大きさ、英字ならアルファベット順、カタカナ・ひらがなならあいうえお順にソートされる。
ただし、漢字の場合は文字コード順となり読みがな順とはならない。
優先度は数値>文字列となる。
リストの作成
# forでリストを再生成してみる。
team = ['勇者','戦士','魔法使い',]
newteam= []
for person in team:
newteam.append('10年後の' + person)
print(newteam)
##出力結果
['10年後の勇者', '10年後の戦士', '10年後の魔法使い']
# 応用編
numbers = [i*2 for i in range(10)]
print(numbers)
##出力結果
[0, 2, 4, 6, 8, 10, 12, 14, 16, 18]
## さらに応用してみる
numbers2 = [[1 for i in range(3)] for j in range(4)]
print(numbers2)
##出力結果
[[1, 1, 1], [1, 1, 1], [1, 1, 1], [1, 1, 1]]
単純にリストを作るには[]
に要素を格納してそれを変数に代入すればいい。
上記のように、for文を用いて別のリストを別の変数の空リストに格納することでリストを再生成することもできる。
応用部分については変数に代入する段階で[]
でfor文を展開し、そこでリストの要素を生成することでリストを作るという手法。
からくりとしては、[繰り返し格納したい要素 for i in range()]
ということになる。
つまり、forの前で指定された要素がrangeの分だけ繰り返しリストに格納されることになるので、この場合はrange(10)=0~9がiに代入され、それを2倍した値が要素としてリストに繰り返し格納されることになるので上記のような出力結果になる。
そして、さらにそれを応用すると二次元リストを作ることができる。
からくりとしては[[繰り返し格納したい要素 for i in range() for j in range()]]
であり、for j in range()
の部分は、forの前で指定したリストをいくつ作るかという意味になる。
単純な数値のリストを作りたいときに使えるかもしれない。
forの入れ子
Pythonでもfor文は入れ子にすることはできる。
letters = [[[0,0,1,1,0,0],
[0,1,0,0,1,0],
[1,0,0,0,0,1],
[1,1,1,1,1,1],
[1,0,0,0,0,1],
[1,0,0,0,0,1]],
[[1,1,1,1,1,0],
[1,0,0,0,0,1],
[1,1,1,1,1,0],
[1,0,0,0,0,1],
[1,0,0,0,0,1],
[1,1,1,1,1,0]],
[[0,1,1,1,1,0],
[1,0,0,0,0,1],
[1,0,0,0,0,0],
[1,0,0,0,0,0],
[1,0,0,0,0,1],
[0,1,1,1,1,0]]]
for i in letters: # 下の処理を繰り返す
for line in i: # 下の処理を繰り返す
for dot in line: # dotに変える処理
if dot == 1:
print('@',end='')
else:
print(' ',end='')
print()
print() # リストの2桁インデックスごとに改行する。
この例は3次リストを用いて0or1を対応する記号に変換してドット絵を作るための処理。
まず。0or1をドット(対応する記号または半角スペース)に変換する処理を行う。
そしてそれを、リストの各1次リストで行う。
また、1次リストは各2次リストに格納されているので各2次リスト毎に行ったあと、改行するという処理になる。
可変長引数
可変長引数とは引数の数がわからない場合に引数に*をつけるか、プロパティを引数にしたい場合は**をつけた場合の引数のこと。
例えば下記のような場合
def introduce(**people):
for name, greeting in people.items():
print("私は" + name + "です。" + greeting)
introduce(hero = "はじめまして", villager = "こんにちは", soldier = "よろしくお願いします")
## 出力結果
例えば上記のような関数で、peopleでしていた引数で役職によって挨拶の仕方を変えたいとする。
すると、設定すべき引数は不定にしたほうが都合がいいので、可変長引数を使う。
この場合はプロパティを引数にしたいので**people
にする。
すると、people = [hero:'はじめまして' ,villager:'こんにちは',soldier:'よろしくお願いします']
といったように引数を設定することができるので、あとは関数を呼び出す際に引数をこのようにしてやればいいということになる。
Pythonの関数・クラス
# 関数
def say_hello():
print('hello' )
say_hello()
## 出力結果
hello
# クラス
class Greeting:
def __init__(self, name):# Pythonでのコンストラクタの記述
self.name = name
def say_hello(self):
print('hello' + self.name)
## クラスの呼び出し
test = Greeting('world')
test.say_hello()
## 結果
hello world
def 関数名():
と定義して、そこからインテンドを下げたところからが関数の処理内容になる。
{}を書かなくていいのは楽。
クラスの場合はこれに加えてclass クラス名
としてあとは関数をメソッドとして定義してやる。
呼び出し方はPHPと同じで変数に代入してオブジェクト化すればいい。
また、self
はPHPでいう$this
やself::
のようにクラス内で自身のインスタンスを表現するもの。
つまり、self.name
はクラスのプロパティ(nameの値)にアクセスしているということになる。
例えばコンストラクタの部分下記のようにして、引数にデフォルト値を設定すると呼び出しでクラスに引数を設定しなかった場合。
def __init__(self, name='unknown'):
## 出力結果
hello unknown
ということになる。
実際にクラスを使ってみると
class Item:
tax = 1.08
def __init__(self, price, quantity):
self.price = price
self.quantity = quantity
def total(self):
return int(self.price * self.quantity * Item.tax)
apple = Item(120,15) # コンストラクタで2つ引数を設定しているので相当するものを指定。
total = apple.total() # メソッドの呼び出し
print(total)
## 出力結果
1944
こういうことになる。
Pythonにおけるprivate
結論から言うとドキュメントでは以下のように説明されている。
オブジェクトの中からしかアクセス出来ない "プライベート" インスタンス変数は、 Python にはありません。
だが、__
を変数やメソッドの前につけることで擬似的にその扱いにすることはできる。
以下の例を見てみる。
class Player:
def __init__(self, job, weapon):
self.job = job
self.__weapon = weapon
def walk(self):
print(self.job + 'は荒野を冒険している')
def __attack(self, enemy):
print(self.__weapon + "で" + enemy + "を攻撃")
player1 = Player('Fighter','Sword')
player1.walk()
player1.attack('Golem') # error
print(player1.__weapon) # error
# privateなメソッドに外部からアクセスする。
class Greeting:
def __init__(self):
self.msg = 'hello'
self.target = 'paiza'
def say_hello(self):
print(self.msg + " " + self.target)
def __say_yeah(self):
print("YEAH YEAH YEAH")
player = Greeting()
player.say_hello()
player._Greeting__say_yeah() # こうすることでクラス外からprivateメソッドを呼び出す。
まずコンストラクタで設定してあるプロパティを見るとjobプロパティはpublicでweaponプロパティはprivateになっていることがわかる。
よって、print(player1.__weapon)
はweapon
プロパティにアクセスできないのでエラーとなる。
メソッドも同様の理屈。
プロパティは外からアクセスされることが好ましくないので基本的にはprivateにしておくのがよい。
逆にメソッドについては実際に使いたいのに外から呼び出せないのでは話にならないので、安易にprivateにはしない。
外部からどうしてもprivateなメソッドなどにアクセスしたい場合は
オブジェクトを代入した変数._クラス名__privateなメソッド名()
という記述でアクセスできる。
pythonでのクラス継承
class Box: #1
def __init__(self, item):
self.item = item
def open(self):
print("宝箱を開いた。" + self.item + "を手に入れた" +)
class ArmsBox(Box): #2
def look(self):
print("マスターソードは静かに佇む……")
box = Box("薬草")
box.open()
armsbox = ArmsBox('光の弓矢') #3
armsbox.look()
armsbox.open()
## 出力結果
宝箱を開いた。薬草を手に入れた
マスターソードは静かに佇む……
宝箱を開いた。光の弓矢を手に入れた
# メソッドのオーバーライド
class Box:
def __init__(self, item):
self.item = item
def open(self):
print("宝箱を開いた。" + self.item + "を手に入れた" )
class ArmsBox(Box):
def open(self):
print("武器を生成した" + "self.item" + "を手に入れた")
## オーバーライドに際し、デフォルト値を持つ引数を追加する
class ArmsBox(Box):
def open(self, skill):
print("武器を生成した。" + self.item + "を手に入れた。" + self.item + "に武器スキル" + '\"' + skill + '\"' + "がついた!")
armsbox = ArmsBox('はがねのつるぎ')
armsbox.open('必殺')
## 出力結果
宝箱を開いた。薬草を手に入れた
武器を生成した。はがねのつるぎを手に入れた。はがねのつるぎに武器スキル"必殺"がついた!
# 親クラスのメソッドを参照ではなくそのまま実行する
class Greeting:
def __init__(self):
self.msg = "hello"
self.target = "paiza"
def say_hello(self):
print(self.msg + " " + self.target)
class Hello(Greeting):
def say_hello(self):
super().say_hello() # 親クラス側のsay_helloメソッドを呼び出す。
print("YEAH YEAH YEAH")
player = Hello()
player.say_hello()
まず#1のように親クラス(スーパークラス)を定義する。
今回はコンストラクタ関数を使い、初期値とそれを用いた関数を定義しておく。
次に#2で親クラスを継承した子クラスを作る(サブクラス)。
今回はprint関数で任意のメッセージを出力する関数を定義しておく。
あとは#3のように呼び出して使う。
クラスを継承した場合、子クラスは親クラスのメソッドも参照できるので以上のような出力となる。
またPythonのクラス継承の定義の仕方はPHPとは違ってクラスの引数に親クラスを指定するような書き方をする。
class 親クラス名(子クラス名):
また、メソッドのオーバーライドも普通にできる。
重要なのはPythonのオーバーライドは引数、及び戻り値の型が同じである必要ないということ。
よって上記の例ではBoxクラスをArmsBoxに継承し、openメソッドオーバーライドした際に引数を追加しても問題はない。
親クラスのメソッドを継承ではなく、そのまま直接使いたい場合はsuper().使いたい親クラスのメソッド名()
としてやればいい。
Pythonでのクラス変数
class Player:
__charactor_count = 0
クラス変数はクラスでの共通の変数になるのでいたずらに外部からアクセスして改変されると困るのでPrivateにしておくのがよい
Pythonでのクラスメソッド
当然クラス変数があるのだから当然クラスメソッドもある、しかしPHPとは少しやり方が違う。
PHPではメソッドの前にstaticを指定して、呼び出しはself::メソッド名などで行ったが
Pythonの場合は
class Player:
__charactor_count = 0
@classmethod # デコレータ
def summary(cls):
print(str(Player.__charactor_count) + "人でスライムを攻撃した")
まず、デコレータと呼ばれる手法を用いる。
デコレータはすでに定義されている関数に新たに機能を追加できる仕組みである。
既存のものもあり、自作することもできる。
今回の@classmethod
は用意されているものである。
これを用いた場合、メソッドの引数はself(=インスタンス自身)
ではなくcls(=クラス自身)
となる。
ちなみにこれに関連して@staticmethod
というのもある。
@classmethod
はクラス変数にアクセスすべき、あるいは継承クラスで動作が変わるべきに用いる。
@staticmethod
は継承先でも動作が変わらないときに用いる。
今回の場合は、クラス変数にアクセスしないといけないので@classmethodを使う。
ちなみに、動作が変わるべきときというのは以下のような例である。
class Student:
def __init__(self, name, school):
self.name = name
self.school = school
self.marks = []
def average(self):
"""平均成績を返す
インスタンス変数にアクセスしたいのでinstancemethodを使う。
"""
return sum(self.marks) / len(self.marks)
@classmethod
def friend(cls, origin, friend_name, *args):
"""同じ学校の友達を追加する。
継承クラスで動作が変わるべき(継承クラスでは salaryプロパティがある)
なのでclassmethodを使う。
子クラスの初期化引数は *argsで受けるのがいい
"""
return cls(friend_name, origin.school, *args)
@staticmethod
def say_hello():
"""先生に挨拶する
継承しても同じ動きでいいのでstaticmethodを使う
"""
print("Hello Teacher!")
class WorkingStudent(Student):
def __init__(self, name, school, salary):
super().__init__(name, school)
self.salary = salary
hiro = WorkingStudent("Hiro", "Stanford", 20.00)
mitsu = WorkingStudent.friend(hiro, "Mitsu", 15.00)
print(mitsu.salary)
親クラスではname,schoolのみプロパティがあるが継承先の子クラスではsalaryプロパティが新たに追加されているので動作が変わるということになる。
ちなみにargsは任意の可変長引数をタプル(定数リスト)で受け取るという意味になる。
そして、このargsの前に引数が設定されているとその引数以外のプロパティがタプにまとめられる。
つまり、この場合はorigin, friend_nameにそれぞれ相当するhiro、"Mitsu"はそのまま引数として用いられるが、子クラスの初期化引数のうちname及びschollはhiro = WorkingStudent("Hiro", "Stanford", 20.00)の部分で用いられ、それがmitsu = WorkingStudent.friendで引数として使われているのでタプルにはまとめられないが、
self.salary = salaryはdef friend()の引数としては浮いてしまうので。それを*argsで受け取っておくということになる。
余談であるが、よくみると親クラスのコンストラクタメソッドをオーバライドしていてやはり引数を追加しても問題はないということがわかる。
閑話休題、つまりこれで以下のような処理が可能となる。
class Player:
__charactor_count = 0
@classmethod
def summary(cls):
print(str(Player.__charactor_count) + "人で、スライムを攻撃した。")
def __init__(self, name):
self.name = name
Player.__charactor_count += 1
print(str(Player.__charactor_count) + "番目のプレイヤー、" + self.name + "が登場した。")
def attack(self, enemy):
print(self.name + "は、" + enemy + "を攻撃した!")
class Wizard(Player):
def __init__(self):
super().__init__("魔法使い")
def attack(self, enemy):
self.__spell()
print(self.name + "は、" + enemy + "に炎を放った!")
def __spell(self):
print("ズバーン!")
print("=== パーティーでスライムと戦う ===")
hero = Player("勇者")
warrior = Player("戦士")
wizard = Wizard()
party = [hero, warrior, wizard]
for member in party:
member.attack("スライム")
Player.summary()
## 出力結果
=== パーティーでスライムと戦う ===
1番目のプレイヤー、勇者が登場した。
2番目のプレイヤー、戦士が登場した。
3番目のプレイヤー、魔法使いが登場した。
勇者は、スライムを攻撃した!
戦士は、スライムを攻撃した!
ズバーン!
魔法使いは、スライムに炎を放った!
3人で、スライムを攻撃した。
Pythonでの例外処理
try:
except 例外名 as e:
finally:
tryブロックが実行する処理、exceptブロックが例外名に対応する例外が起きた場合の処理(例外キャッチする部分)、finallyブロックが例外を無視してでも実行される処理のブロックとなる。
つまり
try:
number = 0
print('これは0です')
print(number / 2)
except ZeroDivisionError as e:
print(e)
finally:
print('例外が起きているか確認してみてください')
という構文の場合、まずtryブロックでnumber変数に0が代入される。
その後、1回目のprintでメッセージが出力されるが、2回めのprintは0の割り算を行っているため例外が発生してしまう。
よって、その例外はexceptブロックへとthrowされ、それをcatchし、変数eにエラーメッセージが代入され出力される。
そして、最後にfinallyブロックの処理が実行されるという寸法になる。
もちろん、exceptブロックにおける変数eはエラーメッセージを引数とするという意味になるので
print(’ゼロの割り算が発生しました、処理が中止されます’)
といったように任意のメッセージに置き換えることもできる。
ちなみに、traceback
およびsys
モジュールをimport
すると、print
の代わりにsys.stderr.write()
という記述をすることができる。これは標準エラー出力でのエラーメッセージを定義することができるもので、エラーログ側のエラーメッセージを任意のものに置き換えることができる。
逆に、print
で出力を行うのと同じように画面に出力する方にエラーログ側のメッセージを出力したい場合は、print(traceback.format_exc())
を使う。
また、複数の例外を捕捉するためにexceptブロックを複数定義することもできる。
つまり、
except Exception as e:
except ZeroDivisionError as e:
except NameError as e:
と定義すると、上から順に例外チェックが行われていく。
ただし、Exception
クラスは下記の2クラスに対しての親クラスにあたるので定義する場合は先頭に記述して、下層に子クラスの例外クラスを定義していく形にしていかないといけない。
この場合だと、まずZeroDivisionError
及びNameError
以外の例外が発生していた場合はException
クラスがそれをcatch
し、ゼロの割り算及び未定義変数の指定に関する例外はここのクラスへとthrow
されるという処理になる。
例外クラス名の例
ZeroDivisionError
# 0の割り算での例外
NameError
# 未定義の変数を呼び出したときにスルーされる例外
自作もできるが、当然標準で備え付けられているものが多くあるので例外をthrowさせるときは適宜確認をして実装するべきである。
tryブロック中で意図的に例外を発生させる。
raise 例外クラス名()
これで、任意の例外クラスの例外を発生させる事ができるのであとはそれに相当する例外クラスでcatchすればいい。
引数にはメッセージを指定することもできる。