[Python] 파이썬에서 얕은 목록 병합


Answers

당신은 거의 그것을 가지고 있습니다! 중첩 된 목록 내포를 수행 하는 방법for 문을 정규 내포 된 for 문에서와 동일한 순서로 배치하는 것입니다.

따라서, 이것은

for inner_list in outer_list:
    for item in inner_list:
        ...

~에 해당

[... for inner_list in outer_list for item in inner_list]

그래서 너는 원한다.

[image for menuitem in list_of_menuitems for image in menuitem]
Question

이 질문에는 이미 답변이 있습니다.

목록 이해력으로 반복 가능한 목록을 단순화하는 간단한 방법이 있습니까? 그렇지 않으면 성능과 가독성의 균형을 맞추기 위해 이와 같은 얕은 목록을 평평하게 만드는 가장 좋은 방법은 무엇입니까?

나는 다음과 같이 중첩 된 목록 이해력으로 그러한 목록을 평평하게하려고했다.

[image for image in menuitem for menuitem in list_of_menuitems]

그러나 name 'menuitem' is not defined 않기 때문에 NameError 다양한 문제가 발생합니다. 인터넷 검색 및 스택 오버플로 둘러보고 후, 나는 원하는 진술을 reduce 진술 :

reduce(list.__add__, map(lambda x: list(x), list_of_menuitems))

그러나 x가 Django QuerySet 객체이기 때문에 list(x) 호출이 필요하기 때문에이 방법은 상당히 읽을 수 없습니다.

결론 :

이 질문에 공헌 한 모든 분들께 감사드립니다. 다음은 내가 배운 것을 요약 한 것입니다. 또한 다른 사람들이 이러한 관찰을 추가하거나 수정하기를 원할 경우이를 위키로 만듭니다.

원래의 reduce 명령문은 중복되어 다음과 같이 작성하는 것이 좋습니다.

>>> reduce(list.__add__, (list(mi) for mi in list_of_menuitems))

중첩 된 목록 이해를위한 올바른 구문입니다 (Brilliant summary dF !) :

>>> [image for mi in list_of_menuitems for image in mi]

그러나 이러한 방법 중 어느 것도 itertools.chain 을 사용하는 것만 큼 효율적이지 않습니다.

>>> from itertools import chain
>>> list(chain(*list_of_menuitems))

그리고 @cdleary는 다음과 같이 chain.from_iterable을 사용하여 * 연산자 마법을 피하는 것이 더 좋은 스타일 일 것입니다.

>>> chain = itertools.chain.from_iterable([[1,2],[3],[5,89],[],[6]])
>>> print(list(chain))
>>> [1, 2, 3, 5, 89, 6]



다음은 collectons.Iterable 사용하여 여러 수준의 목록에 대해 작동하는 버전입니다.

import collections

def flatten(o):
    result = []
    for i in o:
        if isinstance(i, collections.Iterable):
            result.extend(flatten(i))
        else:
            result.append(i)
    return result



다음은 목록 내재법을 사용하는 올바른 해결책입니다 (문제는 역방향입니다).

>>> join = lambda it: (y for x in it for y in x)
>>> list(join([[1,2],[3,4,5],[]]))
[1, 2, 3, 4, 5]

귀하의 경우에는

[image for menuitem in list_of_menuitems for image in menuitem.image_set.all()]

또는 당신은 join 사용하여 말할 수 있습니다.

join(menuitem.image_set.all() for menuitem in list_of_menuitems)

두 경우 모두, 잡아 당김은 for 루프의 중첩입니다.




성과 결과. 수정 됨.

import itertools
def itertools_flatten( aList ):
    return list( itertools.chain(*aList) )

from operator import add
def reduce_flatten1( aList ):
    return reduce(add, map(lambda x: list(x), [mi for mi in aList]))

def reduce_flatten2( aList ):
    return reduce(list.__add__, map(list, aList))

def comprehension_flatten( aList ):
    return list(y for x in aList for y in x)

30 항목의 2 단계 목록을 1000 번 평평하게했습니다.

itertools_flatten     0.00554
comprehension_flatten 0.00815
reduce_flatten2       0.01103
reduce_flatten1       0.01404

Reduce은 언제나 나쁜 선택입니다.




목록의 각 항목이 문자열이고 그 문자열 안에있는 문자열이 ''이 아닌 ""을 사용하는 경우 일반 표현식 ( re 모듈)을 사용할 수 있습니다.

>>> flattener = re.compile("\'.*?\'")
>>> flattener
<_sre.SRE_Pattern object at 0x10d439ca8>
>>> stred = str(in_list)
>>> outed = flattener.findall(stred)

위의 코드는 in_list를 문자열로 변환하고, 정규 표현식을 사용하여 따옴표 안에있는 모든 부분 문자열 (즉, 목록의 각 항목)을 찾아서 목록으로 뱉어냅니다.




이 버전은 발전기입니다. 목록을 원한다면 조심하십시오.

def list_or_tuple(l):
    return isinstance(l,(list,tuple))
## predicate will select the container  to be flattened
## write your own as required
## this one flattens every list/tuple


def flatten(seq,predicate=list_or_tuple):        
    ## recursive generator 
    for i in seq:
        if predicate(seq):
            for j in flatten(i):
                yield j
        else:
            yield i

조건을 만족하는 항목을 병합하려는 경우 조건부를 추가 할 수 있습니다.

파이썬 요리 책에서 가져온 것




sum(list of lists, []) 평평하게합니다.

l = [['image00', 'image01'], ['image10'], []]
print sum(l,[]) # prints ['image00', 'image01', 'image10']



는 어때:

from operator import add
reduce(add, map(lambda x: list(x.image_set.all()), [mi for mi in list_of_menuitems]))

하지만 Guido는 가독성을 떨어 뜨리기 때문에 한 줄의 코드에서 너무 많이 수행하지 말 것을 권장합니다. 한 줄에서 여러 줄까지 원하는대로 수행하여 최소한의 성능 향상을 얻을 수 있습니다.




간단한 대안은 numpy의 concatenate 를 사용하는 것이지만 내용을 float로 변환합니다 :

import numpy as np
print np.concatenate([[1,2],[3],[5,89],[],[6]])
# array([  1.,   2.,   3.,   5.,  89.,   6.])
print list(np.concatenate([[1,2],[3],[5,89],[],[6]]))
# [  1.,   2.,   3.,   5.,  89.,   6.]



operator.add 와 혼란이있는 것 같습니다! 두리스트를 함께 추가 할 때, 그 정확한 용어는 concat 이지 추가는 아닙니다. operator.concat 을 사용하면됩니다.

당신이 기능적이라고 생각한다면, 이것만큼 쉽습니다. :

>>> list2d = ((1,2,3),(4,5,6), (7,), (8,9))
>>> reduce(operator.concat, list2d)
(1, 2, 3, 4, 5, 6, 7, 8, 9)

reduce가 시퀀스 타입을 존경한다는 것을 알기 때문에 튜플을 제공 할 때 튜플을 얻는다. 리스트로 시도해 보자. ::

>>> list2d = [[1,2,3],[4,5,6], [7], [8,9]]
>>> reduce(operator.concat, list2d)
[1, 2, 3, 4, 5, 6, 7, 8, 9]

아하, 목록 가져와.

어때요? ::

>>> list2d = [[1,2,3],[4,5,6], [7], [8,9]]
>>> %timeit list(itertools.chain.from_iterable(list2d))
1000000 loops, best of 3: 1.36 µs per loop

from_iterable은 꽤 빠릅니다! 그러나 그것은 concat로 줄이기 위해 비교가 아닙니다.

>>> list2d = ((1,2,3),(4,5,6), (7,), (8,9))
>>> %timeit reduce(operator.concat, list2d)
1000000 loops, best of 3: 492 ns per loop



def flatten(items):
    for i in items:
        if hasattr(i, '__iter__'):
            for m in flatten(i):
                yield m
        else:
            yield i

테스트:

print list(flatten2([1.0, 2, 'a', (4,), ((6,), (8,)), (((8,),(9,)), ((12,),(10)))]))