避免子类化内置类型。当你发现你的对象由于某种不明原因而改变了类型时,你会后悔的。改用代表团。例如:
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
。
总之,子类化内置的结果比开始时想象的要多得多,它可能会引入一些不可预知的错误,这是由于您未预料到的类型或行为的改变。调试也变得更加困难,因为您不能简单地在日志中打印对象的实例,表示将是正确的!你真的必须检查周围所有对象的类来捕捉这些微妙的错误。
在您的具体情况中,如果您打算仅在单一方法内转换字典,那么您可以避免dict
的子类化的大多数缺点,但在那一点上,为什么不简单地编写函数并比较dict
s用它? 这应该工作得很好,除非你想将dict
s传递给进行比较的库函数。
一个选项是为float创建一个包装,并在那里覆盖'__eq__'。 – NullUserException
但是你需要用'fuzzyfloat(0.5)'等来创建所有的浮点数。 – alexis
对。我知道这种方法的工作原理,只是不想使用特殊的对象/类,如果我能避免它。在这个例子中,我只需要比较“模糊”。这就是为什么我希望使用上下文管理器,并在有限的时间内进入不同的“模式”。 –