はじめに
Twitterで一時期流行していた 100 Days Of Code なるものを先日知りました。本記事は、初学者である私が100日の学習を通してどの程度成長できるか記録を残すこと、アウトプットすることを目的とします。誤っている点、読みにくい点多々あると思います。ご指摘いただけると幸いです!
今回学習する教材
-
- 8章構成
- 本章216ページ
-
第4章:メタクラスと属性
遅延属性には __getattr__
, __geatattribute__
, __setattr__
を使う
__getattr__
メソッド
通常、オブジェクトのインスタンス辞書に属性が見つからない場合は、AttributeError になります。
class Sample(object):
self.exists = 5
sample = Sample()
print(sample.exists)
print(sample.foo)
実行結果
5
AttributeError: 'Sample' object has no attribute 'foo'
__getattr__
は、オブジェクトのインスタンス辞書に属性が見つからないときに呼び出されるメソッドです。
例を見てみます。
class SampleGetattr(object):
def __init__(self):
self.exists = 5
def __getattr__(self, name):
print('__getattr__メソッドが呼び出されました。')
data = SampleGetattr()
print(data.exists)
data.foo
実行結果
5
# __getattr__メソッドが呼び出されました。
このように、存在しない属性にアクセスしようとすると呼び出されます。
__getattr__
メソッドは、遅延アクセス(実際にオブジェクトが必要になったときに初期化すること)したい場合に便利です。
class LazyGetattr(object):
def __init__(self):
self.exists = 5
def __getattr__(self, name):
print('__getattr__メソッドが呼び出されました。')
value = 12
setattr(self, name ,value) # インスタンス辞書に追加
data = LazyGetattr()
print('Before', data.__dict__)
data.foo
print('After', data.__dict__)
data.foo
実行結果
Before {'exists': 5}
__getattr__メソッドが呼び出されました。
After {'exists': 5, 'foo': 12}
1回目にfooが呼び出されたときにインスタンス辞書に追加し、2回目に呼び出されたときは、既に辞書にあるので`getattr' は呼び出されません。
__getattribute__
メソッド
__getattribute__
メソッドは、インスタンス辞書にアクセスしようとした属性が存在してもしなくても呼び出されます。
class SampleGetAttribute(object):
def __init__(self):
self.exists = 5
def __getattribute__(self, name):
print('__getattribute__メソッドが呼び出されました')
data = SampleGetAttribute()
data.exists
data.foo
data.foo
#__getattribute__メソッドが呼び出されました
#__getattribute__メソッドが呼び出されました
#__getattribute__メソッドが呼び出されました
__setattr__
メソッド
__setattr__
メソッドは属性がインスタンスで代入されるたびに呼び出されます。
class SampleSetattr(object):
def __init__(self):
self.exists = 5
self.x = 2
def __setattr__(self, name, value):
print('__setattr__メソッドが呼び出されました')
super().__setattr__(name, value)
data = SampleSetattr()
data.x = 3
data.y = 4
__setattr__メソッドが呼び出されました # self.exists = 5 の初期化時に呼び出された
__setattr__メソッドが呼び出されました # self.x = 2 の初期化時に呼び出された
__setattr__メソッドが呼び出されました # data.x = 3 で x に代入されたので呼び出された
__setattr__メソッドが呼び出されました # 新たな属性に代入されたので呼び出された
__getattribute__
と__setattr__
の問題点
これらのメソッドは、オブジェクトのあらゆるアクセスで呼び出されてしまうため、無限ループの原因になります。回避するためには、インスタンス辞書から値を取り出す__getattribute__
メソッドでは、super.__getattiribute__
を使い、オブジェクトの属性を変更する__setattr__
メソッドでは、super.__setattr__
を使う必要があります。
親クラスのメソッドを使うことで無限ループに陥らずに済みます。