【问题标题】:Dynamic list that automatically expands自动展开的动态列表
【发布时间】:2012-06-29 16:08:58
【问题描述】:

如何从 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 项要处理。试图一次将所有这些都放入内存(在正常列表中)将是一个挑战。生成器通过一次呈现一个来解决这部分问题;每个都按需创建或从磁盘单独读取。但是假设在处理过程中您想引用一些最近的值,而不仅仅是当前值?您可以记住单独列表中的最后(例如)十个值。但是动态列表更好,因为它会自动记住它们。

【问题讨论】:

  • 覆盖列表的__getitem__ 方法以捕获IndexError
  • 所以你有列表L 并执行L[999999999] - 列表应该变成那个长度?
  • 是的,原则上。警告程序员!
  • 例如使用什么填充值?
  • 也许我应该更好地解释动机。假设您确实有 999999999 项要处理。试图一次将所有这些都放入内存(在正常列表中)将是一个挑战。生成器通过一次呈现一个来解决这部分问题;每个都按需创建或从磁盘或其他任何内容单独读取。但是假设在处理过程中您想引用一些最近的值,而不仅仅是当前值?您可以记住单独列表中的最后(例如)十个值。但是动态列表更好,因为它会自动记住。

标签: python pop-11


【解决方案1】:

这可能会让你开始:

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)

您需要为切片添加一些特殊处理(目前,它们只返回一个普通列表,因此您会失去动态行为)。另外,如果您希望生成器本身是一个列表项,那会增加一些复杂性。

【讨论】:

  • 这不应该是一个列表子类; __init__ 不兼容,它不应该支持 __len____setitem__...
  • @agf:__init__ 问题是有道理的,但为什么不支持__len____setitem__
  • 它的长度是未知的,因为生成器的长度是未知的,它应该只反映来自生成器的项目,所以你应该不能分配给它。
  • @agf: generator's 的长度是未知的,是的。但是动态列表!=生成器。到目前为止,获取长度可能并不总是非常有用,但你能举一个例子说明它会是有害的吗?
  • 在我看来 __len__ 应该返回扩展项目的当前长度,不包括那些已经丢弃的,也不包括那些即将到来的。
【解决方案2】:

刚刚回答了另一个类似的问题,并决定为您更新我的答案 这个怎么样?

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】:

    非常感谢所有贡献想法的人!以下是我从所有回复中收集到的信息。这保留了普通列表类的大部分功能,并在必要时添加了额外的行为以满足额外的要求。

    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 表示最近的项目并不完全满意。

    【讨论】:

      【解决方案4】:

      感谢这个帖子;它帮助我解决了自己的问题。我的有点简单:我想要一个列表,如果索引超过其当前长度,它会自动扩展——>允许读取和写入超过当前长度。如果读取超过当前长度,则返回 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)))
      

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 2011-03-18
        • 2012-05-27
        • 1970-01-01
        • 1970-01-01
        • 2018-11-28
        • 2020-02-22
        • 1970-01-01
        相关资源
        最近更新 更多