避免子类化内置类型。当你发现你的对象由于某种未知原因改变了类型时,你会后悔的。改用委托。例如:
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
关于为什么你不应该继承一个内置的。
这个解决方案看起来简单而正确,但通常并非如此。首先,即使您可以对内置类型进行子类化,但这并不意味着它们被编写为用作子类,因此您可能会发现要使某些东西起作用,您必须编写比您想象的更多的代码。
此外,您可能希望使用内置方法,但这些方法将返回内置类型的实例而不是您的类的实例,这意味着您必须重新实现该类型的每个方法。此外,您有时必须实现内置未实现的其他方法。
例如,子类化list
你可能会这样认为,因为list
只实现了__iadd__
,__add__
你可以安全地重新实现这两个方法,但你错了!您还必须实现__radd__
,否则表达式如下:
[1,2,3] + MyList([1,2,3])
将返回正常list
而不是MyList
.
总而言之,子类化一个内置函数比你一开始想的要多得多,并且由于类型或行为的变化,它可能会引入一些你没有预料到的不可预知的错误。调试也变得更加困难,因为您不能简单地在日志中打印对象的实例,表示是正确的!您确实必须检查周围所有对象的类以捕获这些细微的错误。
在您的特定情况下,如果您打算仅在单个方法内转换字典,那么您可能会避免 subclassing 的大多数缺点dict
,但是在这一点上,您为什么不简单地编写一个函数并将dict
s 与它进行比较呢?这应该很好用,除非您想将dict
s 传递给进行比较的库函数。