0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

propertyに向かってsetattrする

Last updated at Posted at 2024-08-04

またpythonの小ネタです。

概要

  1. クラスのプロパティにsetattrしてもプロパティのsetterは起動する
  2. おまけ要素:__dict__に書き込んでもプロパティに書き込めない

落ち着いて考えてみれば「そりゃそうだろうな」って話ですがちょっと試します。

実験してみる

class PropClass:
    """プロパティvalueを持つクラス"""
    def __init__(self) -> None:
        """プロパティの初期化"""
        self._value = 'no changed.'
    
    @property
    def value(self):
        """ getter プロパティをそのまま返す"""
        return self._value
    
    @value.setter 
    def value(self, v:str) -> None:
        """ setter vを加工してsetする"""
        self._value = f'"{v}"がsetterに渡された'

    def __repr__(self) -> str:
        return f'{self.value}'

if __name__ == '__main__':

    # 普通に代入する
    instance = PropClass()
    instance.value = '普通に代入する'
    print(instance)
    
    # setattrで書き込む
    instance = PropClass()
    setattr(instance, 'value', 'setattrで書き込む')
    print(instance)

もちろん出力はこうです。

"普通に代入する"がsetterに渡された
"setattrで書き込む"がsetterに渡された

そもそもpythonのドキュメントにはこう書いてあります。

setattr(x, 'foobar', 123) は x.foobar = 123 と等価です。

なので、素直に読めばinstance.value = '文字列'setattr(instance, 'value', '文字列')は等価です。しかし「プロパティの場合でもそう」とは書いていないので、「本当にそうなのか」と試してみました。結果はそのままドキュメントの通りでした。
実際に使うシーンはあまりないと思いますが、pythonの標準ライブラリの中でも任意に与えたオブジェクトにsetattrしてくるケースはあります。例えばargparseなんかがやってきます。 1

その他には、使う機会はあまりないはずですがこんなケースも考えられます。

for k,v in somedict.items():
    setattr(obj, k, v)

dictの中身を一方的にsetattrされるコードなので何を書きこまれるか解ったものではありません。この動作に対してpropertyのsetterによって介入することができます。2

おまけの検証 propertyと__dict__

さて、ここまでのテストコードでsetattrはpropertyのsetterと共存可能だと解りました。
しかし、pythonには他にもpropertyに侵攻できそうな方法があります。そのうちのひとつとして、instance.__dict__からアクセスする方法を試しました。

    instance = PropClass()
    instance.__dict__['value'] = 'dictにプロパティ名を指定して書き込む'
    print(instance)

出力はno chabged.となり、プロパティのsetterも呼ばれません。しかし、instance.__dict__['value']にはdictにプロパティ名を指定して書き込むの値が設定されています。ここは詳しく調べると何故こうなるかわかるはず。なお
元々__dict__には書き込むべきではないので、やらないのが無難でしょう。なお書き込み前にinstance.__dict__['value']を呼び出すとKeyErrorが返ります。プロパティは最初から__dict__には含まれていません。

  1. しかしargparseには別途Actionクラスの仕組みがあるので、そっちを使った方がいいでしょう。

  2. こんな手が必要な設計自体が若干疑問ではありますが、時にはあると思うので知っておいて損はないかなと。

0
0
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
0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?