我有以下物件,我想保留在一個容器中,該容器在插入時排序并且不包含重復項,所以我使用的是SortedSet
from sortedcontainers import SortedSet, SortedList
class R():
def __hash__(self):
return hash(self.person_id)
def __eq__(self, other):
return self.__class__ == other.__class__ and self.person_id == other.person_id
def __nq__(self, other):
return not (self == other)
def __lt__(self, other):
return other.value < self.value
def __init__(self, person_id, value):
self.person_id = person_id
self.value = value
def __repr__(self):
return "person: %s (%s)" % (self.person_id, self.value)
x = SortedSet()
x.add(R(13, 2))
x.add(R(17, 4))
x.add(R(11, 21))
x.add(R(7, -41))
print(x)
當我運行此代碼時,我按預期得到以下輸出:
排序集([人:11(21),人:17(4),人:13(2),人:7(-41)])
但是,如果我添加了一個額外的重復元素,即 17:
x.add(R(13, 2))
x.add(R(17, 4))
x.add(R(11, 21))
x.add(R(7, -41))
x.add(R(17, -67))
print(x)
我希望將名為 id 17 的 R 物件person: 17 (4)移到后面,其值person: 17 (-67)如下:
排序集([人:11(21),人:13(2),人:7(-41),人:17(-67)])
但是沒有任何改變:
排序集([人:11(21),人:17(4),人:13(2),人:7(-41)])
如何使用SortedSet在插入時排序且沒有重復的容器或任何其他容器來實作所描述的所需輸出?
uj5u.com熱心網友回復:
DeepSpace 的回答涵蓋了使這項作業(如果效率低下),但我將在這里提出框架挑戰:這是一個糟糕的設計。
集合(邏輯結構)旨在存盤獨特的專案。如果某個added 到一個集合中的某個東西等于其中已經存在的某個東西,則沒有理由替換舊項,因為舊項和新項是等價的。如果您的類不使用相等性意味著可替代性的相等定義(兩個相等的實體可以以所有相關方式互換使用),那么這些實體不適合在set. 即使沒有SortedSet參與,使用 plain set,這也行不通,因為set.add當您插入“相等”專案時不會替換專案;畢竟它們都是等價的,那么為什么要做額外的作業呢?
當您需要一個可以映射到值的鍵的概念時,其中給定鍵的值可以在不知道原始值的情況下稍后更改,您需要一個映射(dict-like),而不是一個集合(set-like)。
Kelly Bundy 建議你想要的東西可能已經存在于sortedcollections包 ( ValueSortedDict) 中,所以如果它有效,我會選擇它。由于sortedcontainers不包含任何允許替換值和對值進行排序的內容,因此您必須做很多作業才能添加該行為,與從頭開始自己實作它的數量級大致相同。
關于為什么這不起作用的附加說明:
除了您的用例根本不適合集合(邏輯概念,而不僅僅是set它本身)之外,SortedSet它本身也非常不適合您的類,因為隱式依賴于兩個不變數(Python 嚴格要求其中一個,盡管另一個是通常遵守):
- Python要求:
__eq__應該與__hash__:如果兩個專案相等,則它們必須具有相同的哈希,并且盡可能地,兩個不相等的專案不應具有相同的哈希(理想情況下,哈希應該基于相同的欄位__eq__比較,但基于這些欄位的子集是合法的) - SortedSet 要求(并且通常由處理排序物件的其他事物假定):
__eq__應該與__lt__(以及所有其他豐富的比較運算子)一致: Ifa == b, thena < b和b < a都應該是 false ;同樣,如果a < b或b < a為真,則a != b。Python中的大多數排序內容都只堅持__lt__比較以允許不一致的定義,但如果你將相同的物件放在atuple中進行比較,突然字典排序規則意味著tuple'自己的__lt__實作依賴于你的類__lt__和__eq__你的類,所以在實踐中你想要反正他們是一致的。
你的班級違反了#2;排序規則與相等的定義完全無關。SortedSet在這里混淆,基于__hash__ __eq__和排序來確定唯一性__lt__,但在某些情況下(例如,洗掉元素時)它依賴于__lt__與 一致__eq__。具體來說,在從內部洗掉之后set(使用__hash__ __eq___),然后從內部SortedList洗掉,使用 平分查找要洗掉的元素__lt__,并使用相等檢查確認它找到了正確的元素__eq__。因為__eq__and__lt__是不一致的(它們只有在你試圖洗掉R具有相同person_id和value),這永遠不會找到它試圖洗掉的值,并且會引發例外。
uj5u.com熱心網友回復:
您可以子類化SortedSet,覆寫它的add和remove方法。我們需要覆寫remove,因為原始實作使用self._list.removewhich 將失敗,因為兩個R物件不會被識別為相等。
class MySortedSet(SortedSet):
def add(self, value):
if value in self:
self.remove(value)
super().add(value)
def remove(self, value):
self._set.remove(value)
for index, e in enumerate(self._list[:]):
if hash(e) == hash(value):
self._list.pop(index)
break
x = MySortedSet()
x.add(R(13, 2))
x.add(R(17, 4))
x.add(R(11, 21))
x.add(R(7, -41))
x.add(R(17, -67))
print(x)
輸出
MySortedSet([person: 11 (21), person: 13 (2), person: 7 (-41), person: 17 (-67)])
轉載請註明出處,本文鏈接:https://www.uj5u.com/houduan/485220.html
上一篇:僅在Integer變數中存盤整數值,同時在輸入中具有雙精度值
下一篇:以插值順序將專案推送到陣列
