Python でクラスを使ったサンプルコードをまとめておきます。
加えて、関数に対し、クラスを作るメリットについてポイントをまとめておくことにします。
環境: Python 3.x
背景
Python でプログラミングを覚えるとき、最初は def で関数を作ると思いますが、クラスでメソッドを作ることもあると思います。
クラス class の使い方について、忘れてしまったりもするので、コピー&ペーストですぐに使えるよう、サンプルコードの形でまとめておくことにします。
なお、サンプルコードでは Python のスクリプトを2つにわけましたが、2つめのスクリプトの冒頭の2行(import している行)に、1つめのスクリプトを貼りつけても、そのまま動くと思います。
サンプルコード
“readwrite1.py”
# -*- coding: utf-8 -*-
class Rw1:
def __init__( self ):
self.path0 = ""
def set_path1( self, path1 ):
self.path0 = path1
def read1( self, file0 ):
file1 = self.path0 + file0
with open( file1, 'r', encoding='utf-8' ) as f1:
str1 = f1.read()
return str1
def write1( self, file0, str1 ):
file1 = self.path0 + file0
with open( file1, 'w' ) as f1:
f1.write( str1 )
return 0
“test1.py”
# -*- coding: utf-8 -*-
from readwrite1 import Rw1
rw1 = Rw1()
rw1.set_path1( "C:/user/test/" )
file1 = "text1.txt"
str1 = rw1.read1( file1 )
print( str1 )
print( rw1.path0 )
file2 = "text2.txt"
rw1.write1( file2, str1 )
“text1.txt”
abc
def
使い方
スクリプトに記載したパス “C:/user/test/” を参考に、PC にフォルダ “test” を作ってください。
パスを変えたい場合は、”C:/user/test/” の部分を修正してください。
その中に、”readwrite1.py”, “test1.py”, “text1.txt” の3つのファイルを作成し、上のファイルを貼りつけて保存してください。
コマンドプロンプトで、python test1.py として実行すると、text1.txt の内容を読みだして表示し、text2.txt として保存します。
まずは動くか確認し、動いたら、好きなように修正をしてください。
たとえば、rw1.set_path( ) で、ファイルの読み書きをするパスを設定していますが、class の __init__ のところに移してデフォルトとなるパスを設定してしまえば、この行は不要となり、スクリプトがより簡単になります。
スクリプトの動き
・ Python のスクリプトを実行すると、まず、クラスを呼び出してインスタンス(=オブジェクト)rw1 を生成します。
・ つぎに、最初にテキストが入っているパスを設定します。
ファイル名を指定すると、フルパスを都度気にすることなく、テキストファイルの読み書きが自由にできるようになります。
・ 設定したパスにある “text1.txt” を読み込んで、”text2.txt” というファイル名で同内容をかき出します。
・ ファイルの読み書きを実行する基本的なプログラムにしています。うまく動いたら、スクリプトを修正してアレンジしてみてください。
クラス class のメリットは何か
関数のみでプログラミングするとどうなるか
class を使わずに関数のみで平易に記載すると、以前に作成した下記のサンプルのようになります。
テキストファイルを入出力する 【Python】
しかしこの場合、読み書きを行うパス自体は、それぞれ、関数の実行後、メモリから解放されてしまいます。
したがって、関数を使うたびにフルパスを指定する必要があります。
関数にパスの文字列を渡し、次回もこのパスは使うので、設定したパスをしばらく覚えておきたい、といったことができないわけです。
関数の中にパスを埋め込んでも、パスを変更するたびに読み書きの2つの関数を修正する必要が生じるため、煩雑です。
グローバル変数を使おうとすると、たとえば、読み書き用の作業フォルダを3つ設定して使い分けることにし、同時に読み書きしたい、となったとき、処理が複雑化してしまいます。
結局、関数のみしか使わないとすると、覚えておきたいパラメータ(動的に変わるかもしれないが維持しておきたい変数)は、スクリプト本体側(test1.py)で維持しておく必要が生じます。
そして、スクリプト本体側で、パス(path1, path2, …)を使った別の処理(本来、作りたかった処理)を加えていくと、処理が混ざって複雑度が増すので、バグの要因となります。
クラスを使うとどうなるか
そこで、例えば、ファイルの読み書きのような共通性のある処理については、クラスを作ることにします。
2つの関数に加え、パスを保持する文字列も、一つのクラスにまとめてしまいます。
すると、作成したインスタンス rw1 のみを気にするだけで、処理をメインのスクリプトからは完全に分離できます。
「インスタンスを作る」というのは、不揮発性のメモリ(RAM)上に領域を確保する、と考えると理解できると思います。
作成したインスタンス rw1 に積極的にアクセスしない限り、rw1 の内部のパラメータが変わることはない。
そして、スクリプト本体側で path1, path2 をいくら使っても、また書き換えても、読み書きの処理 rw1 に影響することはない、ということになります。
バグが入り込む余地がなくなって、本来作りたいプログラミングに集中できることになります。
クラスを定義するときに出てくる self とは何か
クラスでメソッド(関数)定義するとき、かならず、「class … : def … ( self, … ): … 」のパターンになります。
このとき出てくる self とは何か、疑問になると思います。
self は、一般には、「インスタンスそのもの」、「オブジェクトそのもの」となっています。
しかし、概念的な用語だけで説明されてもプログラムとの対応がわからないため、最初のうちは意味がわからないと思います。
そこで、self は、プログラムでドット “.” の前に出てくるオブジェクト名に対応する、と考えると理解しやすいと思います。
上記のサンプルでいえば、クラスを使う段階で出てきたインスタンス(オブジェクト)の名前 “rw1″ が self に対応しています。
インスタンス(オブジェクト)は、いくつでも定義できます。
たとえば、”rw1″ と同様に、”rw2″、”rw3″ といった任意の名前でいくつでもインスタンス(オブジェクト)を定義(生成)できます。
しかし、class を定義している段階では、オブジェクトがどのような名前で、いくつ設定されるかはわかりません。
そこで、class を定義している段階では、self という文字列でオブジェクト名称を仮おきし、そのオブジェクト self にぶら下げる形で、インスタンス変数やメソッド(関数)を定義していく記載ルールにしている、と考えると理解しやすいと思います。
なお、JavaScript を使っていると、ときどき、”this” が出てきます。
関数の中などで唐突に “this” が出てくるため、違和感があると思いますが、上記の “self” に対応している(オブジェクト名に対応している)と考えられます。
つまり、JavaScript で出てくる “this” は、Python でクラスを定義する際の “self” (オブジェクト名)に対応している、と考えることができます。
両者とも、天下り式に唐突に出てくるという点で共通していますし、オブジェクト名と考えると、すっきり理解ができると思います。
クラスを定義するときに出てくる __init__ とは何か
init も、上記の同様の考え方で理解ができます。
上記のサンプルで、”rw1 = …” を実行した時点(オブジェクトを生成した時点)で、最初に def __init__( self ) が自動で実行されます。
オブジェクトを生成した時点で自動実行できる特殊関数(特殊メソッド)が書けるようにしておかないと、ユーザーは、”rw1 = … ” を記載したあと、その都度、初期化のためのメソッド(例: rw1.initialize1( … ) )を実行する必要が生じうることになります。
プログラムが冗長となりますし、記載抜けなどのバグの要因となります。Python の言語仕様として、__init__ のような特殊なメソッドが定義できるほうが、合理的といえます。
まとめ
Python でクラスを使うサンプルコードについてまとめました。
関数をいくつか作ってきたけれども、関数が増えてきてだんだんわからなくなってきた。パラメータも増えてきたけれども、共通化してまとめてしまいたい、となった場合、class を作ってまとめてしまえばよい、ということになります。
クラス class を定義し、関係しそうな関数とパラメータをひとくくりにまとめてしまいます。
すると、他のスクリプトから完全に分離でき(読み書きのような定型処理は完全に忘れることができ/分離度を上げることができ)、本来のプログラミングだけに集中できる、バグが生じる要因をなくせる、という点で、メリットがあるといえます。
関連リンク
・ 関数とメソッドの違い 【Python】
・ 他のスクリプトの関数を実行する 【Python】
・ 「参照渡し」に関するまとめ 【Python】