避免子類化內置類型。當你發現你的對象由於某種不明原因而改變了類型時,你會後悔的。改用代表團。例如:
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
對。我知道這種方法的工作原理,只是不想使用特殊的對象/類,如果我能避免它。在這個例子中,我只需要比較「模糊」。這就是爲什麼我希望使用上下文管理器,並在有限的時間內進入不同的「模式」。 –