Liste listesinden düz liste nasıl yapılır?


3371

Python'daki listelerden basit bir liste yapmak için bir kısayol olup olmadığını merak ediyorum.

Bunu bir fordöngü içinde yapabilirim , ama belki bazı serin "bir astar" var? İle denedim reduce(), ama bir hata alıyorum.

kod

l = [[1, 2, 3], [4, 5, 6], [7], [8, 9]]
reduce(lambda x, y: x.extend(y), l)

Hata mesajı

Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
  File "<stdin>", line 1, in <lambda>
AttributeError: 'NoneType' object has no attribute 'extend'

20
Burada derinlemesine bir tartışma var: rightfootin.blogspot.com/2006/09/more-on-python-flatten.html , keyfi olarak iç içe liste listelerini düzleştirmenin birkaç yöntemini tartışıyor. İlginç bir okuma!
RichieHindle

6
Diğer bazı yanıtlar daha iyidir, ancak başarısız olmanızın nedeni, 'genişletme' yönteminin her zaman Yok değerini döndürmesidir. Uzunluk 2 olan bir liste için çalışır, ancak Yok döndürür. Daha uzun bir liste için, Yok değerini döndüren ilk 2 bağımsız değişkeni tüketir. Daha sonra bu hataya neden olan None.extend (<üçüncü arg>) ile devam eder
mehtunguh

@ shawn-chin çözümü burada daha pitoniktir, ancak dizi türünü korumanız gerekiyorsa, bir liste listesi yerine bir demet tupleınız olduğunu varsayalım, o zaman reduce kullanmalısınız (operator.concat, tuple_of_tuples). Operator.concat'i tuples ile kullanmak chain.from_iterables ile listeden daha hızlı performans gösteriyor gibi görünüyor.
Meitham

Yanıtlar:


4791

Liste listesi verildiğinde l,

flat_list = [item for sublist in l for item in sublist]

bunun anlamı:

flat_list = []
for sublist in l:
    for item in sublist:
        flat_list.append(item)

şu ana kadar yayınlanan kısayollardan daha hızlı. ( ldüzleştirilecek listedir.)

İşte ilgili fonksiyon:

flatten = lambda l: [item for sublist in l for item in sublist]

Kanıt olarak, timeitmodülü standart kütüphanede kullanabilirsiniz:

$ python -mtimeit -s'l=[[1,2,3],[4,5,6], [7], [8,9]]*99' '[item for sublist in l for item in sublist]'
10000 loops, best of 3: 143 usec per loop
$ python -mtimeit -s'l=[[1,2,3],[4,5,6], [7], [8,9]]*99' 'sum(l, [])'
1000 loops, best of 3: 969 usec per loop
$ python -mtimeit -s'l=[[1,2,3],[4,5,6], [7], [8,9]]*99' 'reduce(lambda x,y: x+y,l)'
1000 loops, best of 3: 1.1 msec per loop

Açıklama: dayalı kısayollar +(içinde zımni kullanımı dahil sum) olan, gereklilik, O(L**2)- ara sonuç listesi yeni ara sonuç listesi nesnesi tahsis alır her adımda, uzun olmaya devam ediyor gibi ve tüm öğeleri ne zaman orada L Alt listeler vardır önceki ara sonuçta kopyalanmalı (ve sonuna birkaç yenisi eklenmelidir). Yani, basitlik ve fiili genel kayıp olmadan, her biri I öğenin L alt listesine sahip olduğunuzu varsayalım: ilk I öğeleri L-1 kez ileri ve geri kopyalanır, ikinci I öğeleri L-2 kez vb. toplam kopya sayısı, 1'den x'e hariç x için x toplamının çarpımının çarpımıdır, yani I * (L**2)/2.

Liste kavraması yalnızca bir kez bir liste oluşturur ve her öğeyi (orijinal ikamet yerinden sonuç listesine) tam olarak bir kez kopyalar.


486
Ben kullanarak, aynı verilerle bir test çalıştı itertools.chain.from_iterable: $ python -mtimeit -s'from itertools import chain; l=[[1,2,3],[4,5,6], [7], [8,9]]*99' 'list(chain.from_iterable(l))'. Burada gösterilen alternatiflerin en hızlısı olan iç içe liste kavrayışının iki katından daha hızlı çalışır.
intuited

274
Sözdizimini, döngüler için yuvalanmış gibi düşünebildiğini anlayana kadar anlamakta zorlandım. l'deki alt liste için: alt listedeki öğe için: ürün
Rob Crowell

23
@BorisChervenkov: list()Yineleyiciyi bir liste haline getirmek için çağrıyı tamamladığımı fark ettim .
intuited

163
[ormandaki ağaç için yaprak ağaçtaki yaprak için] anlamak ve uygulamak daha kolay olabilir.
John Mee

80
@Joel, aslında bugünlerde list(itertools.chain.from_iterable(l))en iyisi - diğer yorumlarda ve Shawn'un cevabında fark edildiği gibi.
Alex Martelli

1568

Şunları kullanabilirsiniz itertools.chain():

import itertools
list2d = [[1,2,3], [4,5,6], [7], [8,9]]
merged = list(itertools.chain(*list2d))

Veya itertools.chain.from_iterable()listenin *operatörle birlikte paketlenmesini gerektirmeyen kullanabilirsiniz :

import itertools
list2d = [[1,2,3], [4,5,6], [7], [8,9]]
merged = list(itertools.chain.from_iterable(list2d))

13
Bu *, chainliste kavrayışından daha az anlaşılır kılan zor bir şeydir . Zincirin yalnızca parametre olarak iletilen yinelemeleri bir araya getirdiğini ve * üst düzey listenin parametrelere genişletilmesine neden olduğunu chainbilmeniz gerekir, bu nedenle tüm bu yinelemeleri birleştirir, ancak daha fazla inmez. Bence bu, kavrayışı bu durumda zincir kullanımından daha okunaklı kılıyor.
Tim Dierks

52
@TimDierks: "Bu Python sözdizimini anlamanızı gerektirir" emin değilim Python belirli bir tekniği kullanmaya karşı bir argüman. Elbette, karmaşık kullanım kafa karıştırıcı olabilir, ancak "splat" operatörü genellikle birçok durumda yararlıdır ve bu özellikle belirsiz bir şekilde kullanılmaz; yeni başlayan kullanıcılar için mutlaka açık olmayan tüm dil özelliklerini reddetmek, bir elinizi arkanıza bağladığınız anlamına gelir. Siz de listeyi okuduğunuzda da kavrayışları ortaya çıkarabilir; diğer arka planlardan kullanıcılar fortekrar tekrar appenddaha belirgin bir döngü bulurlar .
ShadowRanger

Bu yanıt ve buradaki diğer yanıtlar, en üst düzey de bir değer içeriyorsa yanlış sonuç verir. örneğin, list = [["abc","bcd"],["cde","def"],"efg"]çıktısı["abc", "bcd", "cde", "def", "e", "f", "g"].
gouravkr

906

Yazardan not : Bu verimsiz. Ama eğlenceli, çünkü monoidler harika. Üretim Python kodu için uygun değildir.

>>> sum(l, [])
[1, 2, 3, 4, 5, 6, 7, 8, 9]

Bu sadece birinci argümanda geçirilen yinelenebilir unsurları toplar, ikinci argümanı toplamın başlangıç ​​değeri olarak ele alır (eğer verilmezse, 0kullanılır ve bu durumda size bir hata verecektir).

İç içe listeler topladığınız için, aslında [1,3]+[2,4]bunun sonucunda sum([[1,3],[2,4]],[])eşittir [1,3,2,4].

Yalnızca liste listelerinde çalıştığını unutmayın. Liste listelerinin listeleri için başka bir çözüme ihtiyacınız vardır.


100
bu oldukça temiz ve zekice ama ben kullanmazdım çünkü okumak kafa karıştırıcı.
andrewrk

87
Bu bir Shlemiel ressamın algoritması joelonsoftware.com/articles/fog0000000319.html - gereksiz yere verimsiz ve gereksiz yere çirkin.
Mike Graham

44
Listelerdeki ekleme işlemi, Monoidbir +işlemi genel anlamda düşünmek için en uygun soyutlamalardan biri olan a'yı oluşturur (yalnızca sayılarla sınırlı değildir). Yani bu cevap, listelerin bir monoid olarak (doğru) muamele görmesi için benden bir +1 hak ediyor. Performans olsa ilgili ...
ulidtko

7
@andrewrk Eh, bazı insanlar bunun bunu yapmanın en temiz yolu olduğunu düşünüyor: youtube.com/watch?v=IOiZatlZtGU Neden bu kadar havalı olduğunu anlamayanların sadece herkesin bu şekilde yapmasını birkaç yıl beklemesi gerekiyor: ) keşfedilen ve icat edilmeyen programlama dillerini (ve soyutlamaları) kullanalım, Monoid keşfedildi.
jhegedus

11
bu, toplamın kuadratik yönü nedeniyle çok verimsiz bir yoldur.
Jean-François Fabre

460

Perfplot (benim için bir evcil hayvan projesi, aslında bir sarıcı timeit) ile en çok önerilen çözümleri test ettim ve buldum

functools.reduce(operator.iconcat, a, [])

birçok küçük liste ve birkaç uzun liste birleştirildiğinde en hızlı çözüm olmaktır. ( operator.iaddeşit derecede hızlı.)

resim açıklamasını buraya girin

resim açıklamasını buraya girin


Grafiği yeniden oluşturmak için kod:

import functools
import itertools
import numpy
import operator
import perfplot


def forfor(a):
    return [item for sublist in a for item in sublist]


def sum_brackets(a):
    return sum(a, [])


def functools_reduce(a):
    return functools.reduce(operator.concat, a)


def functools_reduce_iconcat(a):
    return functools.reduce(operator.iconcat, a, [])


def itertools_chain(a):
    return list(itertools.chain.from_iterable(a))


def numpy_flat(a):
    return list(numpy.array(a).flat)


def numpy_concatenate(a):
    return list(numpy.concatenate(a))


perfplot.show(
    setup=lambda n: [list(range(10))] * n,
    # setup=lambda n: [list(range(n))] * 10,
    kernels=[
        forfor,
        sum_brackets,
        functools_reduce,
        functools_reduce_iconcat,
        itertools_chain,
        numpy_flat,
        numpy_concatenate,
    ],
    n_range=[2 ** k for k in range(16)],
    xlabel="num lists (of length 10)",
    # xlabel="len lists (10 lists total)"
)

25
Büyük iç içe listeler için, 'list (numpy.array (a) .flat)' yukarıdaki tüm işlevler arasında en hızlısıdır.
Sara

Normal ifade kullanılarak denendi: 'list (map (int, re.findall (r "[\ w] +", str (a))))'. Hız numpy_concatenate'den biraz daha yavaş
Justas

Üç boyutlu bir perflot yapmanın bir yolu var mı? dizinin ortalama büyüklüğüne göre dizi sayısı?
Leo

Çözümünü seviyorum. Kısa, basit ve verimli :-)
ShadyMBA

181
from functools import reduce #python 3

>>> l = [[1,2,3],[4,5,6], [7], [8,9]]
>>> reduce(lambda x,y: x+y,l)
[1, 2, 3, 4, 5, 6, 7, 8, 9]

extend()Daki örnekte yöntem, modifiye xyerine (faydalı bir değeri dönen reduce()bekler).

reduceSürümü yapmanın daha hızlı bir yolu

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

19
reduce(operator.add, l)reducesürümü yapmanın doğru yolu olurdu . Ankastre lambdalardan daha hızlıdır.
agf

3
@agf burada nasıl: * timeit.timeit('reduce(operator.add, l)', 'import operator; l=[[1, 2, 3], [4, 5, 6, 7, 8], [1, 2, 3, 4, 5, 6, 7]]', number=10000) 0.017956018447875977 * timeit.timeit('reduce(lambda x, y: x+y, l)', 'import operator; l=[[1, 2, 3], [4, 5, 6, 7, 8], [1, 2, 3, 4, 5, 6, 7]]', number=10000) 0.025218963623046875
lukmdo

8
Bu bir Shlemiel ressamın algoritması joelonsoftware.com/articles/fog0000000319.html
Mike Graham

2
bu sadece için kullanılabilir integers. Peki ya liste içeriyorsa string?
Freddy

3
@Freddy: operator.addİşlev hem tamsayılar hem de dize listeleri için eşit derecede iyi çalışır.
Greg Hewgill

119

Django kullanıyorsanız tekerleği yeniden icat etmeyin :

>>> from django.contrib.admin.utils import flatten
>>> l = [[1,2,3], [4,5], [6]]
>>> flatten(l)
>>> [1, 2, 3, 4, 5, 6]

... Pandalar :

>>> from pandas.core.common import flatten
>>> list(flatten(l))

... Itertools :

>>> import itertools
>>> flatten = itertools.chain.from_iterable
>>> list(flatten(l))

... Matplotlib

>>> from matplotlib.cbook import flatten
>>> list(flatten(l))

... Unipath :

>>> from unipath.path import flatten
>>> list(flatten(l))

... Kurulum araçları :

>>> from setuptools.namespaces import flatten
>>> list(flatten(l))

4
flatten = itertools.chain.from_iterabledoğru cevap olmalı
geckos

3
mükemmel cevap! pandalar durumunda l = [[[1, 2, 3], [4, 5]], 5] için de çalışır
Markus Dutschke

1
Panda çözümünü seviyorum. Eğer böyle bir şey varsa: list_of_menuitems = [1, 2, [3, [4, 5, [6]]]]bu üzerinde neden olur: [1, 2, 3, 4, 5, 6]. Kaçırdığım şey düzleştirme seviyesidir.
imjoseangel

115

İşte sayılar , karakter dizileri , iç içe listeler ve karışık kaplar için geçerli olan genel bir yaklaşım .

kod

#from typing import Iterable 
from collections import Iterable                            # < py38


def flatten(items):
    """Yield items from any nested iterable; see Reference."""
    for x in items:
        if isinstance(x, Iterable) and not isinstance(x, (str, bytes)):
            for sub_x in flatten(x):
                yield sub_x
        else:
            yield x

Notlar :

  • Python 3, yield from flatten(x)yerini alabilirfor sub_x in flatten(x): yield sub_x
  • Python 3.8 içinde, soyut temel sınıfları vardır hareket gelen collection.abciçin typingmodül.

gösteri

lst = [[1, 2, 3], [4, 5, 6], [7], [8, 9]]
list(flatten(lst))                                         # nested lists
# [1, 2, 3, 4, 5, 6, 7, 8, 9]

mixed = [[1, [2]], (3, 4, {5, 6}, 7), 8, "9"]              # numbers, strs, nested & mixed
list(flatten(mixed))
# [1, 2, 3, 4, 5, 6, 7, 8, '9']

Referans

  • Bu çözelti Beazley, D. ve B. Jones'daki bir tariften modifiye edilmiştir . Reçete 4.14, Python Yemek Kitabı 3. Baskı, O'Reilly Media Inc. Sebastopol, CA: 2013.
  • Muhtemelen orijinal gösteri olan daha eski bir SO yayını bulundu .

5
Hemen hemen aynı yazdım, çünkü çözümünüzü görmedim ... işte "birden çok listeyi yinelemeli olarak düzleştirin" ... (+1)
Martin Thoma

3
@MartinThoma Çok takdir edildi. FYI, iç içe yinelenenleri düzleştirmek sizin için yaygın bir uygulamasa, bunu iyi işleyen bazı üçüncü taraf paketleri vardır. Bu, tekerleği yeniden icat etmekten kurtarabilir. more_itertoolsBu yazıda tartışılan diğerleri arasında bahsetmiştim . Şerefe.
Mart'ta pylang

Belki traversede bir ağacın bu yolu için iyi bir isim olabilirken , iç içe listelere bağlı kalarak bu cevap için daha az evrensel kalırdım .
Wolf

if hasattr(x, '__iter__')Karşıdan içe aktarma / kontrol etme yerine kontrol edebilirsiniz Iterableve bu da dizeleri hariç tutar.
Ryan Allen

yukarıdaki kod, iç içe listelerden birinde dizelerin bir listesi varsa işe yaramıyor gibi görünüyor. [1, 2, [3, 4], [4], [], 9, 9.5, 'ssssss', ['str', 'sss', 'ss'], [3, 4, 5]] çıktı: - [1, 2, 3, 4, 4, 9, 9.5, 'ssssss', 3, 4, 5]
sunnyX

51

Eğer iç içe ne kadar derin olduğunu bilmiyorum bir veri yapısını düzleştirmek istiyorsanız kullanabilirsiniz 1iteration_utilities.deepflatten

>>> from iteration_utilities import deepflatten

>>> l = [[1, 2, 3], [4, 5, 6], [7], [8, 9]]
>>> list(deepflatten(l, depth=1))
[1, 2, 3, 4, 5, 6, 7, 8, 9]

>>> l = [[1, 2, 3], [4, [5, 6]], 7, [8, 9]]
>>> list(deepflatten(l))
[1, 2, 3, 4, 5, 6, 7, 8, 9]

Bu bir jeneratör, bu yüzden sonucu listaçık bir şekilde veya açıkça yinelemeniz gerekiyor.


Sadece bir seviye düzleştirmek ve her bir öğenin kendisi yinelenebilirse, kendisi iteration_utilities.flattenetrafında sadece ince bir sargı olan da kullanabilirsiniz itertools.chain.from_iterable:

>>> from iteration_utilities import flatten
>>> l = [[1, 2, 3], [4, 5, 6], [7], [8, 9]]
>>> list(flatten(l))
[1, 2, 3, 4, 5, 6, 7, 8, 9]

Sadece bazı zamanlamalar eklemek için (bu cevapta sunulan işlevi içermeyen Nico Schlömer cevabına dayanarak):

resim açıklamasını buraya girin

Yayılmış çok çeşitli değerlere uyum sağlamak için bir günlük kaydı grafiğidir. Niteliksel akıl yürütme için: Düşük daha iyidir.

Sonuçlar iterable sadece birkaç iç Iterables içeriyorsa o zaman olduğunu göstermektedir sumuzun Iterables yalnızca bununla birlikte, en hızlı olacaktır itertools.chain.from_iterable, iteration_utilities.deepflattenmakul bir performansa sahip veya iç içe kavrama itertools.chain.from_iterable(zaten Nico Schlomer tarafından fark) en hızlı olmak.

from itertools import chain
from functools import reduce
from collections import Iterable  # or from collections.abc import Iterable
import operator
from iteration_utilities import deepflatten

def nested_list_comprehension(lsts):
    return [item for sublist in lsts for item in sublist]

def itertools_chain_from_iterable(lsts):
    return list(chain.from_iterable(lsts))

def pythons_sum(lsts):
    return sum(lsts, [])

def reduce_add(lsts):
    return reduce(lambda x, y: x + y, lsts)

def pylangs_flatten(lsts):
    return list(flatten(lsts))

def flatten(items):
    """Yield items from any nested iterable; see REF."""
    for x in items:
        if isinstance(x, Iterable) and not isinstance(x, (str, bytes)):
            yield from flatten(x)
        else:
            yield x

def reduce_concat(lsts):
    return reduce(operator.concat, lsts)

def iteration_utilities_deepflatten(lsts):
    return list(deepflatten(lsts, depth=1))


from simple_benchmark import benchmark

b = benchmark(
    [nested_list_comprehension, itertools_chain_from_iterable, pythons_sum, reduce_add,
     pylangs_flatten, reduce_concat, iteration_utilities_deepflatten],
    arguments={2**i: [[0]*5]*(2**i) for i in range(1, 13)},
    argument_name='number of inner lists'
)

b.plot()

1 Feragatname: Bu kütüphanenin yazarıyım


sumartık başlangıçta gelişigüzel diziler üzerinde çalışmaz 0, bu functools.reduce(operator.add, sequences)da değiştirmeyi yapar ( reduceyerleşiklerden kaldırıldıklarına memnun değil miyiz ?). Türler bilindiğinde kullanımı daha hızlı olabilir type.__add__.
Yann Vernier

@YannVernier Bilgi için teşekkürler. Bu kriterleri Python 3.6'da çalıştırdığımı düşündüm ve işe yaradı sum. Hangi Python sürümlerinde çalışmayı bıraktığını biliyor musunuz?
MSeifert

Biraz yanılmışım. 0yalnızca varsayılan başlangıç ​​değeridir, bu nedenle boş bir listeyle başlamak için başlangıç ​​bağımsız değişkenini kullanırsa çalışır ... ancak yine de özel durumlar dizeleri ve birleştirme kullanmamı söyler. Bunun foldlyerine uyguluyor foldl1. Aynı konu 2.7'de ortaya çıkıyor.
Yann Vernier

39

İfademi geri alıyorum. toplam kazanan değil. Liste küçük olduğunda daha hızlı olmasına rağmen. Ancak performans, daha büyük listelerle önemli ölçüde düşer.

>>> timeit.Timer(
        '[item for sublist in l for item in sublist]',
        'l=[[1, 2, 3], [4, 5, 6, 7, 8], [1, 2, 3, 4, 5, 6, 7]] * 10000'
    ).timeit(100)
2.0440959930419922

Toplam sürüm hala bir dakikadan fazla çalışıyor ve henüz işlenmedi!

Araç listeleri için:

>>> timeit.Timer(
        '[item for sublist in l for item in sublist]',
        'l=[[1, 2, 3], [4, 5, 6, 7, 8], [1, 2, 3, 4, 5, 6, 7]] * 10'
    ).timeit()
20.126545906066895
>>> timeit.Timer(
        'reduce(lambda x,y: x+y,l)',
        'l=[[1, 2, 3], [4, 5, 6, 7, 8], [1, 2, 3, 4, 5, 6, 7]] * 10'
    ).timeit()
22.242258071899414
>>> timeit.Timer(
        'sum(l, [])',
        'l=[[1, 2, 3], [4, 5, 6, 7, 8], [1, 2, 3, 4, 5, 6, 7]] * 10'
    ).timeit()
16.449732065200806

Küçük listeler ve timeit kullanma: sayı = 1000000

>>> timeit.Timer(
        '[item for sublist in l for item in sublist]',
        'l=[[1, 2, 3], [4, 5, 6, 7, 8], [1, 2, 3, 4, 5, 6, 7]]'
    ).timeit()
2.4598159790039062
>>> timeit.Timer(
        'reduce(lambda x,y: x+y,l)',
        'l=[[1, 2, 3], [4, 5, 6, 7, 8], [1, 2, 3, 4, 5, 6, 7]]'
    ).timeit()
1.5289170742034912
>>> timeit.Timer(
        'sum(l, [])',
        'l=[[1, 2, 3], [4, 5, 6, 7, 8], [1, 2, 3, 4, 5, 6, 7]]'
    ).timeit()
1.0598428249359131

23
örneğin, 3 alt listeye sahip gerçek bir küçük liste için, belki de - ancak toplamın performansı O (N ** 2) ile giderken, liste kavraması O (N) ile giderken, giriş listesini biraz büyütmek işleri tersine çevirir - - gerçekten de LC, N büyüdükçe sınırdaki toplamdan "son derece hızlı" olacaktır. Toplamı tasarlamaktan ve Python çalışma zamanında ilk uygulamasını yapmaktan sorumluydum ve hala sayıları toplamanın (gerçekten iyi olanı) etkili bir şekilde sınırlandırmanın ve insanlara sunduğu "çekici sıkıntıyı" engellemenin bir yolunu bulmamı diliyorum listelerini "toplamak" isteyen ;-).
Alex Martelli

38

Bir karışıklık var gibi görünüyor operator.add! İki listeyi birlikte eklediğinizde, bunun için doğru terim concateklemektir. operator.concatkullanmanız gereken şey bu.

Fonksiyonel düşünüyorsanız, bu kadar kolay ::

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

Dizi türüne saygıyı azalttığını görüyorsunuz, bu yüzden bir demet beslediğinizde, bir demet geri alırsınız. Bir liste ile deneyelim ::

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

Aha, bir listeyi geri getir.

Performans hakkında ::

>>> 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_iterableoldukça hızlı! Ama azaltmak için bir karşılaştırma yok 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

1
Hmm adil olmak için ikinci örnek de listelenmelidir (veya ilk demet?)
Mr_and_Mrs_D

2
Bu tür küçük girdileri kullanmak, adil bir karşılaştırma değildir. 1000 uzunluk 1000 dizisi için 0.037 saniye list(chain.from_iterable(...))ve 2.5 saniye için reduce(concat, ...). Sorun, reduce(concat, ...)ikinci dereceden çalışma süresine sahipken chain, doğrusaldır.
kaya3

33

Neden expand kullanıyorsunuz?

reduce(lambda x, y: x+y, l)

Bu iyi olmalı.


7
python3 içinfrom functools import reduce
andorov

Üzgünüm bu gerçekten yavaş cevapların geri kalanını görün
Mr_and_Mrs_D

Bu, Python 2 ve 3'te çalışan, anlaşılması en kolay olan kısa çözümdür. Python milletlerinin işlenmesi için büyük miktarda veri olduğu ve böylece hıza çok önem verdiği zaman veri işlemede olduğunun farkındayım. bir kabuk betiği yazıyor ve birkaç alt listede yalnızca birkaç düzine öğeye sahip, o zaman bu mükemmel.
Asfand Qazi

27

more_itertoolsPaketi kurmayı düşünün .

> pip install more_itertools

Bir uygulama ile birlikte gönderilir flatten( kaynak , itertools tariflerinden ):

import more_itertools


lst = [[1, 2, 3], [4, 5, 6], [7], [8, 9]]
list(more_itertools.flatten(lst))
# [1, 2, 3, 4, 5, 6, 7, 8, 9]

2.4 sürümünden itibaren, daha karmaşık, iç içe yinelenebilirleri more_itertools.collapse( kaynak , abarnet'in katkıda bulunduğu) düzleştirebilirsiniz.

lst = [[1, 2, 3], [4, 5, 6], [7], [8, 9]]
list(more_itertools.collapse(lst)) 
# [1, 2, 3, 4, 5, 6, 7, 8, 9]

lst = [[1, 2, 3], [[4, 5, 6]], [[[7]]], 8, 9]              # complex nesting
list(more_itertools.collapse(lst))
# [1, 2, 3, 4, 5, 6, 7, 8, 9]

Aslında. Kabul edilen cevap bu olmalı
brunetton

Projenize bir paket eklemeyi göze alabiliyorsanız - bu cevap en iyisidir
viddik13

22

İşlevinizin çalışmamasının nedeni, genişletmenin bir diziyi yerinde genişletmesi ve döndürmemesi. Yine de böyle bir şey kullanarak lambda'dan x döndürebilirsiniz:

reduce(lambda x,y: x.extend(y) or x, l)

Not: expand, listelerde + işaretinden daha etkilidir.


7
extendDaha iyi olarak kullanılır newlist = [], extend = newlist.extend, for sublist in l: extend(l)o (oldukça büyük) önler olarak havai ait lambda, özelliğiyle ilgili arama xve or.
agf

3 add içinfrom functools import reduce
Markus Dutschke

17
def flatten(l, a):
    for i in l:
        if isinstance(i, list):
            flatten(i, a)
        else:
            a.append(i)
    return a

print(flatten([[[1, [1,1, [3, [4,5,]]]], 2, 3], [4, 5],6], []))

# [1, 1, 1, 3, 4, 5, 2, 3, 4, 5, 6]

def flatten(l, a=None): if a is None: a = [][...]
Poik

16

Özyinelemeli sürüm

x = [1,2,[3,4],[5,[6,[7]]],8,9,[10]]

def flatten_list(k):
    result = list()
    for i in k:
        if isinstance(i,list):

            #The isinstance() function checks if the object (first argument) is an 
            #instance or subclass of classinfo class (second argument)

            result.extend(flatten_list(i)) #Recursive call
        else:
            result.append(i)
    return result

flatten_list(x)
#result = [1,2,3,4,5,6,7,8,9,10]

1
güzel, hiçbir ithalat gerekli ve ne yaptığı açıktır ... bir liste düzleştirmek, dönem :)
Goran B.

1
sadece parlak!
Sachin Sharma

15

matplotlib.cbook.flatten() yuvalanmış listelerde, örnekten daha derin yuvalanmış olsalar bile çalışacaktır.

import matplotlib
l = [[1, 2, 3], [4, 5, 6], [7], [8, 9]]
print(list(matplotlib.cbook.flatten(l)))
l2 = [[1, 2, 3], [4, 5, 6], [7], [8, [9, 10, [11, 12, [13]]]]]
print list(matplotlib.cbook.flatten(l2))

Sonuç:

[1, 2, 3, 4, 5, 6, 7, 8, 9]
[1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13]

Alt çizgiden 18 kat daha hızlıdır.

Average time over 1000 trials of matplotlib.cbook.flatten: 2.55e-05 sec
Average time over 1000 trials of underscore._.flatten: 4.63e-04 sec
(time for underscore._)/(time for matplotlib.cbook) = 18.1233394636

14

Değişken uzunluklardaki metin tabanlı listelerle uğraşırken kabul edilen cevap benim için çalışmadı. İşte benim için işe yarayan alternatif bir yaklaşım.

l = ['aaa', 'bb', 'cccccc', ['xx', 'yyyyyyy']]

Did cevabı Kabul değil iş:

flat_list = [item for sublist in l for item in sublist]
print(flat_list)
['a', 'a', 'a', 'b', 'b', 'c', 'c', 'c', 'c', 'c', 'c', 'xx', 'yyyyyyy']

Yeni çözüm önerdi mi iş için beni:

flat_list = []
_ = [flat_list.extend(item) if isinstance(item, list) else flat_list.append(item) for item in l if item]
print(flat_list)
['aaa', 'bb', 'cccccc', 'xx', 'yyyyyyy']

13

Anil'nin yukarıdaki işlevinin kötü bir özelliği, kullanıcının boş bir liste olarak her zaman ikinci bağımsız değişkeni manuel olarak belirtmesini gerektirmesidir []. Bunun yerine bu bir varsayılan değer olmalıdır. Python nesnelerinin çalışma şekli nedeniyle, bunlar bağımsız değişkenlerde değil işlevin içinde ayarlanmalıdır.

İşte çalışan bir işlev:

def list_flatten(l, a=None):
    #check a
    if a is None:
        #initialize with empty list
        a = []

    for i in l:
        if isinstance(i, list):
            list_flatten(i, a)
        else:
            a.append(i)
    return a

Test yapmak:

In [2]: lst = [1, 2, [3], [[4]],[5,[6]]]

In [3]: lst
Out[3]: [1, 2, [3], [[4]], [5, [6]]]

In [11]: list_flatten(lst)
Out[11]: [1, 2, 3, 4, 5, 6]

13

Aşağıdaki benim için en basit görünüyor:

>>> import numpy as np
>>> l = [[1, 2, 3], [4, 5, 6], [7], [8, 9]]
>>> print (np.concatenate(l))
[1 2 3 4 5 6 7 8 9]

Farklı boyutlara sahip listelerde çalışmaz. -1
nurub

10

Bir de numpy en kullanabilirsiniz düz :

import numpy as np
list(np.array(l).flat)

Edit 11/02/2016: Yalnızca alt listelerin boyutları aynı olduğunda çalışır.


bu en uygun çözüm olabilir mi?
RetroCode

6

Numpy kullanabilirsiniz:
flat_list = list(np.concatenate(list_of_list))


Bu, sayısal, dizgiler ve karışık listeler için de geçerlidir
Nitin

2
Eşit olmayan bir şekilde iç içe geçmiş veriler için başarısız[1, 2, [3], [[4]], [5, [6]]]
EL_DON

5

Daha temiz bir görünüm için az miktarda hızdan vazgeçmek istiyorsanız, numpy.concatenate().tolist()veya seçeneğini kullanabilirsiniz numpy.concatenate().ravel().tolist():

import numpy

l = [[1, 2, 3], [4, 5, 6], [7], [8, 9]] * 99

%timeit numpy.concatenate(l).ravel().tolist()
1000 loops, best of 3: 313 µs per loop

%timeit numpy.concatenate(l).tolist()
1000 loops, best of 3: 312 µs per loop

%timeit [item for sublist in l for item in sublist]
1000 loops, best of 3: 31.5 µs per loop

Daha fazla bilgiyi burada numpy.concatenate ve numpy.ravel belgelerinde bulabilirsiniz.


1
Gibi dengesiz iç içe listeler için çalışmaz[1, 2, [3], [[4]], [5, [6]]]
EL_DON

5

Bulduğum en hızlı çözüm (yine de büyük liste için):

import numpy as np
#turn list into an array and flatten()
np.array(l).flatten()

Bitti! Elbette listeyi (l) yürüterek tekrar listeye dönüştürebilirsiniz.


1
Bu yanlıştır, düzleştir nd dizisinin boyutlarını bire düşürür, ancak içindeki listeleri bir olarak birleştirmez.
Ando Jurai

5

underscore.pyPaket fanı için basit kod

from underscore import _
_.flatten([[1, 2, 3], [4, 5, 6], [7], [8, 9]])
# [1, 2, 3, 4, 5, 6, 7, 8, 9]

Tüm düzleştirme sorunlarını çözer (hiçbir liste öğesi veya karmaşık iç içe geçme)

from underscore import _
# 1 is none list item
# [2, [3]] is complex nesting
_.flatten([1, [2, [3]], [4, 5, 6], [7], [8, 9]])
# [1, 2, 3, 4, 5, 6, 7, 8, 9]

underscore.pyPip ile kurabilirsiniz

pip install underscore.py

Benzer şekilde, pydash kullanabilirsiniz . Bu sürümün liste kavrayışından veya diğer yanıtlardan çok daha okunabilir olduğunu düşünüyorum.
gliemezis

2
Bu çok yavaş.
Nico Schlömer

2
Neden _ adlı bir modül var? Kötü bir isim gibi görünüyor. Bkz. Stackoverflow.com/a/5893946/6605826
EL_DON

2
@EL_DON: underscore.py benioku sayfasından "Underscore.py mükemmel javascript kitaplığı underscore.js bir python bağlantı noktasıdır". Bence bu ismin sebebi bu. Ve evet, python için iyi bir isim değil
Vu Anh

5
def flatten(alist):
    if alist == []:
        return []
    elif type(alist) is not list:
        return [alist]
    else:
        return flatten(alist[0]) + flatten(alist[1:])

[[1, 2, 3], [4, 5, 6], [7], [8, 9]]
Sorudaki

@EL_DON python 2.7.5 üzerinde test edildi. iyi çalışıyor
englealuze

5

Not : Aşağıda, Python 3.3+ için kullanıldığı için geçerlidir yield_from. sixkararlı olmasına rağmen, üçüncü taraf bir pakettir. Alternatif olarak kullanabilirsiniz sys.version.


Bu durumda, obj = [[1, 2,], [3, 4], [5, 6]]liste kavraması ve dahil olmak üzere buradaki tüm çözümler iyidir itertools.chain.from_iterable.

Ancak, bu biraz daha karmaşık bir durumu düşünün:

>>> obj = [[1, 2, 3], [4, 5], 6, 'abc', [7], [8, [9, 10]]]

Burada birkaç sorun var:

  • Bir element, 6sadece bir skalerdir; yinelenebilir değil, bu yüzden yukarıdaki yollar burada başarısız olacak.
  • Bir eleman, 'abc', olup (teknik olarak iterable strs) vardır. Bununla birlikte, çizgiler arasında biraz okumak, ona böyle davranmak istemiyorsunuz - tek bir eleman olarak ele almak istiyorsunuz.
  • Son elemanın [8, [9, 10]]kendisi, iç içe geçmiş bir yinelenebilirdir. Temel liste anlama ve chain.from_iterablesadece "1 seviye aşağı" ayıklayın.

Bunu aşağıdaki gibi düzeltebilirsiniz:

>>> from collections import Iterable
>>> from six import string_types

>>> def flatten(obj):
...     for i in obj:
...         if isinstance(i, Iterable) and not isinstance(i, string_types):
...             yield from flatten(i)
...         else:
...             yield i


>>> list(flatten(obj))
[1, 2, 3, 4, 5, 6, 'abc', 7, 8, 9, 10]

Burada, alt elemanın (1) Iterablebir ABC ile yinelenebilir olup olmadığını kontrol edersiniz itertools, fakat aynı zamanda (2) elemanın "dize benzeri" olmadığından emin olmak istersiniz.


1
Hala Python 2 uyumluluğu ile ilgileniyorsanız, yield frombir fordöngüye for x in flatten(i): yield x
geçin

5
flat_list = []
for i in list_of_list:
    flat_list+=i

Bu Kurallar, listeyi sonuna kadar genişlettiği için de iyi çalışır. Çok benzer olmasına rağmen, sadece döngü için bir tane var. Bu yüzden döngüler için 2 eklemekten daha az karmaşıklığa sahiptir.


5
from nltk import flatten

l = [[1, 2, 3], [4, 5, 6], [7], [8, 9]]
flatten(l)

Bu çözümün diğerlerine göre avantajı şuna benzer bir listeniz varsa:

l = [1, [2, 3], [4, 5, 6], [7], [8, 9]]

diğer çözümlerin çoğu hata verirken bu çözüm bunları ele alır.


Soru bir "liste listesi" olarak belirtilir, ancak örnek listenizde liste dışı bir öğe bulunur. Diğer birçok çözüm orijinal soruya sadık kalıyor. Çözümünüz daha geniş bir sorunu çözüyor, ancak önce yüklenmesi gereken temel olmayan bir Python paketi (nltk) de gerekiyor.
simonobo

4

Bu en etkili yol olmayabilir ama bir astar (aslında iki astar) koymayı düşündüm. Her iki sürüm de rasgele hiyerarşi iç içe listelerinde çalışır ve dil özelliklerini (Python3.5) ve özyinelemeyi kullanır.

def make_list_flat (l):
    flist = []
    flist.extend ([l]) if (type (l) is not list) else [flist.extend (make_list_flat (e)) for e in l]
    return flist

a = [[1, 2], [[[[3, 4, 5], 6]]], 7, [8, [9, [10, 11], 12, [13, 14, [15, [[16, 17], 18]]]]]]
flist = make_list_flat(a)
print (flist)

Çıktı

[1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18]

Bu öncelikle derinlemesine çalışır. Özyineleme, liste dışı bir öğe bulana kadar aşağı iner, ardından yerel değişkeni genişletir flistve sonra üst öğeye geri döndürür. Ne zaman flistiade edilirse , flistliste kavrayışında ebeveynin genişletilir . Bu nedenle, kökte düz bir liste döndürülür.

Yukarıdakiler birkaç yerel liste oluşturur ve bu listeyi ebeveyn listesini genişletmek için kullanır. Sanırım bunun için yol flistaşağıdaki gibi bir gloabl yaratmak olabilir .

a = [[1, 2], [[[[3, 4, 5], 6]]], 7, [8, [9, [10, 11], 12, [13, 14, [15, [[16, 17], 18]]]]]]
flist = []
def make_list_flat (l):
    flist.extend ([l]) if (type (l) is not list) else [make_list_flat (e) for e in l]

make_list_flat(a)
print (flist)

Çıktı yine

[1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18]

Her ne kadar bu konuda verimlilik konusunda emin değilim.


Neden (l) yerine ([l]) öğesini genişletmeliyim?
Maciek

3

Hetero ve homojen tamsayı listeleri için çalışan bir başka alışılmadık yaklaşım:

from typing import List


def flatten(l: list) -> List[int]:
    """Flatten an arbitrary deep nested list of lists of integers.

    Examples:
        >>> flatten([1, 2, [1, [10]]])
        [1, 2, 1, 10]

    Args:
        l: Union[l, Union[int, List[int]]

    Returns:
        Flatted list of integer
    """
    return [int(i.strip('[ ]')) for i in str(l).split(',')]

Bu, 0003000'in daha önce yayınladığı şeyin sadece daha karmaşık ve biraz daha yavaş bir yoludur. Dün önerisini yeniden keşfettim, bu yüzden bu yaklaşım bugünlerde oldukça popüler görünüyor;)
Darkonaut

Pek değil: wierd_list = [[1, 2, 3], [4, 5, 6], [7], [8, 9], 10]>>nice_list=[1, 2, 3, 4, 5, 6, 7, 8, 9, 1, 0]
tharndt

bir liner olarak kodum olurdu: flat_list = [int(e.replace('[','').replace(']','')) for e in str(deep_list).split(',')]
tharndt

1
Gerçekten doğru +1'siniz, 0003000'in teklifi birden fazla basamaklı sayılarla çalışmaz, daha önce açık olsa da bunu test etmedim. Kodunuzu basitleştirebilir ve yazabilirsiniz [int(e.strip('[ ]')) for e in str(deep_list).split(',')]. Ancak gerçek kullanım durumları için Deleet'in önerisine sadık kalmayı öneririm. Keskin tip dönüşümleri içermez, daha karışık ve daha çok yönlüdür, çünkü doğal olarak karışık tiplere sahip listeleri de işler.
Darkonaut

2
Ne yazık ki hayır. Ama bu kodu son zamanlarda burada gördüm: Python Uygulama Kitabı 6.1.2
tharndt
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.