将列表合并为可重用的单个可迭代

时间:2017-08-21 13:30:08

标签: python list

我有一个问题,我需要将一些列表(2个或更多)组合成一个可迭代的列表。问题是,组成可迭代的列表不断更新,我希望我的iterable能够在没有任何额外工作的情况下获得这些更改。

仅仅组合列表不起作用,因为向列表添加列表会生成新列表。

list_a = ['foo', 'bar']
list_b = ['abc']
list_c = list_a + list_b
list_a.append('ttt')
print(list_c)
# Result: ['foo', 'bar', 'abc']

我可以将列表添加到列表中以制作列表列表,并且这样做有效,但是解压缩会使我的生产代码逻辑太复杂而无法生存。

list_a = ['foo', 'bar']
list_b = ['abc']
list_c = [list_a, list_b]
list_a.append('ttt')
print(list_c)
# Result: [['foo', 'bar', 'ttt'], ['abc']]

我喜欢itertools.chain的想法,因为它让我成为了那里的一部分但我只能在我丢失对原始列表的引用之前迭代一次列表。

import itertools

list_a = ['foo', 'bar']
list_b = ['abc']
iter_c = itertools.chain(list_a, list_b)
list_a.append('ttt')

for item in iter_c:
    print(item)  # Works fine here

for item in iter_c:
    print(item)  # The iterable was exhausted - this doesn't work anymore

您可以将链转换为列表,但是一旦您执行了list_a的修改就不会继续。

import itertools

list_a = ['foo', 'bar']
list_b = ['abc']
iter_c = itertools.chain(list_a, list_b)
list_a.append('ttt')
list_c = list(iter_c)

for item in list_c:
    print(item)  # Works fine here

for item in list_c:
    print(item)  # Now works fine here, too

list_a.append('zzz')  # This won't get added to our chain
print(list_c)
# Result: ['foo', 'bar', 'ttt', 'abc']

我建立了一个hacky课程来做我想做的事,但我对它非常不满意。

import collections
import uuid


class IterGroup(object):
    def __init__(self):
        super(IterGroup, self).__init__()
        self._data = collections.OrderedDict()

    def append(self, item):
        # The key doesn't matter as long as it's unique. The key is ignored.
        self._data[str(uuid.uuid4())] = item

    def __iter__(self):
        for items in self._data.values():
            for item in items:
                yield item


list_a = ['foo', 'bar']
list_b = ['abc']
list_c = IterGroup()  # Not really a list but just go with it
list_c.append(list_a)
list_c.append(list_b)

list_a.append('ttt')

for item in list_c:
    print(item)

list_a.append('zzz')

for item in list_c:
    print(item)
# Prints ['foo', 'bar', 'ttt', 'zzz', 'abc']

所以我对所需解决方案的标准是

  1. 必须能够向其添加iterables
  2. 在添加一个可迭代后,它会自动反映在组解决方案中
  3. 组合的iterable可以按照我们想要的次数迭代
  4. 必须维持秩序
  5. 理想情况下,我也可以通过索引获取项目,但不是必需的
  6. 我宁愿解决方案不是一个自定义类(但如果它是必须的话,我不会挑剔)
  7. 必须使用Python 2
  8. 有没有人有这个问题的清洁解决方案?

2 个答案:

答案 0 :(得分:3)

我不喜欢'我认为有一种方法可以使用自定义类来解决此问题,但我不明白为什么该类应该扩展OrderedDict;只需实施__iter____getitem__方法即可。你可以尝试这样的事情。

class Multilistview:

    def __init__(self, *lists):
        self.lists = lists

    def __iter__(self):
        return itertools.chain.from_iterable(self.lists)

    def __getitem__(self, idx):
        if isinstance(idx, slice):
            return list(itertools.islice(self, idx.start, idx.stop, idx.step))
        else:
            for i, x in enumerate(self):
                if i == idx:
                    return x

或者更简单,但每次要求提供项目时,这将实现整个列表:

    def __getitem__(self, idx):
        return list(self)[idx]

(您还可以通过检查每个项目的长度来确定__getitem__更高效,以确定要使用的列表以及该列表中的"更正的"索引。)

实施例

list_a = ['foo', 'bar']
list_b = ['abc']
list_c = Multilistview(list_a, list_b)
for x in list_c:
    print(x)
# foo
# bar
# abc
list_a.append('blub')
list_b[:] = [1,2,3]
print(list(list_c))
# ['foo', 'bar', 'blub', 1, 2, 3]
print(list_c[4])
# 2
print(list_c[2:5])
# ['blub', 1, 2]

答案 1 :(得分:1)

具体 - 两个清单:

def f(l1, l2): return lambda: l1 + l2

L1 = [1, 2, 3]
L2 = [4, 5]
newl = f(L1, L2)
print(newl())

L2.append(6)
print(newl())

更一般 - 任意数量的列表:

def f(*L):
  def g():
    out = []
    for li in L:
      out += li
    return out
  return g

L1 = [1,2]
L2 = [3,4]
L3 = [5]
newl = f(L1,L2,L3)
print(newl())

L3.append(6)
print(newl())

更通用 - 任何可变迭代器,任何组合:

def f(combine, *iters): return lambda: combine(*iters)

def makelist(*L):
  out = []
  for li in L:
    out += li
  return out

def makegenerator(*L):
  for li in L:
    for i in li:
      yield i

def makedict(*D):
  out = dict()
  for di in D:
    for key in di:
      out[key] = di.get(key)
  return out

def ListsToList(*L): return f(makelist, *L)

def ListsToGenerator(*L): return f(makegenerator, *L)

def DictsToDict(*D): return f(makedict, *D)

L1 = [1,2]
L2 = [3,4]
L3 = [5]
newl = ListsToList(L1,L2,L3)
newg = ListsToGenerator(L1,L2,L3)

L3.append(7)
print(newl())
print([i for i in newg()])

D1 = {"x": 1, "y": 2}
D2 = {"a": 5, "b": 10}
newd = DictsToDict(D1,D2)

D2["c"] = 15
print(newd())