[Work/Class/Python3の基礎とデータ処理/1_PythonBasic]

Pythonの関数とクラスの定義

関数の定義

def 関数名(引数をカンマで区切って定義):
    # 関数の処理内容...
    # 戻り値がある場合
    return 戻り値

というように定義する.

Pythonは関数型言語の側面も持っているので,クラスのインスタンスメンバ関数でない関数の場合,必ずreturnで何らかの値を返すようにしておく方が良い.(クラスのインスタンスメンバ関数の場合で返すものがない場合は,後述のselfを返すのが一番わかりやすい)

Pythonでは複数の戻り値を指定することができる.例えば,

def multi_div_return(x, y):
    return x*y, x/y

a, b = multi_div_return(6, 2)

のように定義し呼び出す.

グローバル変数

「クラスを定義してそれを駆動する」以前のPythonコードで,関数外部で宣言初期化された変数はグローバル変数となる.関数内部でグローバル変数にアクセスするためには,アクセスする前にglobal グローバル変数名という式を実行しておく必要がある.

クラスの定義

クラス定義

class クラス名(親クラス):
    # クラス定義の内容...

と定義する.親クラスがない時(正確にはobjectクラスが親の時)は,小括弧の中はobjectにする,空にする,小括弧をつけない,のどれかを選択する.

selfキーワード

Javaでthisに相当する「このクラスの」もしくは「このインスタンスの」を表すキーワードはselfである.

コンストラクタの定義

def __init__(self, コンストラクタが取る変数...):
    # コンストラクタ関数内の処理...

と書くことで,クラスのコンストラクタを定義する.

コンストラクタは「何も返さない」ことが決められているので,return Noneを最後に実行する.Noneキーワードに注意.

内部で親クラスのコンストラクタを実行したい時には,

super().__init__(親クラスのコンストラクタの引数からselfを除いた引数)

という形で親クラスのコンストラクタにアクセスする.(Python2では書法が異なる)

インスタンスメンバ変数の定義

コンストラクタや他の関数内部でself.変数名と宣言すると,インスタンスメンバ変数となる.

クラスのインスタンスメンバ変数は,基本的にprivateであるべきである.privateな変数の定義は,変数名がアンダースコア2つ連続で始まる規則になっており,__variable_nameと書かれるので,インスタンスメンバ変数はself.__variable_nameと定義する.

インスタンスメンバ関数の定義

インスタンスメンバ関数の定義では,第一引数が必ずselfになる.ただし呼び出しの時は第一引数を書かずに第二引数から書き始める.

静的関数と静的変数

インスタンスを生成しなくても使えるクラス内の定数(Javaでいう所のpublic static定数)は,selfをつけずにコンストラクタや関数外で宣言する.

同様に静的関数は以下のように宣言する.

@staticmethod
def my_function(引数):
    # 処理内容....

静的関数はselfを引数に取らないので,書かない.つまり静的関数は(当たり前だが)インスタンスメンバ変数にはアクセスできない.

クラス関数

上記の静的関数と静的変数の他に「クラス関数」(クラスメソッド)というものもある.これは第一引数にselfを取る静的関数のことで,静的変数にも内部的にアクセスできる.が,ややこしくなるので書かない方がよい.クラスの静的定数には「クラス名.定数名」でアクセスする方が明確である.

ソースコードが関数定義やクラス定義から始まる時のmain関数

Pythonのソースコードは,上から順次実行されるが,関数定義やクラス定義がある場合,まず初めに関数定義やクラス定義を書いて,実行開始main関数を次に書く.main関数の定義は以下のように行う.

# ...
# ここまでクラス定義

if __name__ == '__main__':
   # ソースコードの実行開始時に読まれる関数
   # この中でクラスのインスタンスを作り,処理する.
   # ...

コード例

# -*- coding: utf-8 -*-
# BounceAndColoredBall.py
import sys # 今回はアプリケーション起動時の引数を取ってくるために使う
import math # 数学関係
import random # ランダム用ライブラリ

# GUIに使うQt5のライブラリ
# 本筋とは関係ないので,「そんなもんか」と捉えておけば良い
from PyQt5.QtCore import * 
from PyQt5.QtGui import *
from PyQt5.QtWidgets import *

class BounceBall():
    # コンストラクタ
    # 引数の頭が必ずselfになることに注意
    def __init__(self, windowWidth, windowHeight):
        self.__windowWidth = windowWidth #privateメンバ変数は「self.__変数名」
        self.__windowHeight = windowHeight
        self.__x_pos = math.floor(random.random() * self.__windowWidth)
        self.__y_pos = math.floor(random.random() * self.__windowHeight)
        if random.random() > 0.5 :
            self.__x_direction = 1
        else:
            self.__x_direction = -1
        if random.random() > 0.5 :
            self.__y_direction = 1
        else:
            self.__y_direction = -1

        return None # コンストラクタはNoneを返すと決められている

    def updateParameters(self):
        self.__x_pos += 10 * self.__x_direction
        if self.__x_pos > self.__windowWidth :
            self.__x_direction = -1
        elif self.__x_pos < 0 :
            self.__x_direction = 1

        self.__y_pos += 10 * self.__y_direction
        if self.__y_pos > self.__windowHeight :
            self.__y_direction = -1
        elif self.__y_pos < 0 :
            self.__y_direction = 1

        return self

    def get_x(self):
        return self.__x_pos

    def get_y(self):
        return self.__y_pos


class ColoredBall(BounceBall): # BounceBallを継承して,色情報を持たせたクラス
    # コンストラクタのオーバーライド
    def __init__(self, windowWidth, windowHeight):
        # 最初に親クラス(BounceBall)のコンストラクタを呼び出す
        super().__init__(windowWidth, windowHeight)

        # 子クラス独自のコンストラクタの内容を書いていく
        self.__r_color = math.floor(random.random() * 155) + 100
        self.__g_color = math.floor(random.random() * 155) + 100
        self.__b_color = math.floor(random.random() * 155) + 100
	
	return None

    # 子クラス独自の関数を定義
    def get_r(self):
        return self.__r_color
    def get_g(self):
        return self.__g_color
    def get_b(self):
        return self.__b_color

# 実際に描画するウィンドウを呼び出して描画するクラス(JavaではJFrameに相当する)
# 本筋には関係ないので「こんなもんか」と眺めるだけで良い
class MyWidget(QWidget):
    # コンストラクタのオーバーライド
    def __init__(self, parent=None):
        super().__init__(parent)

        self.__ball_list = list([])

        for i in range(10):
            a_ball = BounceBall(640, 480)
            self.__ball_list.append(a_ball)

        self.__colored_ball_list = list([])
        for i in range(10):
            a_ball = ColoredBall(640, 480)
            self.__colored_ball_list.append(a_ball)

        self.resize(640, 480)
        self.show()

	return None


    # 描画イベント関数(Processingでいうdraw関数)のオーバーライド
    def paintEvent(self, event):
        painter = QPainter(self) # QPainterのインスタンスを作成
        painter.setPen(Qt.black) # Stroke(枠線)に相当
        # 画面のRefresh
        painter.setBrush(Qt.white) # 中の色に相当
        painter.drawRect(0, 0, 640, 480);

        for a_ball in self.__ball_list:
            a_ball.updateParameters()
            painter.drawEllipse(a_ball.get_x(), a_ball.get_y(), 20, 20)

        for a_ball in self.__colored_ball_list:
            a_ball.updateParameters()
            painter.setBrush(QColor(a_ball.get_r(), a_ball.get_g(), a_ball.get_b()))
            painter.drawEllipse(a_ball.get_x(), a_ball.get_y(), 20, 

	# 最後にupdate関数を呼び出すと描画される
        self.update()

# main関数        
if __name__ == '__main__':
        app = QApplication(sys.argv)
        w = MyWidget() # 自分で作ったクラスのインスタンスを作る
        w.raise_()
        app.exec_() #paintEventが回るようにする