1. ホーム
  2. パイソン

[解決済み】Pythonでimmutableオブジェクトを作るには?

2022-04-08 11:20:05

質問

今まで必要なかったのですが、Pythonでイミュータブルなオブジェクトを作るのは少し難しいかもしれないと思いました。あなたは、単に __setattr__ に属性を設定することもできないからです。 __init__ . タプルをサブクラス化するのは、うまくいくコツです。

class Immutable(tuple):
    
    def __new__(cls, a, b):
        return tuple.__new__(cls, (a, b))

    @property
    def a(self):
        return self[0]
        
    @property
    def b(self):
        return self[1]

    def __str__(self):
        return "<Immutable {0}, {1}>".format(self.a, self.b)
    
    def __setattr__(self, *ignored):
        raise NotImplementedError

    def __delattr__(self, *ignored):
        raise NotImplementedError

しかし、その後にアクセスできるのは ab 変数を介して self[0]self[1] というのは、迷惑な話です。

これはPure Pythonで可能ですか?もし不可能なら、C言語の拡張機能でどうすればいいのでしょうか?

(Python 3 でしか動作しない回答も可)。

更新してください。

Python 3.7では、この方法は @dataclass のデコレータを使用することができます。新しく受理された回答を参照してください。

どのように解決するのですか?

フローズンデータクラスの使用

Python 3.7+の場合、Pythonにインストールする前に データクラス を付けて frozen=True オプション これは非常にpythonicで保守性の高い方法で、あなたが望むことを実現します。

というような感じになると思います。

from dataclasses import dataclass

@dataclass(frozen=True)
class Immutable:
    a: Any
    b: Any

として タイプヒンティングが必要 をデータクラスのフィールドに使用しました。 からの任意の typing モジュール .

Namedtupleを使用してはいけない理由

Python 3.7以前では、namedtupleがimmutableオブジェクトとして使用されているのを頻繁に見かけました。これは色々な意味で厄介なのですが、そのうちの1つは __eq__ メソッドは、オブジェクトのクラスを考慮しません。例えば

from collections import namedtuple

ImmutableTuple = namedtuple("ImmutableTuple", ["a", "b"])
ImmutableTuple2 = namedtuple("ImmutableTuple2", ["a", "c"])

obj1 = ImmutableTuple(a=1, b=2)
obj2 = ImmutableTuple2(a=1, c=2)

obj1 == obj2  # will be True

ご覧のように、たとえ obj1obj2 は、たとえフィールド名が異なっていても、異なるものです。 obj1 == obj2 はまだ True . それは __eq__ メソッドはタプルのもので、フィールドの位置を指定してその値だけを比較します。これは、特にこれらのクラスをサブクラス化している場合、エラーの大きな原因となり得ます。