10
6

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.

PythonAdvent Calendar 2020

Day 21

Pythonの自作クラスに対して、object['key']でデータにアクセスする方法

Posted at

表題の実装方法ですが、パッと思い浮かぶでしょうか?
そもそも何に使うのか、と疑問に思われるかもしれませんが
私の場合は、Pandasと全く同じIFでデータが操作出来るカラム志向のライブラリを
自作してみようとした時にこの問題にぶつかりました。


# この(↓)データアクセスをどう実装すれば良いのだろう?
df['column_name']

表題のような実装方法は、公開ライブラリなどを作る際に求められる知識だと思うのですが
個人的にとても印象深かったので、今回記事を投稿させて頂きました。

※本記事の動作検証には、Pythonのバージョン3.7.3を使用しています。


object['key']の形で自作クラスからデータを取得するには、__getitem__という特殊メソッドを定義する必要があります。


class SampleClass:
    
    def __init__(self):
        self.dict = {'key': 'keyが取得出来ました。'}
        
    def __getitem__(self, key):
        
        if not isinstance(key, str):
            raise Exception()
                
        return self.dict.get(key)

上記のサンプルコードでは、SampleClassがフィールドに辞書型のデータ(self.dict)を持っており、SampleClass['key']を実行した時に__getitem__が呼び出されて、辞書型のデータ内でkeyというキー名とペアになっているデータを取り出す動きになります。

実行例

sample = SampleClass()
print(sample['key'])

逆に、自作クラスに対してobject['key']の形でデータを追加する時の動作を定義するには、__setitem__という特殊メソッドを使います。


class SampleClass:
    
    def __init__(self):
        self.dict = {}
        
    def __setitem__(self, key, value):
        
        if not isinstance(key, str):
            raise Exception()
                
        self.dict[key] = value

上記のサンプルコードでは、SampleClass['key']に対する代入処理を実行した時に、__setitem__が呼び出されて、SampleClassに定義されている辞書型のデータ(self.dict)に指定したキーと値のペアを登録する動きになります。

実行例

sample = SampleClass()
sample['key'] = 'keyが取得出来ました。'

このように、特殊メソッドを用いる事で自作のクラスに様々な振る舞いを与える事が可能になります。他にも、例えば+演算子を使った時の挙動を定義したい場合は__add__メソッドを使う事で実現出来ます。


class SampleClass:
    
    def __init__(self, in_list):
        self.list = in_list
        
    def __add__(self, other):
        
        if not isinstance(other, SampleClass):
            raise Exception()
                
        return SampleClass([*self.list, *other.list])

上記のサンプルコードでは、2つのSampleClassのインスタンスを足した時に(+演算子で処理した時に)、__add__が呼び出されて、2つのインスタンスのリスト(self.list)を結合した新しいSampleClassのインスタンスを生成する動きになります。

実行例

sample1 = SampleClass(['1', '2', '3'])
sample2 = SampleClass(['4', '5'])
sample3 = sample1 + sample2

異なる型のインスタンスの足し算を考慮する必要がある場合には、__add__メソッドに加えて__radd__メソッドを定義する必要があります。
(+演算子から見て前方のクラスに__add__メソッドが定義されていない場合に後方のクラスの__radd__メソッドが動作します。)


class SampleClass:
    
    def __init__(self, in_list):
        self.list = in_list
        
    def __radd__(self, other):
        
        if not isinstance(other, str):
            raise Exception()
                
        return SampleClass([*self.list, other])

上記のサンプルコードは、自作のクラス(SampleClass)が+演算子の後方にある場合に期待する挙動を__radd__メソッドに定義しています。

実行例

sample = SampleClass(['1', '2', '3'])
sample2 = '4' + sample

後、個人的に便利だと感じたのは__repr__メソッドです。
これはインスタンスがprint関数などで呼び出された時に表示する文字列を定義するメソッドで
これを定義しておくと、(おそらく誰にでも経験がある)以下のような表示を回避する事が出来ます。

<__main__.SampleClass object at 0x7f3d06feb3c9>

class SampleClass:
    
    def __init__(self):
        self.list = ['1', '2', '3']
        
    def __repr__(self):
        return '{}'.format(vars(self))
実行例

sample = SampleClass()
print(sample)

10
6
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
10
6

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?