Python基礎4:クラス(初級編)
概要
Pythonのクラスの説明となります。初級編なのは私が未熟であり、まだまだクラスは奥が深いためとりあえず初級としております。
1.用語の説明
用語が多く混乱しやすいため、参考までに一部の用語を記載しました。
2.概念図(自己流)
一般的に「クラスとは設計図であり、その設計図をもとに作成された物がインスタンスである。」と言われます。最初は”なるほど”と思いますが、いざ自分で作ってみるとよくわからん!となりました。
自分なりで解釈すると「変数と関数を集めてpythonの組み込み関数も使えるもの」という理解です。頭のイメージは下記の通り。
関数とクラスの違いですが、(個人的な意見として)クラスは変数を保持できたり専用の処理を入れたりできるため高度な処理ができるのが便利です。
3.メインコード(Class)
説明用クラスです。説明用のためクラスの中身に意味はありません。
[In]
import string #文字作成用
class Cobject:
cattr1 = 11 #クラス変数
cattr2 = 22 #クラス変数
@classmethod
def classfunc(cls): #クラスメソッド
print(f'クラスメソッドを実行!クラスオブジェクト利用可能->{cls.cattr1}, {cls.cattr2}')
def __init__(self, iattr1): #初期化
print('__init__が実行!')
self.iattr1 = iattr1 #インスタンス.属性で値を呼び出せる
self.iattr2 = 100
self.iattr3 = string.ascii_lowercase #output->'abcdefghijklmnopqrstuvwxyz'
self.count = 26 #後でイテレータで使用する->self.iattr3も文字数
def __call__(self, callattr): #特殊メソッド:実行時にメソッド名が不要
print(f'callメソッド名の実行!{callattr}')
def function1(self, f1attr):
self.f1attr = f1attr
return self.cattr1 + f1attr #クラス変数も頭にselfがないとエラー発生
def function2(self, f2attr):
return self.f1attr + f2attr #クラス変数も頭にselfがないとエラー発生
@property
def function3(self): #オーバーライド試験用->戻り値(return)必須
return self.iattr3 #output->'abcdefghijklmnopqrstuvwxyz'
def makeval(self):
for idx, word in enumerate(self.iattr3): #'abcdefghijklmnopqrstuvwxyz'の26文字+index番号をfor文で回す
setattr(self, word, idx) #これでidxを値に持つwordを変数名としたインスタンス変数ができる。
def __len__(self): #インスタンスをlen()に入れた時に所定の数値を返
return len(self.iattr3) #ここでは上の文字列の長さを返すため数値は26(文字)となる。
def __getitem__(self, index): #インスタンスをlen()に入れた時に所定の数値を返す
self.iattr3_list = list(self.iattr3) #文字列をリスト化
return self.iattr3_list[index]
def __iter__(self): #イテレーター自身を返す。
return self
def __next__(self): #コンテナの次の要素を返す
if self.count <=0:
raise StopIteration
self.count -= 1
return self.iattr3_list[25-self.count]
4.クラスの詳細説明
詳細説明より、個々に処理をしてどのような動作になるかを示します。
4-1.エラーになる処理(インスタンス化前)
下記はすべてエラーになります。そのことから下記が分かります。
[In]
#Cobject.function1(5) #インスタンス化前にメソッドは使えない->TypeError: function1() missing 1 required positional argument: 'f1attr'
#Cobject() #インスタンス化しないと、callメソッドと初期化を理解できない->TypeError: __init__() missing 1 required positional argument: 'iattr1'
#print(Cobject.iattr1) #インスタンス変数はインスタンス化しないと存在しない->AttributeError: type object 'Cobject' has no attribute 'iattr1'
4-2.クラス変数とクラスメソッド
クラス変数とクラスメソッドはインスタンス化前に使用できます。
[In]
print('クラス変数:', Cobject.cattr1, Cobject.cattr2) #クラス変数の確認
Cobject.classfunc() #クラスメソッド:インスタンス化前にも実行可能
[Out]
クラス変数: 11 22
クラスメソッドを実行!クラスオブジェクト利用可能->11, 22
4-3.インスタンス化、変数、メソッド
インスタンス化するとインスタンス変数およびメソッドが使用できます。なお、インスタンス化時に__init__()メソッドは自動で実行されます。
[In]
ins1 = Cobject('初期化時の定数')
print('ins1.iattr1の値:', ins1.iattr1) #インスタンス変数の確認
ins1('call用変数を追加') #callメソッド
[Out]
__init__が実行!
ins1.iattr1の値: 初期化時の定数
callメソッド名の実行!call用変数を追加
あるメソッド戻り値を別メソッドで使用するなら事前にメソッド実行が必要
[In]
# ins1.function2(100) #先に持ってくるとエラー:AttributeError: 'Cobject' object has no attribute 'f1attr'
ins1.function1(2) #クラス変数+入力値
ins1.function2(100) #後はOK
[Out]
13
102
メソッドはメソッド名()で実行するが@propertyだと()が不要である。propertyをつけたメソッドの戻り値を更新したいなら@method.setteer
[In]
print(ins1.function3) #@propertyにより()を付けずに呼び出せる
# ins1.function3=100 #更新できるようにするためには@関数名.setterが必要:エラー:AttributeError: can't set attribute
[Out]
abcdefghijklmnopqrstuvwxyz
4-4.組み込み関数(setattr(), getattr())
クラスにイテレータを渡してインスタンス変数を作成するならsetattr()、値を取得するならgetattr()、削除ならdelattr()で処理可能。
[In]
ins1.makeval() #setatterを使用してインスタンス変数を作成->a~zの属性に対して0~25の整数が与えられている
print(ins1.a, ins1.c, ins1.t, ins1.z) #適当な属性を与えて値を取得
print(getattr(ins1,'a'), getattr(ins1,'c'), getattr(ins1,'t'), getattr(ins1,'z')) #同上※getattr(インスタンス, 属性)で取得
delattr(ins1, 'a') #インスタンス変数を削除
print(ins1.a) #上で削除したためエラー発生
[Out]
0 2 19 25
0 2 19 25
AttributeError: 'Cobject' object has no attribute 'a'
4-5.特殊メソッド(__init__, __call__以外)
インスタンスに下記のような処理したい場合に使用(一部未紹介)
一部の結果は下記の通り
[In]
print('文字数を数値に取っているため26を返す->', len(ins1))
print([ins1[i] for i in range(6)]) #__getitem__を使用することで辞書のような形でインスタンスから値を取得することができる。
print([i for i in ins1]) #__iter__と__next__を組み合わせることでインスタンスをイテレーターとして使用可能である。
print([i for i in ins1]) #イテレーターが消費されるため2回目では空の値が出力される。->再利用する場合はクラスを2つに分割する
[Out]
文字数を数値に取っているため26を返す-> 26
['a', 'b', 'c', 'd', 'e', 'f']
['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']
[]
なお、クラスオブジェクトは特殊メソッドを持てない??と思う。
[In]
print('文字数を数値に取っているため26を返す->', len(Cobject))
[Out]
TypeError: object of type 'type' has no len()
5.継承
親クラスの変数やメソッドと同じものを引き継いで新しいクラスを作成することを継承と言います。ベースのクラスだけ作成しておいて機能を追加する時に使用します(Pytorchのnn.Moduleなど)。
5-1.継承テスト1:関数追加、メソッドの継承
そのまま継承して2つメソッド追加してみました。新規クラス内で親クラスのメソッドを呼び出す場合はsuper()をつけます。結果として下記が確認できました。
[In]
class Overrideobj(Cobject): #クラス名(基底クラス名)
def addfunc(self, x):
print(f'オーバーライドしたクラスのメソッド->変数{x}')
def function2(self):
print('function2メソッドはオーバーライドしました。')
def makeupperval(self):
lowercases = super().function3 #基底クラスのメソッドを呼び出したい場合はsuper()をつける
return lowercases.upper() #function3はpropertyにしたため()は不要
#インスタンス化前*************************
print('クラス変数:', Overrideobj.cattr1, Overrideobj.cattr2) #クラス変数の確認
Overrideobj.classfunc() #クラスメソッド:インスタンス化前にも実行可能
print('*'*100) #処理時に線を入れるだけ
#インスタンス化**************************
ins2 = Overrideobj('初期化時の定数')
print('ins2.iattr1の値:', ins2.iattr1)
ins2('Overrideでcallメソッド') #callメソッド
print(ins2.function1(2)) #元のメソッドも使用可能
ins2.function2()
# print(ins2.function2(100)) #TypeError: function2() takes 1 positional argument but 2 were given
print(ins2.makeupperval()) #super()で基底クラス(親クラス)から継承して修正した結果を出力
#特殊メソッドの確認
print('文字数を数値に取っているため26を返す->', len(ins2))
# print('文字数を数値に取っているため26を返す->', len(Overrideobj)) #クラスオブジェクトはエラー:TypeError: object of type 'type' has no len()
print([ins2[i] for i in range(6)]) #__getitem__を使用することで辞書のような形でインスタンスから値を取得することができる。
print([i for i in ins2]) #__iter__と__next__を組み合わせることでインスタンスをイテレーターとして使用可能である。
print([i for i in ins2]) #イテレーターが消費されるため2回目では空の値が出力される。->再利用する場合はクラスを2つに分割する
[Out]
クラス変数: 11 22
クラスメソッドを実行!クラスオブジェクト利用可能->11, 22
****************************************************************************************************
__init__が実行!
ins2.iattr1の値: 初期化時の定数
callメソッド名の実行!Overrideでcallメソッド
13
function2メソッドはオーバーライドしました。
ABCDEFGHIJKLMNOPQRSTUVWXYZ
文字数を数値に取っているため26を返す-> 26
['a', 'b', 'c', 'd', 'e', 'f']
['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']
[]
5ー2.継承テスト2:__init__()の扱い
結果のまとめとコードは下記の通り
[In]
class Overrideobj2(Cobject): #クラス名(基底クラス名)
def __init__(self, newattr):
self.newattr = newattr
ins3 = Overrideobj2(999)
print(ins3.newattr)
print(ins3.iattr1)
print(ins3.iattr2, ins3.iattr3, ins3.count)
[Out]
999
AttributeError: 'Overrideobj2' object has no attribute 'iattr1'
AttributeError: 'Overrideobj2' object has no attribute 'iattr2'
[In]
class Overrideobj3(Cobject): #クラス名(基底クラス名)
def __init__(self, iattr1, newattr):
super().__init__(iattr1)
self.newattr = newattr
ins3 = Overrideobj3('iaatr1用の引数', 777)
print(ins3.newattr)
print(ins3.iattr1, ins3.iattr2, ins3.iattr3, ins3.count)
[Out]
__init__が実行!
777
iaatr1用の引数 100 abcdefghijklmnopqrstuvwxyz 26
参考資料
あとがき
自分が作りたいものを勉強する時クラスをどこまで理解しないといけないかがわからないのが独学の辛さやね・・・
あと、プログラミングの勉強すると知らない単語を知らない単語で説明されるループが多いから本当に理解が難しい。
この記事が気に入ったらサポートをしてみませんか?