三角や四角も動かしてみよう!クラスのオーバーライドを覚えるよ!

三角や四角も動かしてみよう!クラスのオーバーライドを覚えるよ!

三角や四角も動かしてみる

前回までで、クラスとオブジェクトを使ってウィンドウ内で反射する円をつくることができたね。次は同じ動きで、円だけじゃなく三角や四角も一緒に動かしてみるよ。

カンタンにクラスを増やせる「オーバーライド」って?

前回は円のクラスを作ったから、三角や四角のクラスも作ればいい。でもここでちょっと考えて見て欲しい。違うのは形を変えるだけだから、そのためだけに三角や四角のクラスをまるごと作るのはムダな感じがする。「形は三角で。あとは円のときと一緒!」みたいな書きかたができたらとっても便利だよね。

実はPythonにはそんな書きかたがちゃんと用意されてるんだ。それが「オーバーライド(override)」っていう機能だよ。先につくったクラスをいったんコピーしてきて、違うところだけを上書きして新しいクラスにするってイメージだ。こういうのを、元のクラスを「継承」して新しいクラスを作るって言い方をするから覚えておこう。

オーバーライドして書いてみよう!

継承元のクラスの準備

クラスをオーバーライドできるのは、メソッド(関数)単位っていうルールがあるんだ。だから、継承元のクラスはメソッドを細かく分けて書かないといけない。分かりにくいと思うから、実際にコードを書いてみるよ。円を動かす部分だけを抜き出して前回と比べてみよう。

▽ 前回の書きかた

class Ball:
    def move(self, canvas):
        #前の円を隠す
        canvas.create_oval(self.x-22, self.y-22, self.x+22, self.y+22, fill="white", width=0)
        #円を次の位置に
        self.x = self.x + self.dx
        self.y = self.y + self.dy
        canvas.create_oval(self.x-20, self.y-20, self.x+20, self.y+20, fill=self.color, width=0)
        #画面端で移動量をかえる
        if self.x >= canvas.winfo_width():
            self.dx = -1
        if self.x <= 0:
            self.dx = 1
        if self.y >= canvas.winfo_height():
            self.dy = -1
        if self.y <= 0:
            self.dy = 1

▽ オーバーライド対応版

class Ball:
    def move(self, canvas):
        #前の円を隠す
        self.erase(canvas)
        #円を次の位置に
        self.x = self.x + self.dx
        self.y = self.y + self.dy
        self.draw(canvas)
        #画面端で移動量をかえる
        if self.x >= canvas.winfo_width():
            self.dx = -1
        if self.x <= 0:
            self.dx = 1
        if self.y >= canvas.winfo_height():
            self.dy = -1
        if self.y <= 0:
            self.dy = 1
    def erase(self, canvas):
        canvas.create_oval(self.x-22, self.y-22, self.x+22, self.y+22, fill="white", width=0)
    def draw(self, canvas):
        canvas.create_oval(self.x-20, self.y-20, self.x+20, self.y+20, fill=self.color, width=0)

前回と違うところは、色をつけた行だ。前回はmove()の中に「円を隠す」とか「円を動かす」とか「円を描く」を全部入れて書いてた。オーバーライド対応版では、18〜21行目で、円を消すのはerase()で円を描くのはdraw()みたいにメソッドを細かく分けたんだね。そして、それを呼び出すために4と8行目を追加したって感じだ。

なんで細かく分けたかというと、さっき言った「オーバーライドできるのはメソッド単位」っていうルールがあるから。この書きかただったら、「erase()だけ上書きしてあとは同じで!」みたいな注文ができるんだ。

オーバーライドして新しいクラスを作る

準備ができたから、いよいよ円のクラスを継承して「四角を描くクラス」を作ってみるよ!オーバーライドの書きかたはこんな感じ。

class 新しいクラス名(基のクラス名):

普通にクラスをつくるときと違うのは、()に継承基のクラス名をいれるだけ。わかりやすいね。それじゃあ実際に四角のクラスを書いてみよう。四角を描くのはcreate_rectangle()という関数を使うよ。

class Rectangle(Ball):
    def erase(self, canvas):
        canvas.create_rectangle(self.x-22, self.y-22, self.x+22, self.y+22, fill="white", width=0)
    def draw(self, canvas):
        canvas.create_rectangle(self.x-20, self.y-20, self.x+20, self.y+20, fill=self.color, width=0)

変えたのは、create_oval()をcreate_rectangle()にしたところ。これだけで「四角を描くクラス」ができちゃうんだね。ちなみにこれを書く位置は「class Ball:」の後じゃないと動かないから注意。プログラムは上から順に実行していくので、継承基をさきに書かないと「そんなクラスないよ??」ってなっちゃうんだね。

おなじように「三角を描くクラス」も作ってみよう。三角を描くのはcreate_polygon()で出来るよ。

class Triangle(Ball):
    def erase(self, canvas):
        canvas.create_polygon(self.x, self.y-24, self.x+24, self.y+24, self.x-24, self.y+24, fill="white", width=0)
    def draw(self, canvas):
        canvas.create_polygon(self.x, self.y-20, self.x+20, self.y+20, self.x-20, self.y+20, fill=self.color, width=0)

オーバーライドしたクラスを使ってみよう!

ここまでで四角と三角のクラスができたから、実際に使ってみよう。前回、円を描くクラスを使うのはこんな書きかただったね。

#円をたくさん作る
balls = [
    Ball(400,300,1,1,"red"),
    Ball(200,100,-1,1,"green"),
    Ball(100,200,1,-1,"blue")
]

def loop():
    #動かして繰り返す
    for b in balls:
        b.move(canvas)
    root.after(10,loop)

このうち2つを四角と三角にするとこんな感じだ。

#円と四角と三角をつくる
balls = [
    Ball(400,300,1,1,"red"),
    Rectangle(200,100,-1,1,"green"),
    Triangle(100,200,1,-1,"blue")
]

def loop():
    #動かして繰り返す
    for b in balls:
        b.move(canvas)
    root.after(10,loop)

変えたのは4,5行目だけ。loop()の中身はまったく変えなくていいってのがポイントだ。オーバーライドを使うとこんなに簡単に書けるんだね。それじゃあここまでのコードをぜんぶまとめるよ!

# codeing=utf-8
import tkinter as tk

class Ball:
    def __init__(self, x, y, dx, dy, color):
        self.x = x
        self.y = y
        self.dx = dx
        self.dy = dy
        self.color = color
    def move(self, canvas):
        #前の円を隠す
        self.erase(canvas)
        #円を次の位置に
        self.x = self.x + self.dx
        self.y = self.y + self.dy
        self.draw(canvas)
        #画面端で移動量をかえる
        if self.x >= canvas.winfo_width():
            self.dx = -1
        if self.x <= 0:
            self.dx = 1
        if self.y >= canvas.winfo_height():
            self.dy = -1
        if self.y <= 0:
            self.dy = 1
    def erase(self, canvas):
        canvas.create_oval(self.x-22, self.y-22, self.x+22, self.y+22, fill="white", width=0)
    def draw(self, canvas):
        canvas.create_oval(self.x-20, self.y-20, self.x+20, self.y+20, fill=self.color, width=0)

class Rectangle(Ball):
    def erase(self, canvas):
        canvas.create_rectangle(self.x-22, self.y-22, self.x+22, self.y+22, fill="white", width=0)
    def draw(self, canvas):
        canvas.create_rectangle(self.x-20, self.y-20, self.x+20, self.y+20, fill=self.color, width=0)

class Triangle(Ball):
    def erase(self, canvas):
        canvas.create_polygon(self.x, self.y-24, self.x+24, self.y+24, self.x-24, self.y+24, fill="white", width=0)
    def draw(self, canvas):
        canvas.create_polygon(self.x, self.y-20, self.x+20, self.y+20, self.x-20, self.y+20, fill=self.color, width=0)

#円と四角と三角をつくる
balls = [
    Ball(400,300,1,1,"red"),
    Rectangle(200,100,-1,1,"green"),
    Triangle(100,200,1,-1,"blue")
]

def loop():
    #動かして繰り返す
    for b in balls:
        b.move(canvas)
    root.after(10,loop)

#ウィンドウを作る
root = tk.Tk()
root.geometry("600x400")
  
#Canvasを作る
canvas = tk.Canvas(root, width=600, height=400, bg="white")
canvas.place(x=0, y=0)
 
#タイマーをつくる
root.after(10, loop)
 
 
root.mainloop()

これで実行すればちゃんと三角や四角も一緒に動くよ!

今日のまとめ

  • クラスの「オーバーライド」を理解しよう!
  • オーバーライドの書きかたをマスターしよう!