8
8

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 3 years have passed since last update.

__getattr__, ___getattribute__, __setattr__について

Posted at

はじめに

Twitterで一時期流行していた 100 Days Of Code なるものを先日知りました。本記事は、初学者である私が100日の学習を通してどの程度成長できるか記録を残すこと、アウトプットすることを目的とします。誤っている点、読みにくい点多々あると思います。ご指摘いただけると幸いです!

今回学習する教材

  • Effective Python

    • 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__を使う必要があります。
親クラスのメソッドを使うことで無限ループに陥らずに済みます。

8
8
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
8
8

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?