初心者がpythonでクラスをやってみた

# はじめに
今回は「いちばんやさしいPython入門教室」[1]という教材からbouncing balls(跳ねるボール)を使って**クラス**と**オブジェクト**を理解していく
理解するにあたって「クラスとselfと__ init __」[2]を使った


# クラス
``クラス``とはオブジェクトの基となる、機能を保存している箱のようなもの。
ここからオブジェクトを作り出す


## クラスの作成
```python

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
```

これでBallクラスが作成することができた


### クラス作成において重要な知識

`__init__`
コンストラクタと呼ばれるものでオブジェクトを作るときに自動的に呼び出されるもの

`self`
あとから参照するために使う、オブジェクトの対象（性質・引数）を指し示すために必要なもの
　　"self.引数 = 入力された数値"


# オブジェクト
``オブジェクト``とはクラスに属する、具体的な値が定められているもの


## オブジェクトの作成
先ほど`クラスの作成`のつづきを使用する

```python

b = Ball(400, 300, 1, 1, "red")
```

これでBallクラスの中にBallオブジェクトができた
オブジェクトの内部にある変数 = `インスタンス変数`
※オブジェクトを作るとき、selfは記入しない


# クラス・オブジェクトの利用
実例を見て理解を深める
今回は円を動かすプログラムを先に作ってから、三角・四角を動かすプログラムを追加していく
次のプログラムは`クラスの作成`のときに使ったBallクラスの続きと考える


## 円を動かすプログラム

```python

def move(self, canvas):
        # いまの円を消す
        self.erase(canvas)
        # X座標、Y座標を動かす
        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 - 20, self.y - 20, self.x + 20, self.y + 20, 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)
```

このようにBallクラスに`moveメソッド`、`eraseメソッド`、`drawメソッド`を作った


## 三角・四角を作成する時の重要知識

**オーバーライド**
既存のクラスを改良・変更して別のクラスを作るときに使う機能

**継承**
既存のクラスを基に、新しいクラスを作ること
　　"class 新しいクラス名(基のクラス名)"


### オーバーライド・継承の実例

```python

class Rectangle(Ball):
    def erase(self, canvas):
        canvas.create_rectangle(self.x - 20, self.y - 20, self.x + 20, self.y + 20, 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_rectangle` とは四角を描画するときに使うメソッドで、三角を描画するときは `create_polygon （多角形を描画する）`というメソッドを使う

ここで書き換えているのは *eraseメソッド* と *drawメソッド* だけ
*moveメソッド* は *Ballクラス* から**継承**される


# 実行結果

---<img width="960" alt="2020-06-07.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/638190/f57489b3-4ddb-e118-4331-895bec0960df.png">

# コーディング完成形

```python

# coding: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)
        # X座標、Y座標を動かす
        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.delete(self.color)

    def draw(self, canvas):
        canvas.create_oval(self.x - 20, self.y - 20, self.x + 20, self.y + 20, fill=self.color, width=0, tag=self.color)

class Rectangle(Ball):
    def erase(self, canvas):
        canvas.delete(self.color)

    def draw(self, canvas):
        canvas.create_rectangle(self.x - 20, self.y - 20, self.x + 20, self.y + 20, fill=self.color, width=0, tag=self.color)

class Triangle(Ball):
    def erase(self, canvas):
        canvas.delete(self.color)

    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, tag=self.color)

# 円、四角形、三角形を、まとめて用意する
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("800x600")

# Canvasを置く
canvas = tk.Canvas(root, width =800, height =600, bg="#fff")
canvas.place(x = 0, y = 0)

# タイマーをセット
root.after(10, loop)

root.mainloop()
```


# ボールを消す（改定版)

もう一つ、**delete**という機能を使ってボールを消す操作の説明をしていく

参考
「いちばんやさしいPython入門教室 修正」[3] と「tkinterのcanvasを使ってみる」[4]
で理解ができたので貼っておく。


## deleteとは(コーディング例)

**delete**とはカッコ内にtagで指定したオブジェクトを消すことができるもの

　　          `canvas.delete( )`

```python

def erase(self, canvas):
        canvas.delete(self.color)

    def draw(self, canvas):
        canvas.create_oval(self.x - 20, self.y - 20, self.x + 20, self.y + 20, fill=self.color, width=0, tag=self.color)
```

deleteでやることの利点は操作が重くならないこと
（1回ずつ消しているから。白に塗るのだと操作が多くなっていってだんだん重くなっていく）

# 感想
・ __ init __ や self あたりは理解が難しい。どんどん使っていって慣れていく
・途中で出た bindメソッド （イベントと実行したい関数を結ぶもの）も忘れない



# 追記
・キャンバスはX軸は右に、Y軸は下に正の向きをとる
・afterメソッドで指定する時間は 1000分の1 秒である


# 参考文献

[1].「いちばんやさしいPython入門教室」,  大沢文孝著,  株式会社ソーテック社発行
[2]. Webサイト [「クラスとselfと__ init __」] (https://python.ms/class/#はじめに)
[3]. 西谷教授  [「いちばんやさしいPython入門教室」修正] (https://qiita.com/daddygongon/items/53c1206ca4f68a77f1bf)
[4]. qiita記事　[tkinterのcanvasを使ってみる](https://qiita.com/nnahito/items/2ab3ad0f3adacc3314e6)