どうも! 職業訓練生のがっちゃんです!
50歳にして長期人材育成「情報セキュリティ管理者資格」2年コースを絶賛受講中です!
今回は、授業で習った「Python3系」プログラミングの基礎:クラス編として書いていきたいと思います。
プログラミングで使用したソフトは「Anaconda」というパッケージソフトの「Anaconda Prompt」というアプリを使って勉強しました。
classって、なんぞ!?
Pythonが、組み込みで提供しているさまざまな型はクラスです。例えば、以下のコードを実行してみればわかります。
1 2 |
print( type('a') ) print( type(7) ) |
実行結果は、以下のとおり。それぞれ、<class ‘str’> や <class ‘int’> と表示されます。
この「class」というのが、文字列’a’の型は「strクラス」、整数7の型は「intクラス」ということを表している。「文字列’a’はstrクラスのオブジェクト」であり「整数7はintクラスのオブジェクト」となる。オブジェクトとは、プログラムを実行したときに作成されるデータ(実体)のことをいう。これに対して「class」とは「オブジェクトを作るための設計図」と言えます。
オブジェクトのことを「あるクラスの設計図から作り出された実体」であることを言うために「インスタンス」ということもある。
例えば、int型は「整数という概念を記述した型(クラス)」であり、整数値「7」は「整数という概念をもとに作り出された具体的な値(インスタンス)」であるといえます。
そして、プログラマーが、そうした型(クラス)を独自に作り出すこともできます。そのために使うのが「class」と呼ばれるものです。
turtleクラスを体験する
Pythonには「turtle class」というものがあって、一定の広場で亀さんを動かすことができるクラス(設計図)があります。あなたは「コマンドプロンプト」から、その亀に対してアチコチと歩き回らせることができる。
基本的な命令として「○○歩前進せよ」「○○歩後退せよ」「××度右(左)に回転せよ」がある。さらに亀さんはペンを持っていて「ペンを下げる」「ペンを上げる」ことも可能である。亀さんの色や形を変更したりすることもできる。亀さんを増やすこともOK。
これらを使って楽しみながら「クラス」や「インスタンス」について理解を深めようという魂胆である。
操作方法
今までは「Spyder」というソフトを使っていましたが、このタートルクラスは、とっても相性が悪いようで、使うとすぐにフリーズしてしまうのでご注意ください。今回は「Anaconda」というパッケージソフトの中にある「Anaconda Prompt」というアプリを使ってください。Macの方は「Qt Console」というのをお使いください。
操作コードはざっと以下のとおり。まずは「import turtle」で、タートルモジュールを呼び出します。
1 2 3 4 5 6 7 8 9 10 11 |
# タートルモジュールの呼び出し import turtle # 三角形を描いてみる # 左に120度旋回 turtle.left( 120 ) # 前に100進んで。これを3回繰り返せば三角形の出来上がり。 turtle.forward( 100 ) turtle.left( 120 ) turtle.forward( 100 ) turtle.left( 120 ) turtle.forward( 100 ) |
実際のコードと実行結果はこんな感じです。同じコマンドを何度も書くのは大変なので「↑」「↓」キーで、一度入力したコマンドは呼び出すようにしましょう。
その他、授業で教えてもらったコードは以下のとおりです。色々と実行してお楽しみください。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 |
# 矢印を亀さんに変更 turtle.shape( 'turtle' ) # 亀さんの色を赤に変更 turtle.color( 'red' ) # 画面クリア turtle.clear # 亀さん2匹出す時 from turtle import Turtle t1 = Turtle() # 2匹目の亀さんを青色に t1.color( 'blue' ) # ペンを上げる turtle.penup() # ペンを下げる turtle.pendown() # 半径100の円を描画 turtle.circle( 100 ) # 直前の指示を取り消す turtle.undo() # 座標移動する( x座標, y座標 ) turtle.goto( 100, 200 ) |
Pythonの公式ページにいろいろなコマンドが掲載されていますので、こちらもご参考にどうぞ。
クラスを作ってみる
Pythonでのクラスの基本的な書き方は、以下のとおりになります。
クラスの中のメソッドには「self」というものが存在します。このselfは、インスタンス自身(具体的な値、実体)を示すものです。また、selfはpythonの設計仕様で欠かすことはできません。名前を self 以外のキーワードにすることは可能ですが、慣例として self を使用します。Pythonでは作成されたコードを他人でも一眼で分かるように書くことが重要です。そのため、基本的には self としておくほうが良いと思います。
1 2 3 4 5 6 7 8 9 10 11 12 13 |
# Employeeクラスの定義 class Employee: # データ name = "山田太郎さん" # work関数(引数にselfを書くこと) def work(self): print('今日はお仕事です’) # 変数 m にEmployeeクラスのインスタンスを代入 m = Employee() # Employeeクラスにあるname変数をプリントして! print(m.name) # Employeeクラスにあるwork()関数を実行して! m.work() |
実行してみると、以下のとおりになります。
ちなみに「 m = Employee() 」とインスタンス化せずに、そのまま Employee を使ってみましょう。
1 2 3 4 5 6 7 8 9 10 11 |
# Employeeクラスの定義 class Employee: # データ name = "山田太郎さん" # work関数(引数にselfを書くこと) def work(self): print('今日はお仕事です') # Employeeクラスにあるname変数をプリントして! print(Employee.name) # Employeeクラスにあるwork()関数を実行して! Employee.work() |
そうすると、print(Employee.name)はうまくいきましたが、Employee.work()のところでエラーが出ちゃいます。エラーの文面を見ると、「selfに対する引数が必要だけど足りてないよ!」と言われているみたいです。
というわけで、引数「’今日は日曜日なのに仕事って、マジっすか!?’」を入れて作成してみました。これだけだと、この引数はどこにも反映されそうにないのでにprint文に self を足してみました。
1 2 3 4 5 6 7 |
class Employee: name = "山田太郎さん" def work(self): print('今日はお仕事です。' + self) print(Employee.name) # Employeeクラスにあるwork()関数を実行して! Employee.work('今日は日曜日なのに仕事って、マジっすか!?') |
実行結果は以下のとおり。エラーは出なくなりました。
エラーは出なくなったけど、本来、クラスはインスタンス化して使うものですので、キチンとインスタンス化して使うようにしましょう。でも、これはこれで、クラスのまま呼び出したい時に使えそうな技ですね。
※追記:あとで分かったのですが、クラスを定義しただけでもインスタンスは生成されているそうです。でも、インスタンスは1つしか生成できないので、2つ目を生成しようとするとエラーになるそうです。
そんなわけで、次も、こんな処理方法もあるんだなという感じでやってみましょう。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 |
# Employeeクラスの定義 class Employee: # データ name = "山田太郎さん" # ふるまい(引数にselfを書くこと) def work(self): print(self.name + 'は今日、お仕事です') # 変数 m にEmployeeクラスのインスタンスを代入 m = Employee() # Employeeクラスにあるname変数をプリントして! print(m.name) # Employeeクラスにあるwork()関数を実行して! # その前に name を'佐藤B作さん'に変更して! m.name = '佐藤B作さん' m.work() |
実行結果は以下のとおりです。名前の変更とかも簡単にできちゃいますね。
コンストラクタの作成
コンストラクタとは、「__init__」の部分のことです。インスタンスを作成する際に重要な処理を行うものです。どのように生成するか、どのようなデータを持たせるかなど、情報を定義するのに必要なメソッドです。
第1引数「 self 」は、インスタンス自身を指します。Pythonの慣習として名前は「 self 」で指定します。「 self 」は呼び出し側では指定は不要で、2番目の引数以降を指定します。
まずは、デフォルトで引数に何も設定しないプログラムになります。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 |
class School: # コンストラクタ # デフォルトで name の引数は何も設定しないよ! def __init__(self, name): # データ self.name = name # work関数の作成 def work(self): print( self.name + "は今日も学校です。") # hello関数の作成 def hello(): print("こんにちわ!!") # Schoolクラスにあるhello関数を実行して! School.hello() # 変数 m にSchoolクラスのインスタンスを代入 # 変数 name には"比企谷八幡くん"って入れてね m = School("比企谷八幡くん") # Schoolクラスにあるwork関数を実行して! m.work() # 変数 m2 にEmployeeクラスのインスタンスを代入 # 変数name には"材木座義輝くん"って入れてね m2 = School("材木座義輝くん") # Schoolクラスにあるwork関数を実行して! m2.work() |
プログラムの実行結果がこちらになります。
続いては、デフォルトで引数を設定した場合です。他にもちょっといろいろ変更してみました。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 |
class School: # コンストラクタ name にはデフォルトで"雪ノ下雪乃さん"と設定 def __init__(self, name = "雪ノ下雪乃さん"): # データ self.name = name # work関数の作成 def work(self): print( self.name + "は今日も学校です。") # hello関数の作成 def hello(self): print(self.name + ' こんにちわ!!') # 変数 n にSchoolクラスのインスタンスを代入 # 変数 name はデフォルトの値"雪ノ下雪乃さん"を使ってね n = School() # nインスタンスのhello関数を実行して! n.hello() # nインスタンスのwork関数を実行して! n.work() # 変数 m にSchoolクラスのインスタンスを代入 # 変数 name には"比企谷八幡くん"って入れてね m = School("比企谷八幡くん") # やっぱ m のインスタンスにはこっちの名前使ってくんない!? m.name = "ひきがやはちまんくん" # mインスタンスのwork関数を実行して! m.work() # 変数 m2 にSchoolクラスのインスタンスを代入 # 変数name には"材木座義輝くん"って入れてね m2 = School("材木座輝義くん") # m2インスタンスのwork関数を実行して! m2.work() |
実行結果はこんな感じです。デフォルト引数を設定しておけば、関数を呼び出すときに、その引数を省略することができます。また、その逆で関数を呼び出すときに「デフォルト引数=設定したい値」と書けば、デフォルト値以外の値を設定することができます。
現状、このようなデフォルトの引数設定は「オン・オフ」の機能設定などで使われていることが多いそうです。
work関数に引数 place をつけて作ってみました。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 |
class School: # コンストラクタ def __init__(self, name = "雪ノ下雪乃さん"): # データ self.name = name # work関数の作成 def work(self, place = "奉仕部の部活"): print( self.name + "は今日、" + place + "があります") # 変数 n にSchoolクラスのインスタンスを代入 # 変数 name はデフォルトの値"雪ノ下雪乃さん"を使ってね n = School() # nインスタンスのwork関数を実行して! n.work() # 変数 m にSchoolクラスのインスタンスを代入 # 変数 name には"比企谷八幡くん"って入れてね m = School("比企谷八幡くん") # やっぱ m のインスタンスにはこっちの名前使ってくんない!? m.name = "ひきがやはちまんくん" # mインスタンスのwork関数を実行して! m.work() # 変数 m2 にSchoolクラスのインスタンスを代入 # 変数name には"材木座義輝くん"って入れてね m2 = School("材木座輝義くん") # m2インスタンスのwork関数を実行して!placeは"文化祭の準備"に変更してね! m2.work("文化祭の準備") |
実行結果はこんな感じ。色々と自分で機能を追加したりしていくと、どんな感じで動くのか理解できるようになりますね。
継承
継承は、クラスの中でよく使われる機能で、作成済みのクラスを再利用したり、機能を追加したりできる、とても便利な機能です。継承元を「親クラス」、継承先を「子クラス」と呼び、「子クラス」は「親クラス」の性質(メソッドなど)を引き継ぐことができます。
一般的には、抽象性や汎用性が高いものを「親クラス」、具体性が高いものを「子クラス」として表します。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 |
# 親クラス class Mobilesuits: def __init__(self, mobilesuit, pilot ): self.mobilesuit = mobilesuit self.pilot = pilot def intro(self): print(self.mobilesuit + "のパイロットは、" + self.pilot + "です。") # 子クラス(Mobilesuitsという親クラスを使って!) class Renpougun(Mobilesuits): # 親クラスの機能そのまま使ってね! pass class Zeon(Mobilesuits): # 親クラスの機能そのまま使ってね! # 実行 r = Renpougun("ガンダム", "アムロ・レイ") r.intro() z = Zeon("赤いザク", "シャァ・アズナブル") z.intro() |
実行結果は以下のとおりになります。各「Renpougun」「Zeon」クラスにはpassしか書かれていませんが、親クラスの値やメソッドを継承していることがわかります。
「親クラス」「子クラス」に同じメソッドがあると、子クラスのメソッドが優先して実行されます。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 |
class Mobilesuits: def __init__(self, mobilesuit, pilot ): self.mobilesuit = mobilesuit self.pilot = pilot def intro(self): print(self.mobilesuit + "のパイロットは、" + self.pilot + "です。") # 子クラス(Mobilesuitsという親クラスを使って!) class Renpougun(Mobilesuits): # 親クラスの機能そのまま使ってね! pass class Zeon(Mobilesuits): # 親クラスの機能そのまま使ってね! pass class NeoZeon(Mobilesuits): def intro(self): print("あの" + self.mobilesuit + "のパイロットが" + self.pilot + "だったなんて!!") # 実行 r = Renpougun("ガンダム", "アムロ・レイ") r.intro() z = Zeon("赤いザク", "シャァ・アズナブル") z.intro() n = NeoZeon("シナンジュ", "フル・フロンタル") n.intro() |
で、実行結果はご覧のとおり。子クラスのメソッドが優先されて実行されています。
superで親クラスを継承
「子クラス」が「親クラス」の機能を継承するのに「super()」を使って「親クラス」の機能を呼び出すことができます。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 |
# 親クラス class Mobilesuits: def __init__(self, mobilesuit, pilot ): self.mobilesuit = mobilesuit self.pilot = pilot def intro(self): print(self.mobilesuit + "のパイロットは、" + self.pilot + "です。") # 子クラス(Mobilesuitsという親クラスを使って!) class Renpougun(Mobilesuits): # 親クラスの機能そのまま使ってね! pass class Zeon(Mobilesuits): # 親クラスの機能そのまま使ってね! pass def shout(self): print("ジーク・ジオン!") class NeoZeon(Mobilesuits): def intro(self): print("あの" + self.mobilesuit + "のパイロットが" + self.pilot + "だったなんて!!") super().intro() # 実行 r = Renpougun("ガンダム", "アムロ・レイ") r.intro() z = Zeon("赤いザク", "シャァ・アズナブル") z.intro() z.shout() n = NeoZeon("シナンジュ", "フル・フロンタル") n.intro() |
実行結果はこんな感じ。「super().intro()」だけで親クラスの「intro関数」を使えるようになりましたね。
カプセル化
プログラムの、外部からの操作を制御し、プログラムの独立性を保つための仕組みです。呼び出しを制限することで、ミスを防いだり、プログラムを読みやすいものにすることができます。
pythonでは、クラスからインスタンスが作られるときに、変数に値が渡されます。このインスタンス変数は、今までのプログラムでもそうでしたが、簡単に書き換えることができます(例:「比企谷八幡くん」を「ひきがやはちまんくん」に変更した件や、「山田太郎さん」を「佐藤B作さん」に変更した件)。
公式ドキュメントでも「データ隠蔽を補強するための機能は何もありません」と記述されており、基本的にすべてのデータやメソッドは外部に公開されており、プライベートなデータやメソッドは存在しません。その代わり、慣習的な命名規則として「__」(アンダーバー)から始まる変数名やメソッド名は、プライベートとみなします。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 |
# カプセル化 class BankAccount( object ): def __init__(self, name, money): self.myname = name self.__mymoney = money # 残高確認confirm関数を作成 def confirm(self): print("名前:" + self.myname) print("残高:" + str(self.__mymoney)) # 銀行からお金をおろすwithdraw関数を作成 def withdraw(self, money): self.__mymoney -= money # 銀行にお金を預けるdeposit関数を作成 def deposit(self, money): self.__mymoney += money # BankAccountクラスをインスタンス化、初期の口座氏名と残高を入力。 account1 = BankAccount("がっちゃん", 10000) account1.confirm() # 銀行にお金を預けます account1.deposit(5000) account1.confirm() # 銀行からお金をおろします account1.withdraw(3000) account1.confirm() # 直接,mynameと__mymoneyを書き換えてみる account1.myname = "gacchan" account1.__mymoney = 1000000000 account1.confirm() |
プログラムの実行結果は以下のとおりです。「myname」はカプセル化されていないので、直接書き換えることが可能ですが、「__mymoney」はカプセル化されているので、直接書き換えることはできません。
エラーオブジェクト
プログラムの処理中にエラーが発生した時、強制的にプログラムが終了されないよう「エラーハンドリング」と言って、あらかじめエラー発生に備えて、エラーを察知してこちらで準備したエラ〜メッセージを出すようにし、プログラムを強制終了させないようにすることです。
以下のプログラムのままだと、入力値によってはエラーが出てプログラムが強制終了になってしまいます。
1 2 3 4 5 6 |
# エラーオブジェクト print('分数の計算をするよ!数字を入力してね!') x = input('分母は?:') y = input('分子は?:') z = int(x)/int(y) print('答えは{}です。'.format(str(z))) |
こんなふうにキチンと割り切れる数字を入力すれば良いのですが、
わざとこんな数字を入れようものなら、エラーが出てプログラムが強制終了されてしまいます。
と、こんなふうにプログラムが強制終了されないように「try except」でエラーキャッチができるようにします。
1 2 3 4 5 6 7 8 9 10 11 12 |
# エラーオブジェクト try: print('分数の計算をするよ!数字を入力してね!') x = input('分母は?:') y = input('分子は?:') z = int(x)/int(y) print('答えは{}です。'.format(str(z))) # エラーを捕まえて、こちらで準備したエラ〜メッセージを吐き出すようにします except ZeroDivisionError: print('ゼロで割っちゃダメだよ!') except ValueError: print('数字以外を入力しちゃダメだって!') |
こうすることで、以下のようにエラーをつかまえて、準備したエラーメッセージをプリントすることができます。これにより、エラーが出ても、プログラムの最初に戻って実行させることも可能になります(while文とか使って、プログラムの最初に戻るように書かないとダメっすけどね)。
以前作成した「抜けているアルファベットを当てるプログラム」をクラス化してみる
Pythonプログラミングの基礎:その2の最後の練習問題で作成した「抜けているアルファベットを当てるプログラム」を、クラス化していきます。そのプログラムが掲載されているブログリンクを以下に貼っておきますので、ご参考にどうぞ。
その前に「回答にかかった時間を計測」する機能を追加
難易度を高めて作成したプログラムに、時間軸を取り入れて、回答にかかった時間を計測できるようにします。これで回答時間が一番早いのは誰かを競えるようになり、ゲーム性が一段と高められますね。
先日作成した「mojiate5.py」を、以下のように修正します。「mojiate5_module.py」は変更ありませんので、そのまま流用します。
※「mojiate5_module.py」のプログラムは、「Pythonプログラミングの基礎:その2(関数)」のブログをご参照ください。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 |
#!/usr/bin/env python3 # -*- coding: utf-8 -*- """ Created on Thu Aug 13 09:38:58 2020 @author: gacchan """ # mojiate5_module.pyの呼び出し from mojiate5_module import show_rules, make_question, judge # datetimeモジュールの呼び出し import datetime # 抜けているアルファベットを当ててみよう(関数をモジュール化した改良版) # アルファベットのリストを準備する(さっきより難易度を上げるためたくさん用意した!) alphabets = ["A", "B", "C", "D", "E", "F", "G", "H", "I", "J", "K", "L", "M", "N", "O", "P", "Q", "R", "S", "T", "U", "V", "W", "X", "Y", "Z", ] # ゲーム開始、ルールの説明の関数を呼び出し show_rules(alphabets) # シャッフルでアルファベットをひとつ抽出 import random random.shuffle(alphabets) # print ( alphabets ) # 解答の選出(抽出したアルファベットを正解とする) answer = random.choice(alphabets) # print ( answer ) # 正解を抜いたアルファベットの文字列出力の関数を呼び出し print ( make_question( alphabets, answer ) ) # 回答開始時刻の取得 t1 = datetime.datetime.now() # 回答:抜けているアルファベットの文字を入力してもらう players_input = input( "抜けているアルファベットを入力してみて!:") # 回答終了時刻の取得 t2 = datetime.datetime.now() # 回答にかかった時間を計算、秒単位で表示させる print("回答にかかった時間は{}秒でした!".format((t2-t1).seconds)) # 判定:正解か不正解かの結果出力の関数を呼び出し judge ( players_input, answer ) |
実行結果は以下のとおりです。キチンと時間表示ができましたね。
これが完成版ですが、「datetime.datetime.now()」がどんな数字を取ってきているのかを確認するためにも、print文を一度「print( t2 – t1 )」にして実行してみてください。
時間計測するタイマークラスと入出力クラスの作成
時間を計測するためのストップウオッチ機能のクラス化と入出力系の機能をクラス化を行います。以下「mojiate6.py」を修正して「mojiate7.py」にして進化させました!
※注意:プログラムを実行した際、「なんか分からんけどエラー出る。引数とか間違ってないねんけどなぁ」というあなた!インデントが正しいかをまず疑いましょう。プログラミング専用のソフトで作成するとたいていのエラーは事前に知らせてくれるのですが、インデントだけは「間違ってるで!」って知らせてくれないので、まずはこれを疑いましょう。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 |
#!/usr/bin/env python3 # -*- coding: utf-8 -*- """ Created on Thu Aug 13 09:38:58 2020 @author: gacchan """ # mojiate5_module.pyの呼び出し from mojiate5_module import show_rules, make_question, judge # datetimeモジュールの呼び出し import datetime # ランダムモジュールの呼び出し import random # ゲーム時間を計測するタイマー(ストップウオッチ)クラスを作成 class Timer: # コンストラクタ def __init__(self): self.timer1 = "" self.timer2 = "" # スタート時間を計測するメソッド def get_start_time(self): self.timer1 = datetime.datetime.now() # 終了時間を計測するメソッド def get_end_time(self): self.timer2 = datetime.datetime.now() # 回答にかかった時間を出力するメソッド def get_time_diff(self): diff = (self.timer2 - self.timer1).seconds return diff # タイマー(ストップウオッチ)インスタンスを生成 t = Timer() # 入出力クラスの作成(print を io.print) class SystemIO: # 入力メソッド def input(self, word): return(input()) # 出力メソッド def output(self, word): print(word) # 入出力をインスタンス化 io = SystemIO() # 抜けているアルファベットを当ててみよう(関数をモジュール化した改良版) # アルファベットのリストを準備する(さっきより難易度を上げるためたくさん用意した!) alphabets = ["A", "B", "C", "D", "E", "F", "G", "H", "I", "J", "K", "L", "M", "N", "O", "P", "Q", "R", "S", "T", "U", "V", "W", "X", "Y", "Z", ] # ゲーム開始、ルールの説明の関数を呼び出し show_rules(alphabets) # シャッフルでアルファベットをひとつ抽出 random.shuffle(alphabets) # 解答の選出(抽出したアルファベットを正解とする) answer = random.choice(alphabets) # 正解を抜いたアルファベットの文字列出力の関数を呼び出し io.output(make_question(alphabets, answer)) # 回答開始時刻の取得 t.get_start_time() # 回答:抜けているアルファベットの文字を入力してもらう players_input = io.input( "抜けているアルファベットを入力してみて!:") # 回答終了時刻の取得 t.get_end_time() io.output("回答にかかった時間は{}秒でした!".format(t.get_time_diff())) # 判定:正解か不正解かの結果出力の関数を呼び出し judge ( players_input, answer ) |
上のプログラムを実行して問題なければ、Timerクラスを「timer_module.py」ファイルに、SystemIOクラスを「systemio_module.py」ファイルに分けていきます。
これにより「mojiate7.py」のプログラムがとても見通し良くなります。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 |
#!/usr/bin/env python3 # -*- coding: utf-8 -*- """ Created on Thu Aug 13 14:17:51 2020 @author: gacchan """ # datetimeモジュールの呼び出し import datetime # ゲーム時間を計測するタイマー(ストップウオッチ)クラスを作成 class Timer: # コンストラクタ def __init__(self): self.timer1 = "" self.timer2 = "" # スタート時間を計測するメソッド def get_start_time(self): self.timer1 = datetime.datetime.now() # 終了時間を計測するメソッド def get_end_time(self): self.timer2 = datetime.datetime.now() # 回答にかかった時間を出力するメソッド def get_time_diff(self): diff = (self.timer2 - self.timer1).seconds return diff |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 |
!/usr/bin/env python3 # -*- coding: utf-8 -*- """ Created on Thu Aug 13 14:20:39 2020 @author: gacchan """ # 入出力クラスの作成(print を io.print) class SystemIO: # 入力メソッド def input(self, word): return(input()) # 出力メソッド def output(self, word): print(word) |
2つのクラスをモジュール化することにより「mojiate7.py」は以下のように見通しがよくなりました。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 |
#!/usr/bin/env python3 # -*- coding: utf-8 -*- """ Created on Thu Aug 13 09:38:58 2020 @author: gacchan """ # 各モジュールの呼び出し from mojiate5_module import show_rules, make_question, judge from timer_module import Timer from systemio_module import SystemIO # ランダムモジュールの呼び出し import random # タイマー(ストップウオッチ)インスタンスの呼び出し t = Timer() # 入出力インスタンスの呼び出し io = SystemIO() # アルファベットのリストを準備する(さっきより難易度を上げるためたくさん用意した!) alphabets = ["A", "B", "C", "D", "E", "F", "G", "H", "I", "J", "K", "L", "M", "N", "O", "P", "Q", "R", "S", "T", "U", "V", "W", "X", "Y", "Z", ] # ゲーム開始、ルールの説明の関数を呼び出し show_rules(alphabets) # シャッフルでアルファベットをひとつ抽出 random.shuffle(alphabets) # 解答の選出(抽出したアルファベットを正解とする) answer = random.choice(alphabets) # 正解を抜いたアルファベットの文字列出力の関数を呼び出し io.output(make_question(alphabets, answer)) # 回答開始時刻の取得 t.get_start_time() # 回答:抜けているアルファベットの文字を入力してもらう players_input = io.input( "抜けているアルファベットを入力してみて!:") # 回答終了時刻の取得 t.get_end_time() io.output("回答にかかった時間は{}秒でした!".format(t.get_time_diff())) # 判定:正解か不正解かの結果出力の関数を呼び出し judge ( players_input, answer ) |
実行結果は以下のとおりです。それぞれのモジュールが呼び出されていることが分かりますね。どうでもいいけど、何べんやっても20秒の壁が破れんわ。
仕上げに「mojiate5_module.py」にある「print文」を無くしたい
せっかく「systemio_module.py」を作ったので、「mojiate5_module.py」にある以下2カ所の「print文」も無くしたいところ。
私は以下のように「SystemIO()」インスタンスの2つめを作って書いてみたのですが、あまり好ましくない対応だそうです。
しかも実行したら、なんだか先ほどと違って「enter」キー何回も押さないと次へと実行しない。ゲームがモタモタしてる。
引数「io」を入れてあげるだけで、「io」インスタンスを使いまわせるという、取ってもシンプルで分かりやす回答を教えてくださいました。以下、修正した「mojiate5_module.py」と「mojiate7.py」になります。「timer_module.py」と「systemio_module.py」は変更なしです。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 |
# -*- coding: utf-8 -*- """ Created on Wed Jun 24 16:22:52 2020 @author: gacchan """ # ランダムモジュールの呼び出し import random # ゲーム開始、ルールの説明を関数化 def show_rules( alphabets, io ): # テスト印刷 io.output('test test test') io.output("{}から{}までのアルファベットを表示します。".format(alphabets[0],alphabets[-1])) io.output("抜けているアルファベットがあるので、その文字を当ててください!") # 正解を抜いたアルファベットの文字列出力を関数化 def make_question( alphabets, answer ): questions = "" for a in alphabets: if a != answer: #ランダムで数字を作って1なら小文字にする r = random.randint( 0, 1 ) if r == 1: a = a.lower() questions += a return questions # 判定:正解か不正解かの結果出力を関数化 # upper()メソッドで小文字入力でも大文字扱いにする def judge(players_input, answer ): judgement = "" if players_input.upper() == answer: judgement = "やったね!正解!" else: judgement = "残念!間違いです!正解は{}です".format(answer) return judgement |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 |
#!/usr/bin/env python3 # -*- coding: utf-8 -*- """ Created on Thu Aug 13 09:38:58 2020 @author: gacchan """ # 各モジュールの呼び出し from mojiate5_module import show_rules, make_question, judge from timer_module import Timer from systemio_module import SystemIO # ランダムモジュールの呼び出し import random # タイマー(ストップウオッチ)インスタンスの呼び出し t = Timer() # 入出力インスタンスの呼び出し io = SystemIO() # アルファベットのリストを準備する(さっきより難易度を上げるためたくさん用意した!) alphabets = ["A", "B", "C", "D", "E", "F", "G", "H", "I", "J", "K", "L", "M", "N", "O", "P", "Q", "R", "S", "T", "U", "V", "W", "X", "Y", "Z", ] # ゲーム開始、ルールの説明の関数を呼び出し show_rules(alphabets, io) # シャッフルでアルファベットをひとつ抽出 random.shuffle(alphabets) # 解答の選出(抽出したアルファベットを正解とする) answer = random.choice(alphabets) # 正解を抜いたアルファベットの文字列出力の関数を呼び出し io.output(make_question(alphabets, answer)) # 回答開始時刻の取得 t.get_start_time() # 回答:抜けているアルファベットの文字を入力してもらう players_input = io.input( "抜けているアルファベットを入力してみて!:") # 回答終了時刻の取得 t.get_end_time() io.output("回答にかかった時間は{}秒でした!".format(t.get_time_diff())) # 判定:正解か不正解かの結果出力の関数を呼び出し io.output(judge ( players_input, answer )) |
出来上がったら最終チェック。実行してみましょう。
バッチリですね。先ほどのモタツキ感もなくなりました。関係ないけど、とうとう回答時間20秒の壁を突破しました!個人的にはなんか嬉しい。
まとめ
Pythonの授業は、ゆっくりと進めてくれているうえに基本構文からの学習なので、とても分かりやすいなぁ、と感じています。プログラム見ただけで、だいたいどんなことが書かれているのかが分かるのも、関係あるのでしょう。
プログラミング超シロウトの私が言うのですから、間違い無いんじゃないでしょうか!?いよいよ基本情報処理技術者試験のプログラミングの選択は「Python」が有力になってきました!
それでは本日も最後までお付き合いいただき、ありがとうございました!