这是对@Amber和@user2357112答案的变体。
它是作为list的子类实现的,并直接处理random.choice()。它仍然像一个set一样工作。它执行了这两种类型的一些常见操作,但我确定它并不完全或完美地模拟它们。
class ListSet(list):
"""
派生自:https://dev59.com/h2Qo5IYBdhLWcg3whPki#15993515
初始化:
与列表相同(基本上与集合相同):
(包括集合、列表、元组、生成器表达式和range()表达式。)
集合行为:
for x in S():
if x in S: 这是常数时间。
S.add(x) " " " "
S.remove(x) " " " "
len(s) " " " "
print(S), str(S), repr(S)
copy(S), list(S), set(S) 复制或转换。
列表行为使用内部列表位置:
S[3], S[-1] # 返回一个项。
S[10:20] # 返回一个列表,而不是ListSet。
random.choice(S) 这是常数时间。
S += [4, 5, 6]
S.append(7),与S.add(7)相同。
len(s)在常数时间内。
x = S.pop() # 从内部列表中弹出最后一个元素。
y = S.pop(3) # 从内部列表中弹出S[3]。
"""
def __init__(self, *args, idx_of=None):
super(ListSet, self).__init__(*args)
if idx_of:
self.idx_of = idx_of.copy()
else:
self.idx_of = {item: i
for (i, item) in enumerate(self)}
def add(self, item):
if item not in self.idx_of:
super(ListSet, self).append(item)
self.idx_of[item] = len(self) - 1
def append(self, item):
"""
append和+=总是添加到内部列表,
但是除了末尾之外的remove和pop可以改变内部列表的顺序。
"""
self.add(item)
def copy(self):
""" 返回ListSet的浅拷贝。 """
return ListSet(self, idx_of=self.idx_of)
def list(self):
return super(ListSet, self).copy()
def __iadd__(self, items):
""" self += items """
for item in items:
self.add(item)
return self
def remove(self, element):
"""
从集合中删除一个元素;它必须是成员。
如果元素不是成员,则引发KeyError。
"""
try:
position = self.idx_of.pop(element)
except:
raise(KeyError(element))
last_item = super(ListSet, self).pop()
if position != len(self):
self[position] = last_item
self.idx_of[last_item] = position
def pop(self, i=-1):
""" 根据内部列表位置删除。 """
item = self[i]
self.remove(item)
return item
def __contains__(self, item):
return item in self.idx_of
def _str_body(self):
return ", ".join(repr(item) for item in self)
def __repr__(self):
return "ListSet([" + self._str_body() + "])"
def __str__(self):
if self:
return "{" + self._str_body() + "}"
else:
return "ListSet()"