オブジェクト指向っていいですよね。
特に継承によって機能を拡張できるのが好みです。
クラス継承
Pythonにおけるクラス継承を使った例として以下のようなプログラムを作りました。
class Syain():
def __init__(self, name = ''):
self.name = name
def syukkin(self):
print(f'{self.name}が出勤しました。')
def taikin(self):
print(f'{self.name}が退勤しました。')
def nanoru(self):
print(f'{self.name}と申します。')
class Kanbu(Syain): #Syainクラスを継承
def __init__(self, name = '', yakusyoku = ''):
super().__init__(name) #super()は親のSyainクラスを示す
self.yakusyoku = yakusyoku
def meirei(self, buka):
print(f'{self.name}は{buka.name}に命令しました。')
def nanoru(self): #Syainクラスのnanoruメソッドをオーバーライド
print(f'{self.yakusyoku}の{self.name}と申します。')
Syainクラスは社員を表します。
出勤と退勤することと、自分の名前を名乗るメソッドを持ちます。
Kanbuクラスは幹部を表します。
幹部も社員ですのでSyainクラスを継承することで同じメソッドを持ちます。
また、幹部だけの機能として社員に命令を下すことができます。
幹部は名乗るときには役職名を付けて名乗ります。
さて、こいつを普通に使うならば以下のようになるでしょう。
class Syain():
def __init__(self, name = ''):
self.name = name
def syukkin(self):
print(f'{self.name}が出勤しました。')
def taikin(self):
print(f'{self.name}が退勤しました。')
def nanoru(self):
print(f'{self.name}と申します。')
class Kanbu(Syain):
def __init__(self, name = '', yakusyoku = ''):
super().__init__(name)
self.yakusyoku = yakusyoku
def meirei(self, buka):
print(f'{self.name}は{buka.name}に命令しました。')
def nanoru(self):
print(f'{self.yakusyoku}の{self.name}と申します。')
suzuki = Kanbu('suzuki', '課長')
tanaka = Syain('tanaka')
suzuki.syukkin()
tanaka.syukkin()
suzuki.nanoru()
tanaka.nanoru()
suzuki.taikin()
tanaka.taikin()
suzukiが出勤しました。
tanakaが出勤しました。
課長のsuzukiと申します。
tanakaと申します。
suzukiが退勤しました。
tanakaが退勤しました。
課長(幹部)である鈴木さんはKanbuクラスのインスタンスとして、
田中さんはSyainクラスのインスタンスとして、
それぞれコンストラクタを呼び出します。
名乗らせてみると、鈴木さんは肩書き込みで名乗ってますね。
インスタンスのクラス名を確認
さて、鈴木さんや田中さんもただの社員なのか幹部なのかはどうやって確認しましょう?
所属するクラス名を表示するには以下のようにします。
print(suzuki.__class__)
print(tanaka.__class__)
<class '__main__.Kanbu'>
<class '__main__.Syain'>
__class__って何?
pythonにおいて__xxx__という形式で定義されているのは特殊メソッドや特殊フィールドと呼ばれるもので、
「__class__」はそのオブジェクトのクラス情報を表す特殊フィールドです。
そして、この特殊フィールド、書き換えることが可能なんです
suzuki.Syain('suzuki')
print(suzuki.__class__)
suzuki.__class__ = Kanbu().__class__
print(suzuki.__class__)
<class '__main__.Syain'>
<class '__main__.Kanbu'>
言語仕様としてどうなの?という気がしないでもないですが、
せっかく面白い仕様なので何かに使ってみたいです。
悪用
ということで、これを悪用すると以下のようなことができます。
class Syain():
def __init__(self, name = ''):
self.name = name
def syukkin(self):
print(f'{self.name}が出勤しました。')
def taikin(self):
print(f'{self.name}が退勤しました。')
def nanoru(self):
print(f'{self.name}と申します。')
class Kanbu(Syain):
def __init__(self, name = '', yakusyoku = ''):
super().__init__(name)
self.yakusyoku = yakusyoku
def meirei(self, buka):
print(f'{self.name}は{buka.name}に命令しました。')
def nanoru(self):
print(f'{self.yakusyoku}の{self.name}と申します。')
#鈴木さんも田中さんもSyainクラスのインスタンスとして作成
suzuki = Syain('suzuki')
tanaka = Syain('tanaka')
#鈴木さんのクラス情報を上書き
suzuki.__class__ = Kanbu().__class__
suzuki.meirei(tanaka)
suzukiはtanakaに命令しました。
Syainクラスのインスタンスであった鈴木さんが、命令できています。
途中でクラス情報を書き換えたためです。
イメージとしては、
社員の鈴木さんが幹部代理として権限を持ったような感じでしょうか。
定義していないフィールドは当然存在しない
無理やり幹部の権限を持たせた鈴木さんですが、
役職名はありませんので幹部として名乗らせようとするとエラーになります。
suzuki.nanoru()
AttributeError: 'Kanbu' object has no attribute 'yakusyoku'
当然と言えば当然ですね。