![見出し画像](https://assets.st-note.com/production/uploads/images/63640812/rectangle_large_type_2_9f25477f8d5fc7f2548373b8395a4d55.jpeg?width=1200)
Python資格取得への道のり 18日目
更新が途切れてしまいました。
Noteの内容は下書きしていたのに公開せず、就寝。
今日は2日分です。
・プロパティを使った属性の設定
class Car(object):
def __init__(self, model=None):
self.model = model
def run(self):
print('run')
class Nissan(Car):
def __init__(self, model='PAD', enable_auto_run=False):
#self.model = model
super().__init__(model)
self.enable_auto_run = enable_auto_run
def run(self):
print('super fast')
def auto_run(self):
print('auto run')
nissan = Nissan('Note')
print(nissan.enable_auto_run)
#False
Nissanクラスで定義した通り、「nissan.enable_auto_run」を出力してもFalseになることがわかります。これをTrueに変えるには下記コード
class Car(object):
def __init__(self, model=None):
self.model = model
def run(self):
print('run')
class Nissan(Car):
def __init__(self, model='PAD', enable_auto_run=False):
#self.model = model
super().__init__(model)
self.enable_auto_run = enable_auto_run
def run(self):
print('super fast')
def auto_run(self):
print('auto run')
nissan = Nissan('Note')
nissan.enable_auto_run = True
print(nissan.enable_auto_run)
#True
しかし、この「nissan.enable_auto_run」の値を勝手に変更されたくない場合はどうすればいいのか?
その時に「プロパティ」
つまり読み込みはできて、書き込みができない設定にすれば良い。
ステップは2つ
①「self.enable_auto_run」の前に「_」をつけて「self._enable_auto_run」にする
self._enable_auto_run = enable_auto_run
②def enable_auto_run(self)を定義し、「@property」(デコレーター)をつけて、返り値として「self._enable_auto_run」を返す。
@property
def enable_auto_run(self):
return self._enable_auto_run
全体としてはこのような感じ。
class Car(object):
def __init__(self, model=None):
self.model = model
def run(self):
print('run')
class Nissan(Car):
def __init__(self, model='PAD', enable_auto_run=False):
#self.model = model
super().__init__(model)
self._enable_auto_run = enable_auto_run
@property
def enable_auto_run(self):
return self._enable_auto_run
def run(self):
print('super fast')
def auto_run(self):
print('auto run')
nissan = Nissan('Note')
nissan.enable_auto_run = True
print(nissan.enable_auto_run)
#エラ〜
上記コードはエラーになる。それは、読み込みのみ可であるが「nissan.enable_auto_run = True」と記載してしまっているため、これをコメントアウトして実行すると下記結果が得られる
class Car(object):
def __init__(self, model=None):
self.model = model
def run(self):
print('run')
class Nissan(Car):
def __init__(self, model='PAD', enable_auto_run=False):
#self.model = model
super().__init__(model)
self._enable_auto_run = enable_auto_run
@property
def enable_auto_run(self):
return self._enable_auto_run
def run(self):
print('super fast')
def auto_run(self):
print('auto run')
nissan = Nissan('Note')
#nissan.enable_auto_run = True
print(nissan.enable_auto_run)
#False
しかし、ある特定の条件のみ書き換えたい場合は下記のようなコードとなる。
まずは読み込み専用から書き込みも可にするには、下記を追加。
@enable_auto_run.setter
def enable_auto_run(self, is_enable):
self._enable_auto_run = is_enable
全体だとこんな感じ
class Car(object):
def __init__(self, model=None):
self.model = model
def run(self):
print('run')
class Nissan(Car):
def __init__(self, model='PAD', enable_auto_run=False):
#self.model = model
super().__init__(model)
self._enable_auto_run = enable_auto_run
@property
def enable_auto_run(self):
return self._enable_auto_run
@enable_auto_run.setter
def enable_auto_run(self, is_enable):
self._enable_auto_run = is_enable
def run(self):
print('super fast')
def auto_run(self):
print('auto run')
nissan = Nissan('Note')
nissan.enable_auto_run = True
print(nissan.enable_auto_run)
#True
これだとどんな条件からでも変更できてしまうので、ここから条件を追加。
新たな引数を追加し、passwdがある特定のものであれば、書き込みができる仕様となりました。
下記コードは書き込み可の場合
class Car(object):
def __init__(self, model=None):
self.model = model
def run(self):
print('run')
class Nissan(Car):
def __init__(self, model='PAD', enable_auto_run=False, passwd='123'):
#self.model = model
super().__init__(model)
self._enable_auto_run = enable_auto_run
self.passwd = passwd
@property
def enable_auto_run(self):
return self._enable_auto_run
@enable_auto_run.setter
def enable_auto_run(self, is_enable):
if self.passwd == '456':
self._enable_auto_run = is_enable
else:
raise ValueError
def run(self):
print('super fast')
def auto_run(self):
print('auto run')
nissan = Nissan('Note', passwd='456')
nissan.enable_auto_run = True
print(nissan.enable_auto_run)
#True
下記コードは書き込みエラーの場合
class Car(object):
def __init__(self, model=None):
self.model = model
def run(self):
print('run')
class Nissan(Car):
def __init__(self, model='PAD', enable_auto_run=False, passwd='123'):
#self.model = model
super().__init__(model)
self._enable_auto_run = enable_auto_run
self.passwd = passwd
@property
def enable_auto_run(self):
return self._enable_auto_run
@enable_auto_run.setter
def enable_auto_run(self, is_enable):
if self.passwd == '456':
self._enable_auto_run = is_enable
else:
raise ValueError
def run(self):
print('super fast')
def auto_run(self):
print('auto run')
nissan = Nissan('Note', passwd='111')
nissan.enable_auto_run = True
print(nissan.enable_auto_run)
#ValueError
新たな引数をトリガーに書き込みができるかを判断させることができる
ちなみに「_enable_auto_run」に関しては、オブジェクト生成後もこの値を知っている場合は、アクセスができます。
print(nissan._enable_auto_run)
#False
この場合、変数にアクセスできてしまうので注意。これをさせたくない場合は「_enable_auto_run」を「__enable_auto_run」(アンダースコアが2個)とする。
print(nissan.__enable_auto_run)
#AttributeError:
この結果、生成したオブジェクトからアクセスができないことがわかります。
しかし、「__enable_auto_run」を使用するとオブジェクトからはアクセスが不可ですが、クラス内からはアクセスができます。
class Car(object):
def __init__(self, model=None):
self.model = model
def run(self):
print('run')
class Nissan(Car):
def __init__(self, model='PAD', enable_auto_run=False, passwd='123'):
#self.model = model
super().__init__(model)
self.__enable_auto_run = enable_auto_run
self.passwd = passwd
@property
def enable_auto_run(self):
return self._enable_auto_run
@enable_auto_run.setter
def enable_auto_run(self, is_enable):
if self.passwd == '456':
self._enable_auto_run = is_enable
else:
raise ValueError
def run(self):
print(self.__enable_auto_run)
print('super fast')
def auto_run(self):
print('auto run')
nissan = Nissan('Note', passwd='456')
nissan.run()
#False
#super fast
どういう仕様にしたいのかでプロパティを使用しつつ、アンダースコアに関しても何個で書いていくのかを決定していく。
・クラスを構造体として扱う時の注意点
__(アンダースコアを2個)を使ったとしてそれを出力をしようとしてもエラーになります。しかし、新たに下記コードを追加した際にはオーバーライドされてしまいます。
class Car(object):
def __init__(self, model=None):
self.model = model
def run(self):
print('run')
class Nissan(Car):
def __init__(self, model='PAD', enable_auto_run=False, passwd='123'):
#self.model = model
super().__init__(model)
self.__enable_auto_run = enable_auto_run
self.passwd = passwd
@property
def enable_auto_run(self):
return self._enable_auto_run
@enable_auto_run.setter
def enable_auto_run(self, is_enable):
if self.passwd == '456':
self._enable_auto_run = is_enable
else:
raise ValueError
def run(self):
print(self.__enable_auto_run)
print('super fast')
def auto_run(self):
print('auto run')
nissan = Nissan('Note', passwd='456')
print(nissan.__enable_auto_run)
#エラー
エラーにならない場合
class Car(object):
def __init__(self, model=None):
self.model = model
def run(self):
print('run')
class Nissan(Car):
def __init__(self, model='PAD', enable_auto_run=False, passwd='123'):
#self.model = model
super().__init__(model)
self.__enable_auto_run = enable_auto_run
self.passwd = passwd
@property
def enable_auto_run(self):
return self._enable_auto_run
@enable_auto_run.setter
def enable_auto_run(self, is_enable):
if self.passwd == '456':
self._enable_auto_run = is_enable
else:
raise ValueError
def run(self):
print(self.__enable_auto_run)
print('super fast')
def auto_run(self):
print('auto run')
nissan = Nissan('Note', passwd='456')
nissan.__enable_auto_run = 'XXXXXXXXXXXXXXXXXXX'
print(nissan.__enable_auto_run)
#XXXXXXXXXXXXXXXXXXX
「nissan.__enable_auto_run」はnissan.を入力した時点では候補として出てこないが、書き換えられてしまうことはあるので、コードの中身を保持するとともに自分で書き換えられてしまうことを念頭にコードを書く必要がある。
・ダックタイピング
class Person(object):
def __init__(self, age=1):
self.age = age
def drive(self):
if self.age >= 18:
print('ok')
else:
raise Exception('No drive')
class Baby(Person):
def __init__(self, age=1):
if age < 18:
super().__init__(age)
else:
raise ValueError
class Adult(Person):
def __init__(self, age=18):
if age >= 18:
super().__init__(age)
else:
raise ValueError
baby = Baby()
adult = Adult()
class Car(object):
def __init__(self, model=None):
self.model = model
def run(self):
print('run')
def ride(self, person):
person.drive()
car = Car()
car.ride(baby)
#raise Exception('No drive')
#Exception: No drive
エラーが返ってこない場合
class Person(object):
def __init__(self, age=1):
self.age = age
def drive(self):
if self.age >= 18:
print('ok')
else:
raise Exception('No drive')
class Baby(Person):
def __init__(self, age=1):
if age < 18:
super().__init__(age)
else:
raise ValueError
class Adult(Person):
def __init__(self, age=18):
if age >= 18:
super().__init__(age)
else:
raise ValueError
baby = Baby()
adult = Adult()
class Car(object):
def __init__(self, model=None):
self.model = model
def run(self):
print('run')
def ride(self, person):
person.drive()
car = Car()
car.ride(adult)
#ok
なぜ「ok」が表示されたのか。
「adult = Adult()」が呼び出され、次に「class Adult(Person):」が呼び出されます。
その中でデフォルト引数である「age=18」と「super().__init__(age)」より親メソッドである「class Person(object):」にage=18を渡していく。
その状態で「car.ride(adult)」よりclass carのrideメソッドを行う。
rideメソッドには「person.drive()」を行うため、class personのdriveメソッドを行い、ageは18という形で条件分岐をして「ok」と表示される。
・抽象クラス
def drive(self):
if self.age >= 18:
print('ok')
else:
raise Exception('No drive')
PersonのクラスにあったdriveというメソッドをPersonではなく、BabyやAdultのクラスに書くことだってできる。
その場合は下記コードは削除。
car = Car()
car.ride(adult)
全容は下記の通り。
class Person(object):
def __init__(self, age=1):
self.age = age
class Baby(Person):
def __init__(self, age=1):
if age < 18:
super().__init__(age)
else:
raise ValueError
def drive(self):
raise Exception('No drive')
class Adult(Person):
def __init__(self, age=18):
if age >= 18:
super().__init__(age)
else:
raise ValueError
def drive(self):
print('ok')
baby = Baby()
#baby.drive()
#Exception: No drive
adult = Adult()
#adult.drive()
#ok
class Car(object):
def __init__(self, model=None):
self.model = model
def run(self):
print('run')
def ride(self, person):
person.drive()
「baby.drive()」か「adult.drive()」のどちらかを実行してみると先ほどと同様の結果が得られる。
これは最終行にあるperson.drive()があるため、予想通りの結果が得られる。
しかし、Adultクラスに書かれた下記コードをコメントアウトするとどうなるのか?
# def drive(self):
# print('ok')
全容として下記となる。
class Person(object):
def __init__(self, age=1):
self.age = age
class Baby(Person):
def __init__(self, age=1):
if age < 18:
super().__init__(age)
else:
raise ValueError
def drive(self):
raise Exception('No drive')
class Adult(Person):
def __init__(self, age=18):
if age >= 18:
super().__init__(age)
else:
raise ValueError
# def drive(self):
# print('ok')
baby = Baby()
#baby.drive()
adult = Adult()
adult.drive()
class Car(object):
def __init__(self, model=None):
self.model = model
def run(self):
print('run')
def ride(self, person):
person.drive()
#AttributeError: 'Adult' object has no attribute 'drive'
AttributeErrorという形でAdultにはdriveが定義されていないと指摘される。
その場合、継承するクラスにdriveメソッドというコードを強制的に書かせることができる。
手順としては3つ
①import abcの追加
②親クラスの引数に「(metaclass=abc.ABCMeta)」を追加
③親クラス内に「@abc.abstractmethod」を追加し、継承したクラスに書かせたいメソッド名を定義する。
※メソッドを定義したとしてもここでは、中身はpassでいい。
実際にコードにするとこんな感じ
import abc
class Person(metaclass=abc.ABCMeta):
def __init__(self, age=1):
self.age = age
@abc.abstractmethod
def drive(self):
pass
class Baby(Person):
def __init__(self, age=1):
if age < 18:
super().__init__(age)
else:
raise ValueError
def drive(self):
raise Exception('No drive')
class Adult(Person):
def __init__(self, age=18):
if age >= 18:
super().__init__(age)
else:
raise ValueError
def drive(self):
print('ok')
baby = Baby()
#baby.drive()
adult = Adult()
#adult.drive()
class Car(object):
def __init__(self, model=None):
self.model = model
def run(self):
print('run')
def ride(self, person):
person.drive()
「baby.drive()」か「adult.drive()」のどちらかを実行してみると先ほどと同様の結果が得られる。
またこの状態で実行してもエラーは起きない。
基本的には抽象クラスは使わない。
今日のまとめ
今日はここまで!昨日は公開設定を押さずに寝てしまいました。
明日でクラスを終え、座学に移っていきます!