組み込み型のサブクラス化は避けてください。なんらかの理由でオブジェクトの型が変更されたことを知ったとき、あなたはそれを後悔するでしょう。代わりに委任を使用してください。例えば:
import operator as op
class FuzzyDict(object):
def __init__(self, iterable=(), float_eq=op.eq):
self._float_eq = float_eq
self._dict = dict(iterable)
def __getitem__(self, key):
return self._dict[key]
def __setitem__(self, key, val):
self._dict[key] = val
def __iter__(self):
return iter(self._dict)
def __len__(self):
return len(self._dict)
def __contains__(self, key):
return key in self._dict
def __eq__(self, other):
def compare(a, b):
if isinstance(a, float) and isinstance(b, float):
return self._float_eq(a, b)
else:
return a == b
try:
if len(self) != len(other):
return False
for key in self:
if not compare(self[key], other[key]):
return False
return True
except Exception:
return False
def __getattr__(self, attr):
# free features borrowed from dict
attr_val = getattr(self._dict, attr)
if callable(attr_val):
def wrapper(*args, **kwargs):
result = attr_val(*args, **kwargs)
if isinstance(result, dict):
return FuzzyDict(result, self._float_eq)
return result
return wrapper
return attr_val
そして使用例:
>>> def float_eq(a, b):
... return abs(a - b) < 0.01
...
>>> A = FuzzyDict(float_eq=float_eq)
>>> B = FuzzyDict(float_eq=float_eq)
>>> A['a'] = 2.345
>>> A['b'] = 'a string'
>>> B['a'] = 2.345
>>> B['b'] = 'a string'
>>> B['a'] = 2.3445
>>> A == B
True
>>> B['a'] = 234.55
>>> A == B
False
>>> B['a'] = 2.345
>>> B['b'] = 'a strin'
>>> A == B
False
また、ネストされている場合でも機能します。
>>> A['nested'] = FuzzyDict(float_eq=float_eq)
>>> A['nested']['a'] = 17.32
>>> B['nested'] = FuzzyDict(float_eq=float_eq)
>>> B['nested']['a'] = 17.321
>>> B['b'] = 'a string' # changed before
>>> A == B
True
>>> B['nested']['a'] = 17.34
>>> A == B
False
を完全に置き換えるにdict
は、もう少しコードが必要で、おそらく堅牢性を確認するためのテストが必要ですが、上記のソリューションでも多くのdict
機能 (例: copy
、setdefault
、など)get
が提供されます。update
組み込みをサブクラス化してはならない理由について。
この解決策は簡単で正しいように見えますが、一般的にはそうではありません。まず第一に、組み込み型はサブクラス化できますが、それはサブクラスとして使用するために書かれたという意味ではありません。
また、おそらく組み込みメソッドを使用したいと思うでしょうが、これらのメソッドはクラスのインスタンスではなく組み込み型のインスタンスを返します。つまり、その型のすべてのメソッドを再実装する必要があります。また、組み込みで実装されていない他のメソッドを実装する必要がある場合もあります。
たとえば、サブクラス化すると、実装のみであり、これら 2 つのメソッドを安全に再実装list
できると考えるかもしれませんが、それは間違いです! また、次のような式を実装する必要があります。list
__iadd__
__add__
__radd__
[1,2,3] + MyList([1,2,3])
list
ではなく通常の値を返しMyList
ます。
要約すると、ビルトインをサブクラス化すると、最初に考えたよりもはるかに多くの結果が生じ、予期しない型や動作の変更により、予期しないバグが発生する可能性があります。ログ内のオブジェクトのインスタンスを単に出力することはできないため、デバッグも難しくなります。表現は正しいでしょう! この微妙なバグを見つけるには、周囲のすべてのオブジェクトのクラスを確認する必要があります。
特定の状況で、単一のメソッド内でのみ辞書を変換する場合は、サブクラス化のほとんどの欠点を回避できますdict
が、その時点で単純に関数を記述してdict
s を比較してみませんか? dict
比較を行うライブラリ関数に s を渡したい場合を除いて、これはうまくいくはずです。