如何从Pop-11制作相当于 pdtolist 的Python?
假设我有一个名为 g 的生成器,它一次返回一个整数。我想构建一个列表 a ,当我要求超出列表当前末尾的值时,该列表会自动增长。例如:
print a # => [ 0, 1, 2, g]
print a[0] # => 0
print a[1] # => 1
print a[2] # => 2
# (obvious enough up to here)
print a[6] # => 6
print a # => [ 0, 1, 2, 3, 4, 5, 6, g]
# list has automatically expanded
a = a[4:] # discard some previous values
print a # => [ 4, 5, 6, g]
print a[0] # => 4
术语 - 预测可能的误解:列表是“动态阵列”,但这不是我的意思;我想要一个更抽象的“动态列表”。
为了更好地解释动机,假设您有999999999个要处理的项目。试图将所有这些同时放入内存(在正常列表中)将是一个挑战。生成器通过一次呈现一个问题来解决问题的这一部分;每个按需创建或从磁盘单独读取。但是假设在处理过程中你想要引用一些最近的值,而不仅仅是当前的值?您可以记住单独列表中的最后一个(例如)十个值。但动态列表更好,因为它会自动记住它们。
答案 0 :(得分:2)
这可能会让你开始:
class DynamicList(list):
def __init__(self, gen):
self._gen = gen
def __getitem__(self, index):
while index >= len(self):
self.append(next(self._gen))
return super(DynamicList, self).__getitem__(index)
您需要为切片添加一些特殊处理(目前,它们只返回正常列表,因此您将失去动态行为)。此外,如果您希望生成器本身成为列表项,那将增加一些复杂性。
答案 1 :(得分:2)
非常感谢所有提出想法的人!这是我从所有回复中收集到的内容。这保留了普通列表类的大部分功能,并在必要时添加其他行为以满足其他要求。
class DynamicList(list):
def __init__(self, gen):
self.gen = gen
def __getitem__(self, index):
while index >= len(self):
self.append(next(self.gen))
return super(DynamicList, self).__getitem__(index)
def __getslice__(self, start, stop):
# treat request for "last" item as "most recently fetched"
if stop == 2147483647: stop = len(self)
while stop > len(self):
self.append(next(self.gen))
return super(DynamicList, self).__getslice__(start, stop)
def __iter__(self):
return self
def next(self):
n = next(self.gen)
self.append(n)
return n
a = DynamicList(iter(xrange(10)))
以前生成的值可以作为项目或切片单独访问。如果请求的项目超出列表的当前末尾,则记录的历史记录将根据需要进行扩展。可以使用print a
一次性访问整个录制的历史记录,也可以使用b = a[:]
将其分配到普通列表。可以使用del a[0:4]
删除记录的历史记录片段。您可以使用for
遍历整个列表,随时删除,或者在适合的时候删除。如果到达生成值的末尾,则会引发StopIteration
。
有些尴尬依然存在。 a = a[0:4]
之类的作业成功截断了历史记录,但结果列表不再自动展开。而是使用del a[0:4]
来保留自动增长属性。此外,我不必完全认识到代表最新项目的魔法值2147483647
。
答案 2 :(得分:1)
刚刚回答了另一个类似的问题,并决定为您更新我的答案 这个怎么样?
class dynamic_list(list):
def __init__(self,num_gen):
self._num_gen = num_gen
def __getitem__(self,index):
if isinstance(index, int):
self.expandfor(index)
return super(dynamic_list,self).__getitem__(index)
elif isinstance(index, slice):
if index.stop<index.start:
return super(dynamic_list,self).__getitem__(index)
else:
self.expandfor(index.stop if abs(index.stop)>abs(index.start) else index.start)
return super(dynamic_list,self).__getitem__(index)
def __setitem__(self,index,value):
if isinstance(index, int):
self.expandfor(index)
return super(dynamic_list,self).__setitem__(index,value)
elif isinstance(index, slice):
if index.stop<index.start:
return super(dynamic_list,self).__setitem__(index,value)
else:
self.expandfor(index.stop if abs(index.stop)>abs(index.start) else index.start)
return super(dynamic_list,self).__setitem__(index,value)
def expandfor(self,index):
rng = []
if abs(index)>len(self)-1:
if index<0:
rng = xrange(abs(index)-len(self))
else:
rng = xrange(abs(index)-len(self)+1)
for i in rng:
self.append(self._num_gen.next())
答案 3 :(得分:0)
感谢这个主题;它帮助我解决了自己的问题。我的有点简单:我想要一个列表,如果索引超过当前长度,它会自动扩展->允许读写当前长度。如果读数超过当前长度,则返回0值。 也许这可以帮助某人:
class DynamicList(list):
def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs)
def __getitem__(self, idx):
self.expand(idx)
return super().__getitem__(idx)
def __setitem__(self, idx, val):
self.expand(idx)
return super().__setitem__(idx, val)
def expand(self, idx):
if isinstance(idx, int):
idx += 1
elif isinstance(idx, slice):
idx = max(idx.start, idx.stop)
if idx > len(self):
self.extend([0] * (idx - len(self)))