組み込み型のサブクラス化は避けてください。あなたは、あなたのオブジェクトが何らかの未知の理由のためにタイプを変更したことを知ったときにそれを後悔します。代わりに委任を使用してください。たとえば、次のように
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
ための完全な交換が少しより多くのコードを必要とし、おそらくいくつかのでしょうそれがどれほど堅牢であるかをテストすることができますが、上記のソリューションでさえ、多くの機能を提供します(例えば、copy
、setdefault
、get
、update
など)
あなたはビルトインのサブクラスはならない理由について。
この解決策は簡単で正しいと思われますが、一般的にはそうではありません。 まず、組み込み型をサブクラス化することはできますが、サブクラスとして使用するように記述されているわけではありませんので、何らかの作業をするために思ったよりも多くのコードを書く必要があります。
また、組み込みのメソッドを使用することもできますが、これらのメソッドはクラスのインスタンスではなく組み込み型のインスタンスを返します。つまり、すべての単一メソッドを再実装する必要がありますタイプの。また、組み込み関数で実装されていない他のメソッドを実装する必要があることもあります。例えば
、あなたはlist
だけ__iadd__
と__add__
を実装しているので、あなたがこれらの2つの方法を安全に再実装されます、と思うかもしれませんが、あなたは間違っているlist
サブクラス化!
[1,2,3] + MyList([1,2,3])
MyList
list
正常ではないが返されます:あなたはまた、それ以外のような表現は、__radd__
を実装する必要があります。
要約すると、組み込み関数をサブクラス化すると、最初に考えたよりも多くの結果が発生し、予期せぬ種類や動作の変化による予期しないバグが発生する可能性があります。ログにオブジェクトのインスタンスを単に印刷することはできないので、デバッグもより困難になります。表現は正しいでしょう!この微妙なバグをキャッチするには、実際にはすべてのオブジェクトのクラスをチェックする必要があります。
特定の状況では、1つのメソッド内でしか辞書を変換しない場合は、サブクラス化の多くの欠点を避けることができますが、その時点で関数を書いてdict
それと? dict
を比較を行うライブラリ関数に渡したい場合を除いて、これはうまくいくはずです。
オプションは、floatのラッパーを作成し、そこに '__eq__'をオーバーライドすることです。 – NullUserException
しかし、すべてのフロートを 'fuzzyfloat(0.5)'などで作成する必要があります。 – alexis
右。私はこのアプローチが有効であることを知っています。私がそれを避けることができれば、特別なオブジェクト/クラスを使用する必要はありません。私は実際には比較がこの1つの事例では「あいまい」である必要があります。このため、私はコンテキストマネージャを使い、限られた時間だけ異なる「モード」に入ることを望んでいたのです。 –