Düzensiz bir liste listesini düzleştirin


440

Evet, bu konunun daha önce ele alındığını biliyorum ( burada , burada , burada , burada ), ama bildiğim kadarıyla, biri hariç tüm çözümler böyle bir listede başarısız oluyor:

L = [[[1, 2, 3], [4, 5]], 6]

İstenen çıktının olduğu yer

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

Ya da belki daha da iyisi, bir yineleyici. Rasgele yuvalama için işe yaradığını gördüğüm tek çözüm bu soruda bulundu :

def flatten(x):
    result = []
    for el in x:
        if hasattr(el, "__iter__") and not isinstance(el, basestring):
            result.extend(flatten(el))
        else:
            result.append(el)
    return result

flatten(L)

Bu en iyi model mi? Bir şeyi gözden kaçırdım mı? Herhangi bir problem?


16
Bu soru üzerinde bu kadar çok cevap ve çok fazla eylem olması, bunun bir yerde yerleşik bir işlev olması gerektiğini gösteriyor, değil mi? Derleyici.ast özellikle Python 3.0'dan kaldırıldı
Mittenchops

3
Python'un gerçekten ihtiyaç duyduğu şeyin başka bir yerleşikten ziyade kesintisiz özyineleme olduğunu söyleyebilirim.
kil

2
@Mittenchops: Kesinlikle katılmıyorum, açıkça kötü API'lar / aşırı karmaşık veri yapıları ile çalışan insanların (sadece bir not: listhomojen olması amaçlanmıştır) bunun bir Python hatası olduğu anlamına gelmez ve bu tür bir görev için bir
yerleşime

1
Projenize bir paket eklemeyi göze alabiliyorsanız - more_itertools.collapse çözümünün en iyisini yapacağını varsayalım . Bu yanıttan: stackoverflow.com/a/40938883/3844376
viddik13

Yanıtlar:


382

Jeneratör işlevlerini kullanmak, örneğinizin okunmasını biraz daha kolaylaştırabilir ve muhtemelen performansı artırabilir.

Python 2

def flatten(l):
    for el in l:
        if isinstance(el, collections.Iterable) and not isinstance(el, basestring):
            for sub in flatten(el):
                yield sub
        else:
            yield el

Ben kullanılan iterable ABC2.6'da eklenen .

Python 3

Python 3'te, basestringartık yok, ancak bir strvebytes orada aynı etkiyi elde etmek için .

yield fromOperatör bir anda jeneratör birinden bir öğe döndürür. Bu bir subgenerator için yetki verme sözdizimi 3.3 ilave edildi

def flatten(l):
    for el in l:
        if isinstance(el, collections.Iterable) and not isinstance(el, (str, bytes)):
            yield from flatten(el)
        else:
            yield el

6
Bu sayfadaki tüm önerilerden, bunu l = ([[chr(i),chr(i-32)] for i in xrange(ord('a'), ord('z')+1)] + range(0,9))yaptığımda bu listeyi bir çırpıda düzleştiren tek kişi bu list(flatten(l)). Diğerleri çalışmaya başlayıp sonsuza dek sürecekti!
nemesisfixx

7
Bu, sözlükleri de düzleştirir. Belki collections.Sequencebunun yerine kullanmak istersiniz collections.Iteratable?
josch

1
Bu, başlangıçta listede olmayan şeylerle çalışmaz, örn for i in flatten(42): print (i). Bu, isinstance-test ve else-cümleciklerini for el-loop'un dışına taşıyarak düzeltilebilir. (O zaman ona bir şey fırlatabilirsin, ve bunun içinden düzleştirilmiş bir liste çıkarır)
RolKau

6
Python 3.7 için collections.Iterable, kullanımdan kaldırılmıştır. collections.abc.IterableBunun yerine kullanın .
dawg

5
Gerçekten, özyineleme asla gerekli değildir. Bu özel durumda özyineleme kullanmak, derin iç içe listelerde (derinlik> 1000) çökeceğinden en iyi çözüm değildir. Ancak, güvenli bir şeye sahip olmayı amaçlamıyorsanız, evet, özyinelemeli işlev, okunması / yazılması çok daha kolay olduğu için daha iyidir.
cglacet

50

Çözümüm:

import collections


def flatten(x):
    if isinstance(x, collections.Iterable):
        return [a for i in x for a in flatten(i)]
    else:
        return [x]

Biraz daha özlü, ama hemen hemen aynı.


5
Bunu sadece try: iter(x)tekrarlanabilir olup olmadığını test etmek için bir şey ithal etmeden yapabilirsiniz ... Ama bir stdlib modülü almak zorunda kaçınmaya değer bir dezavantaj olduğunu düşünmüyorum.
abarnert

8
Bu çözüm yalnızca tüm öğeler int
tipteyse işe yarar

1
Daha özlü def flatten(x): return [a for i in x for a in flatten(i)] if isinstance(x, collections.Iterable) else [x]olabilir , ama okunabilirlik burada öznel olabilir.
Sıfır

4
bu dizelerde çalışmaz, çünkü dizeler de yinelenebilir. if isinstance(x, collections.Iterable) and not isinstance(x, basestring)
Koşulu

36

Özyineleme ve ördek yazmayı kullanan jeneratör (Python 3 için güncellendi):

def flatten(L):
    for item in L:
        try:
            yield from flatten(item)
        except TypeError:
            yield item

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

1
Teşekkürler, bu Python 3 için iyi çalışıyor. 2.x için önceki gerekli: for i in flatten(item): yield i
dansalmo

listesi (düzleştir ([['X'], 'Y'])) 2.X varyantında başarısız oluyor
sten

@ user1019129 yukarıdaki yorumuma bak
dansalmo

Evet döngü ile başarısız .. Bence bir dize de bir "dizi" -of-chars
sten

35

@Andrew tarafından bir yorumda istendiği gibi @ unutbu'nun özyinelemesiz çözümünün jeneratör sürümü:

def genflat(l, ltypes=collections.Sequence):
    l = list(l)
    i = 0
    while i < len(l):
        while isinstance(l[i], ltypes):
            if not l[i]:
                l.pop(i)
                i -= 1
                break
            else:
                l[i:i + 1] = l[i]
        yield l[i]
        i += 1

Bu jeneratörün biraz basitleştirilmiş versiyonu:

def genflat(l, ltypes=collections.Sequence):
    l = list(l)
    while l:
        while l and isinstance(l[0], ltypes):
            l[0:1] = l[0]
        if l: yield l.pop(0)

yuvalanmış listelerin oluşturduğu ağacın ön siparişi çapraz geçişidir. sadece yapraklar iade edilir. Bu uygulamanın daha iyi veya daha kötü olarak orijinal veri yapısını tüketeceğini unutmayın. Hem orijinal ağacı koruyan hem de liste girişlerini kopyalamak zorunda olmayan bir tane yazmak eğlenceli olabilir.
Andrew Wagner

6
Sanırım dizeleri test etmeniz gerekiyor - örneğin "çözüm" (isinstance (l [0], basestring) değil) ekleyin. Aksi takdirde, l [0: 1] = l [0]
c-urchin

Bu bir jeneratör yapmak için iyi bir örnektir, ancak c-urchin'den bahsedildiği gibi, dizi dizeler içerdiğinde algoritmanın kendisi başarısız olur.
Daniel 'Dang' Griffith

28

İşte hem tuples hem de listeleri işleyen ve konumsal argümanların herhangi bir karışımını atmanıza izin veren özyinelemeli düzleştirmenin işlevsel sürümü. Arg tarafından arg dizisinin tamamını sırayla üreten bir jeneratör döndürür:

flatten = lambda *n: (e for a in n
    for e in (flatten(*a) if isinstance(a, (tuple, list)) else (a,)))

Kullanımı:

l1 = ['a', ['b', ('c', 'd')]]
l2 = [0, 1, (2, 3), [[4, 5, (6, 7, (8,), [9]), 10]], (11,)]
print list(flatten(l1, -2, -1, l2))
['a', 'b', 'c', 'd', -2, -1, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11]

1
Ne açıklamak için bazı yorum ekledi eğer harika bir çözüm, her ne kadar yararlı olacağını e, a, nbakın
Kristof Pal

2
@WolfgangKuehne: deneyin argsiçin n, intermediate(ya da daha kısa midya da tercih edebilirsiniz element) için ave resultiçin eböylece,:flatten = lambda *args: (result for mid in args for result in (flatten(*mid) if isinstance(mid, (tuple, list)) else (mid,)))
bir sonraki duyuruya kadar Durduruldu.

Bu önemli ölçüde daha hızlı compiler.ast.flatten. Büyük, kompakt kod, herhangi bir (sanırım) nesne türü için çalışır.
bcdan

Vay bu en çok oylanan ve kabul edilen cevap olmalı ... bir cazibe gibi çalışıyor!
U10-Forvet

27

Bu sürüm, flattenpython'un özyineleme sınırını önler (ve böylece keyfi olarak derin, iç içe yinelenebilirlerle çalışır). Dizeleri ve keyfi yinelenebilirleri (sonsuz olanları bile) işleyebilen bir jeneratördür.

import itertools as IT
import collections

def flatten(iterable, ltypes=collections.Iterable):
    remainder = iter(iterable)
    while True:
        first = next(remainder)
        if isinstance(first, ltypes) and not isinstance(first, (str, bytes)):
            remainder = IT.chain(first, remainder)
        else:
            yield first

İşte kullanımını gösteren bazı örnekler:

print(list(IT.islice(flatten(IT.repeat(1)),10)))
# [1, 1, 1, 1, 1, 1, 1, 1, 1, 1]

print(list(IT.islice(flatten(IT.chain(IT.repeat(2,3),
                                       {10,20,30},
                                       'foo bar'.split(),
                                       IT.repeat(1),)),10)))
# [2, 2, 2, 10, 20, 30, 'foo', 'bar', 1, 1]

print(list(flatten([[1,2,[3,4]]])))
# [1, 2, 3, 4]

seq = ([[chr(i),chr(i-32)] for i in range(ord('a'), ord('z')+1)] + list(range(0,9)))
print(list(flatten(seq)))
# ['a', 'A', 'b', 'B', 'c', 'C', 'd', 'D', 'e', 'E', 'f', 'F', 'g', 'G', 'h', 'H',
# 'i', 'I', 'j', 'J', 'k', 'K', 'l', 'L', 'm', 'M', 'n', 'N', 'o', 'O', 'p', 'P',
# 'q', 'Q', 'r', 'R', 's', 'S', 't', 'T', 'u', 'U', 'v', 'V', 'w', 'W', 'x', 'X',
# 'y', 'Y', 'z', 'Z', 0, 1, 2, 3, 4, 5, 6, 7, 8]

Her ne kadar flattensonsuz jeneratörler işleyebilir, sonsuz iç içe işleyemez:

def infinitely_nested():
    while True:
        yield IT.chain(infinitely_nested(), IT.repeat(1))

print(list(IT.islice(flatten(infinitely_nested()), 10)))
# hangs

1
ABC Yinelenebilir mi yoksa ABC Dizisi mi kullanıldığına dair fikir birliği var mı?
wim

sets, dicts, deques, listiterators, generators, İle filehandles ve özel sınıflar __iter__tanımlanmış tüm örnekleri olan collections.Iterable, ancak collections.Sequence. Bir düzleştirmenin sonucu dictbiraz iffy, ama aksi takdirde bence collections.Iterabledaha iyi bir varsayılan collections.Sequence. Kesinlikle daha liberal.
unutbu

@wim: Kullanmayla ilgili bir sorun collections.Iterable, bunun sonsuz jeneratörleri içermesidir. Cevabımı bu davayı değiştirdim.
unutbu

1
Bu, 3. ve 4. örnekler için işe yaramıyor gibi görünüyor. Atar StopIteration. Ayrıca, görünüşe while True: first = next(remainder) göre değiştirilebilir for first in remainder:.
Georgy

@Georgy bu düzleştirilmiş gövdeyi bir a try-except StopIteration block.
baduker

12

İşte daha da ilginç olan bir cevap daha ...

import re

def Flatten(TheList):
    a = str(TheList)
    b,crap = re.subn(r'[\[,\]]', ' ', a)
    c = b.split()
    d = [int(x) for x in c]

    return(d)

Temel olarak, iç içe listeyi bir dizeye dönüştürür, iç içe sözdizimini çıkarmak için bir normal ifade kullanır ve ardından sonucu tekrar (düzleştirilmiş) bir listeye dönüştürür.


Bunu int değerlerinden başka bir şeye genelleştirmeye çalışırsanız, örneğin eğlenceli olur [['C=64', 'APPLE ]['], ['Amiga', 'Mac', 'ST']]:) Öte yandan, kendisini içeren bir liste verildiğinde, diğer cevaplardan biraz daha iyi olur ve bellek bitene kadar döngü yerine istisna / yığını tüketene kadar yinelenen…
abarnert

Orijinal bilgi istemi, tamsayıların bir listesini düzleştirmekti. Sadece liste kavrayışını d = [x for x in c] olarak değiştirirseniz, örneğiniz için iyi çalışır.
kil

Birincisi, [x for x in c]sadece bir kopyasını almanın yavaş ve ayrıntılı bir yolu c, neden bunu yapasın ki? İkinci olarak, kod açıkça dönüştürmek için gidiyor 'APPLE ]['içine 'APPLE 'o alıntı işlenmemesi nedeniyle, bu sadece herhangi bir parantez liste braketlerdir varsayar.
abarnert

Ha! Yorumunuzun bilgisayarımda biçimlendirilme şekli, eski bilgisayarlarda göründüğü gibi Apple II olması gerektiğini bile fark etmedim. Her durumda, her iki soruya da cevabım, bu egzersizin benim için sadece bir listeyi düzleştirmek için yaratıcı bir çözüm bulmak için bir deney olmasıdır. Oradaki her listeyi düzleştirmek için genelleştireceğimden emin değilim.
kil

Sadece arr_str = str(arr)ve sonra [int(s) for s in re.findall(r'\d+', arr_str)]gerçekten ihtiyacınız var . Bkz. Github.com/jorgeorpinel/flatten_nested_lists/blob/master/…
Jorge Orpinel

10
def flatten(xs):
    res = []
    def loop(ys):
        for i in ys:
            if isinstance(i, list):
                loop(i)
            else:
                res.append(i)
    loop(xs)
    return res

8

Sen kullanabilirsiniz deepflatten3. parti paketinden iteration_utilities:

>>> from iteration_utilities import deepflatten
>>> L = [[[1, 2, 3], [4, 5]], 6]
>>> list(deepflatten(L))
[1, 2, 3, 4, 5, 6]

>>> list(deepflatten(L, types=list))  # only flatten "inner" lists
[1, 2, 3, 4, 5, 6]

Bu bir yineleyicidir, bu yüzden yinelemeniz gerekir (örneğin, onu bir sarma ile listveya bir döngüde kullanarak). Dahili olarak, özyinelemeli bir yaklaşım yerine yinelemeli bir yaklaşım kullanır ve C uzantısı olarak yazılır, böylece saf python yaklaşımlarından daha hızlı olabilir:

>>> %timeit list(deepflatten(L))
12.6 µs ± 298 ns per loop (mean ± std. dev. of 7 runs, 100000 loops each)
>>> %timeit list(deepflatten(L, types=list))
8.7 µs ± 139 ns per loop (mean ± std. dev. of 7 runs, 100000 loops each)

>>> %timeit list(flatten(L))   # Cristian - Python 3.x approach from https://stackoverflow.com/a/2158532/5393381
86.4 µs ± 4.42 µs per loop (mean ± std. dev. of 7 runs, 10000 loops each)

>>> %timeit list(flatten(L))   # Josh Lee - https://stackoverflow.com/a/2158522/5393381
107 µs ± 2.99 µs per loop (mean ± std. dev. of 7 runs, 10000 loops each)

>>> %timeit list(genflat(L, list))  # Alex Martelli - https://stackoverflow.com/a/2159079/5393381
23.1 µs ± 710 ns per loop (mean ± std. dev. of 7 runs, 10000 loops each)

Ben iteration_utilitieskütüphanenin yazarıyım.


7

Python'da düzensiz listeyi düzleştirebilecek bir işlev yaratmaya çalışmak eğlenceliydi, ama elbette Python'un (programlama eğlenceli hale getirmek için) amacı budur. Aşağıdaki jeneratör bazı uyarılarla oldukça iyi çalışır:

def flatten(iterable):
    try:
        for item in iterable:
            yield from flatten(item)
    except TypeError:
        yield iterable

Bu yalnız bıraktı istediğiniz olabileceğini veri türlerini (gibi dümdüz olacak bytearray, bytesve strnesneler). Ayrıca, kod yinelemeyenlerden bir yineleyici istemenin a TypeError.

>>> L = [[[1, 2, 3], [4, 5]], 6]
>>> def flatten(iterable):
    try:
        for item in iterable:
            yield from flatten(item)
    except TypeError:
        yield iterable


>>> list(flatten(L))
[1, 2, 3, 4, 5, 6]
>>>

Düzenle:

Önceki uygulamaya katılmıyorum. Sorun şu ki, tekrarlanamayan bir şeyi düzleştirememeniz gerekir. Kafa karıştırıcı ve argümanın yanlış izlenimini veriyor.

>>> list(flatten(123))
[123]
>>>

Aşağıdaki jeneratör neredeyse birinciyle aynıdır, ancak yinelenemeyen bir nesneyi düzleştirmeye çalışma sorunu yoktur. Uygun olmayan bir argüman verildiğinde beklendiği gibi başarısız olur.

def flatten(iterable):
    for item in iterable:
        try:
            yield from flatten(item)
        except TypeError:
            yield item

Jeneratörü test etmek, verilen listeyle iyi çalışır. Ancak, TypeErroryinelenemeyen bir nesne verildiğinde yeni kod bir yükseltecektir . Örnek, yeni davranışın aşağıda gösterilmiştir.

>>> L = [[[1, 2, 3], [4, 5]], 6]
>>> list(flatten(L))
[1, 2, 3, 4, 5, 6]
>>> list(flatten(123))
Traceback (most recent call last):
  File "<pyshell#32>", line 1, in <module>
    list(flatten(123))
  File "<pyshell#27>", line 2, in flatten
    for item in iterable:
TypeError: 'int' object is not iterable
>>>

5

Zarif ve çok pitonik bir cevap seçilmiş olmasına rağmen çözümümü sadece inceleme için sunacağım:

def flat(l):
    ret = []
    for i in l:
        if isinstance(i, list) or isinstance(i, tuple):
            ret.extend(flat(i))
        else:
            ret.append(i)
    return ret

Lütfen bu kodun ne kadar iyi veya kötü olduğunu söyleyin?


1
Kullanın isinstance(i, (tuple, list)). Boş değişkenleri başlatmak benim için alternatif kod yapılarına, tipik olarak kavrayışlara, üreteçlere, özyinelemeye vb.
Bakmak için bir bayraktır

3
return type(l)(ret)size de iletildiği şekliyle aynı kap türünü geri getirecektir. :)
dash-tom-bang

@ dash-tom-bang Bunun anlamını biraz ayrıntılı olarak açıklayabilir misiniz?
Xolve

1
Bir listeden geçerseniz, muhtemelen bir listeyi geri istersiniz. Bir demet geçirirseniz, muhtemelen bir demet tekrar istersiniz. Eğer ikisinin bir karışımını geçerseniz, dıştaki şey ne olursa olsun alırsınız.
dash-tom-bang

4

Basit cevapları tercih ederim. Jeneratör yok. Özyineleme veya özyineleme sınırı yok. Sadece yineleme:

def flatten(TheList):
    listIsNested = True

    while listIsNested:                 #outer loop
        keepChecking = False
        Temp = []

        for element in TheList:         #inner loop
            if isinstance(element,list):
                Temp.extend(element)
                keepChecking = True
            else:
                Temp.append(element)

        listIsNested = keepChecking     #determine if outer loop exits
        TheList = Temp[:]

    return TheList

Bu iki liste ile çalışır: döngü için bir iç ve dış while döngüsü.

Döngünün içi liste boyunca yinelenir. Bir liste öğesi bulursa, (1) bir bölüm iç içe yerleştirme düzeyini düzleştirmek için list.extend () yöntemini kullanır ve (2) keepChecking öğesini True olarak değiştirir. keepchecking dış while döngüsünü kontrol etmek için kullanılır. Dış döngü doğru olarak ayarlanırsa, iç döngüyü başka bir geçiş için tetikler.

İç içe geçmiş liste bulunana kadar bu geçişler gerçekleşmeye devam eder. Sonunda hiçbirinin bulunmadığı yerde bir geçiş gerçekleştiğinde keepChecking hiçbir zaman true değerine geçmez, bu da listIsNested öğesinin false ve döngü çıkarken dışta kaldığı anlamına gelir.

Sonra düzleştirilmiş liste döndürülür.

Test sürüşü

flatten([1,2,3,4,[100,200,300,[1000,2000,3000]]])

[1, 2, 3, 4, 100, 200, 300, 1000, 2000, 3000]


Ben de basit severim. Ancak bu durumda, yuvalar veya seviyeler olduğu kadar liste üzerinde yineleme yaparsınız. Pahalı olabilir.
telliott99

@ telliott99: Listeleriniz gerçekten büyük ve / veya büyük derinliklere sahipse haklısınız. Ancak, durum böyle değilse, daha basit çözüm de aynı şekilde ve diğer bazı cevapların derin büyüsü olmadan da çalışır. Çok aşamalı özyinelemeli jeneratör kavrayışları için bir yer var, ancak bunun ilk baktığınız yerde olması gerektiğine inanmıyorum. (Sanırım "Daha Kötü Daha İyi" tartışmasında nereye gittiğimi biliyorsun.)
kil

@ telliott99: Ya da başka bir deyişle, çözümümü "Grok yapmaya çalışmanız" gerekmeyecek. Performans bir darboğaz değilse, programcı olarak sizin için en önemli olan nedir?
kil

Daha basit çözümlerin daha az mantığı vardır. Özyineleme, kendini bir programcı olarak gören herkesin tamamen rahat olması gereken oldukça temel bir programlama yapısıdır. Jeneratörler Python Way'dir ve (kavrayışlarla birlikte) herhangi bir profesyonel Python programcısının anında yapması gereken bir şeydir.
dash-tom-bang

1
Özyineleme konusunda hemfikirim. Cevabımı yazdığımda, python hala 1000 döngüde özyinelemeyi kırdı. Bunu değiştirdiler mi? Profesyonel bir python programcısı olmaya gelince, ben değilim. Dahası, python'da programlama yapan birçok insanın tam zamanlı yapmadığını hayal ediyorum.
kil

4

İşte keyfi derinlik listelerini düzleştiren basit bir işlev. Yığın taşmasını önlemek için özyineleme yok.

from copy import deepcopy

def flatten_list(nested_list):
    """Flatten an arbitrarily nested list, without recursion (to avoid
    stack overflows). Returns a new list, the original list is unchanged.

    >> list(flatten_list([1, 2, 3, [4], [], [[[[[[[[[5]]]]]]]]]]))
    [1, 2, 3, 4, 5]
    >> list(flatten_list([[1, 2], 3]))
    [1, 2, 3]

    """
    nested_list = deepcopy(nested_list)

    while nested_list:
        sublist = nested_list.pop(0)

        if isinstance(sublist, list):
            nested_list = sublist + nested_list
        else:
            yield sublist


3

Kimsenin bunu düşünmediğine şaşırdım. Kahretsin özyineleme Buradaki ileri düzey insanların verdiği özyineli cevapları alamıyorum. neyse işte bu benim girişimim. uyarı OP'nin kullanım durumuna çok özel

import re

L = [[[1, 2, 3], [4, 5]], 6]
flattened_list = re.sub("[\[\]]", "", str(L)).replace(" ", "").split(",")
new_list = list(map(int, flattened_list))
print(new_list)

çıktı:

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

3

Burada zaten var olan tüm cevapları gözden geçirmedim, ama burada bulduğum tek bir astar, lisp'in ilk ve dinlenme listesi işleme yönteminden ödünç alındı

def flatten(l): return flatten(l[0]) + (flatten(l[1:]) if len(l) > 1 else []) if type(l) is list else [l]

İşte basit ve basit olmayan bir durum -

>>> flatten([1,[2,3],4])
[1, 2, 3, 4]

>>> flatten([1, [2, 3], 4, [5, [6, {'name': 'some_name', 'age':30}, 7]], [8, 9, [10, [11, [12, [13, {'some', 'set'}, 14, [15, 'some_string'], 16], 17, 18], 19], 20], 21, 22, [23, 24], 25], 26, 27, 28, 29, 30])
[1, 2, 3, 4, 5, 6, {'age': 30, 'name': 'some_name'}, 7, 8, 9, 10, 11, 12, 13, set(['set', 'some']), 14, 15, 'some_string', 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30]
>>> 

Tek bir astar değil. Birine ne kadar sığdırmaya çalışırsanız çalışın, def foo():ayrı bir satırdır. Ayrıca, bu çok okunmaz.
cs95

Ben de-one-line-ified kodu, ve biraz daha yeniden düzenleme yaptım. (Bu yazarken düzenleme düzenlemek emsal inceleme bekliyor) Orijinal kod bazı yeniden düzenleme ihtiyacı olmasına rağmen, bu belirli yöntem benim için çok okunabilir görünüyordu.
Emilio M Bumachar

3

Böyle bir soruyu cevaplamaya çalışırken, çözüm olarak önerdiğiniz kodun sınırlamalarını gerçekten vermeniz gerekir. Sadece performanslar hakkında olsaydı, çok fazla umursamadım, ancak çözüm olarak önerilen kodların çoğu (kabul edilen cevap dahil), 1000'den fazla derinliğe sahip herhangi bir listeyi düzleştiremez.

Kodların çoğunu söylediğimde ben özyineleme hiçbir şekilde kullanan tüm kodları anlamına (veya özyinelemeli olan standart kütüphane işlevi çağırmak). Tüm bu kodlar başarısız olur, çünkü yapılan her özyinelemeli çağrı için (çağrı) yığını bir birim büyür ve (varsayılan) python çağrı yığını 1000 büyüklüğündedir.

Çağrı yığınına çok aşina değilseniz, belki aşağıdakiler yardımcı olacaktır (aksi takdirde sadece Uygulama'ya geçebilirsiniz ).

Çağrı yığını boyutu ve özyinelemeli programlama (zindan benzetmesi)

Hazineyi bulmak ve çıkmak

Bir hazine arayan, numaralı odalar ile büyük bir zindan girdiğinizi düşünün . Yeri bilmiyorsun ama hazineyi nasıl bulacağına dair bazı göstergelerin var . Her bir gösterge bir bilmecedir (zorluk değişir, ancak ne kadar zor olacağını tahmin edemezsiniz). Zaman kazanmak için bir strateji hakkında biraz düşünmeye karar verirsiniz, iki gözlem yaparsınız:

  1. Oraya ulaşmak için (potansiyel olarak zor) bilmeceleri çözmeniz gerekeceğinden hazineyi bulmak zor (uzun).
  2. Hazine bulunduğunda, girişe geri dönmek kolay olabilir, sadece aynı yolu diğer yönde kullanmanız gerekir (ancak yolunuzu hatırlamak için biraz belleğe ihtiyaç duyar).

Zindana girerken, burada küçük bir not defteri görüyorsunuz . Bilmeceyi çözdükten sonra çıktığınız her odayı (yeni bir odaya girerken) yazmak için kullanmaya karar verirsiniz, böylece girişe geri dönebilirsiniz. Bu dahice bir fikir, stratejinizi uygulamak için bir kuruş bile harcamazsınız .

Zindana giriyorsunuz, ilk 1001 bilmeceyi büyük bir başarıyla çözüyorsunuz, ancak burada planlayamadığınız bir şey geliyor, ödünç aldığınız not defterinde boş alanınız yok. Hazineye sahip olmayı zindanın içinde sonsuza dek kaybolmaktan (gerçekten akıllı görünüyor) tercih ettiğiniz için görevinizden vazgeçmeye karar veriyorsunuz.

Özyinelemeli bir program yürütme

Temel olarak, hazineyi bulmakla aynı şey. Zindan bilgisayarın hafızasıdır , şimdi amacınız bir hazine bulmak değil, belirli bir işlevi hesaplamak ( belirli bir x için f (x) bul ). Endikasyonlar basitçe f (x) 'i çözmenize yardımcı olacak alt rutinlerdir . Stratejiniz çağrı yığını stratejisi ile aynıdır , dizüstü bilgisayar yığınıdır, odalar fonksiyonların dönüş adresleridir:

x = ["over here", "am", "I"]
y = sorted(x) # You're about to enter a room named `sorted`, note down the current room address here so you can return back: 0x4004f4 (that room address looks weird)
# Seems like you went back from your quest using the return address 0x4004f4
# Let's see what you've collected 
print(' '.join(y))

Zindanda karşılaştığınız sorun burada aynı olacak, çağrı yığını sonlu bir boyuta sahip (burada 1000) ve bu nedenle, geri dönmeden çok fazla işlev girerseniz, çağrı yığınını dolduracak ve görünen bir hataya sahip olacaksınız. gibi "Sevgili maceraperest, çok üzgünüm ama dizüstü dolu" : RecursionError: maximum recursion depth exceeded. Çağrı yığınını doldurmak için özyinelemeye ihtiyacınız olmadığını unutmayın, ancak özyinelemesiz bir programın geri dönmeden 1000 işlevini çağırması pek olası değildir. Ayrıca, bir işlevden döndüğünüzde, çağrı yığınının kullanılan adresten kurtarıldığını (dolayısıyla "yığının" adı, bir işleve girmeden önce geri dönüş adresinin içeri aktarıldığını ve geri dönerken çıkarıldığını da anlamanız önemlidir. Özel bir özyineleme durumunda (bir işlevfkendini bir kez tekrar tekrar çağırırsınız) f, hesaplama bitene kadar (hazine bulunana kadar ) tekrar tekrar gireceksiniz ve ilk etapta faradığınız yere geri dönene kadar geri döneceksiniz f. Çağrı yığını, tüm dönüş adreslerinden birbiri ardına serbest bırakılacak olana kadar hiçbir şeyden asla kurtarılmayacaktır.

Bu sorun nasıl önlenir?

Bu aslında oldukça basit: "ne kadar derine inebileceğini bilmiyorsanız özyineleme kullanmayın". Bazı durumlarda, Kuyruk Çağrısı özyinelemesi Optimize Edilebilir (TCO) olduğu için bu her zaman doğru değildir . Ancak python'da durum böyle değildir ve "iyi yazılmış" özyinelemeli fonksiyon bile yığın kullanımını optimize etmez . Guido'dan bu soru hakkında ilginç bir yazı var: Tail Recursion Elimination .

Herhangi bir özyinelemeli işlevi yinelemeli hale getirmek için kullanabileceğiniz bir teknik vardır, bu teknik kendi dizüstü bilgisayarınızı getirebiliriz . Örneğin, özel durumumuzda, sadece bir liste keşfediyoruz, bir odaya girmek bir alt listeye girmeye eşdeğerdir, kendinize sormanız gereken soru , bir listeden üst listesine nasıl geri dönebilirim? Cevap o kadar karmaşık değil stack, boş olana kadar aşağıdakileri tekrarlayın :

  1. Geçerli listeyi itmek addressve indexbir de stackyeni bir alt listesini girerek (not listesi adresi + indeks bu nedenle biz sadece çağrı yığını tarafından kullanılan aynı tekniği kullanabilirsiniz, aynı zamanda bir adresi olduğunu);
  2. bir öğe her bulunduğunda öğeyi yield(veya listeye ekleyin);
  3. bir liste tamamen keşfedildikten sonra, stack return address(ve index) tuşlarını kullanarak üst listeye geri dönün .

Bunun, bazı düğümlerin alt liste A = [1, 2]ve bazılarının basit öğeler olduğu bir ağaçtaki DFS'ye eşdeğer olduğuna dikkat edin : 0, 1, 2, 3, 4(for L = [0, [1,2], 3, 4]). Ağaç şöyle görünür:

                    L
                    |
           -------------------
           |     |     |     |
           0   --A--   3     4
               |   |
               1   2

DFS çapraz geçiş ön sırası şöyledir: L, 0, A, 1, 2, 3, 4. Bir yinelemeli DFS uygulamak için ayrıca bir yığına "ihtiyacınız" olduğunu unutmayın. Daha önce önerdiğim uygulama aşağıdaki durumlar ( stackve için flat_list) ile sonuçlanır :

init.:  stack=[(L, 0)]
**0**:  stack=[(L, 0)],         flat_list=[0]
**A**:  stack=[(L, 1), (A, 0)], flat_list=[0]
**1**:  stack=[(L, 1), (A, 0)], flat_list=[0, 1]
**2**:  stack=[(L, 1), (A, 1)], flat_list=[0, 1, 2]
**3**:  stack=[(L, 2)],         flat_list=[0, 1, 2, 3]
**3**:  stack=[(L, 3)],         flat_list=[0, 1, 2, 3, 4]
return: stack=[],               flat_list=[0, 1, 2, 3, 4]

Bu örnekte, giriş listesinin (ve dolayısıyla ağacın) derinliği 2 olduğu için yığın maksimum boyutu 2'dir.

uygulama

Uygulama için, python'da basit listeler yerine yineleyicileri kullanarak biraz basitleştirebilirsiniz. Alt listelerin dönüş adreslerini (hem liste adresine hem de dizine sahip olmak yerine) depolamak için (alt) yineleyicilere referanslar kullanılacaktır . Bu büyük bir fark değil ama bunun daha okunabilir olduğunu düşünüyorum (ve ayrıca biraz daha hızlı):

def flatten(iterable):
    return list(items_from(iterable))

def items_from(iterable):
    cursor_stack = [iter(iterable)]
    while cursor_stack:
        sub_iterable = cursor_stack[-1]
        try:
            item = next(sub_iterable)
        except StopIteration:   # post-order
            cursor_stack.pop()
            continue
        if is_list_like(item):  # pre-order
            cursor_stack.append(iter(item))
        elif item is not None:
            yield item          # in-order

def is_list_like(item):
    return isinstance(item, list)

Ayrıca, bu haber is_list_likeBen isinstance(item, list)daha girdi türlerini işlemek için değiştirilebilir olan, burada ben sadece istediğim (iterable) sadece bir liste sade hâli olması. Ama bunu da yapabilirsiniz:

def is_list_like(item):
    try:
        iter(item)
        return not isinstance(item, str)  # strings are not lists (hmm...) 
    except TypeError:
        return False

Bu, dizeleri "basit öğeler" olarak kabul eder ve bu nedenle flatten_iter([["test", "a"], "b])geri döner ["test", "a", "b"]ve döndürmez ["t", "e", "s", "t", "a", "b"]. Bu durumda, iter(item)her öğede iki kez çağrıldığına dikkat edin, okuyucunun bunu daha temiz hale getirmesi için bir alıştırma yapalım.

Test ve diğer uygulamalar hakkında açıklamalar

Sonunda, Lkullanarak sonsuz sayıda iç içe bir liste yazdıramayacağınızı unutmayın, print(L)çünkü dahili olarak __repr__( RecursionError: maximum recursion depth exceeded while getting the repr of an object) için özyinelemeli çağrılar kullanacaktır . Aynı nedenle, çözümleri flatteniçeren straynı hata iletisiyle başarısız olur.

Çözümünüzü test etmeniz gerekiyorsa, basit bir iç içe liste oluşturmak için bu işlevi kullanabilirsiniz:

def build_deep_list(depth):
    """Returns a list of the form $l_{depth} = [depth-1, l_{depth-1}]$
    with $depth > 1$ and $l_0 = [0]$.
    """
    sub_list = [0]
    for d in range(1, depth):
        sub_list = [d, sub_list]
    return sub_list

Hangi verir: build_deep_list(5)>>> [4, [3, [2, [1, [0]]]]].


2

İşte compiler.ast.flatten2.7.5 yılında uygulanması:

def flatten(seq):
    l = []
    for elt in seq:
        t = type(elt)
        if t is tuple or t is list:
            for elt2 in flatten(elt):
                l.append(elt2)
        else:
            l.append(elt)
    return l

Daha iyi, daha hızlı yöntemler var (Eğer buraya ulaştıysanız, daha önce görmüştünüz)

Ayrıca dikkat:

2.6 sürümünden beri kullanımdan kaldırıldı: Derleyici paketi Python 3'te kaldırıldı.


2

tamamen hacky ama işe yarayacağını düşünüyorum (data_type bağlı olarak)

flat_list = ast.literal_eval("[%s]"%re.sub("[\[\]]","",str(the_list)))

2

Sadece bir funcykütüphane kullanın : pip install funcy

import funcy


funcy.flatten([[[[1, 1], 1], 2], 3]) # returns generator
funcy.lflatten([[[[1, 1], 1], 2], 3]) # returns list

1
FYI: özyinelemeli çözüm kullanıyor: kaynağa bağlantı
Georgy

1

İşte başka bir py2 yaklaşımı, en hızlı veya en zarif veya en güvenli olup olmadığından emin değilim ...

from collections import Iterable
from itertools import imap, repeat, chain


def flat(seqs, ignore=(int, long, float, basestring)):
    return repeat(seqs, 1) if any(imap(isinstance, repeat(seqs), ignore)) or not isinstance(seqs, Iterable) else chain.from_iterable(imap(flat, seqs))

İstediğiniz herhangi bir (veya türetilmiş) türü göz ardı edebilir, bir yineleyici döndürür, böylece daha iyi veya daha kötü bir şekilde, bellek ayak izini azaltmak için liste, tuple, dikte veya herhangi bir kapsayıcıya dönüştürebilirsiniz. int gibi yinelenmeyen ilk nesneleri işleyebilir ...

Ağır kaldırma işlemlerinin çoğunun C'de yapıldığına dikkat edin, çünkü itertools'un nasıl uygulandığını bildiğim kadarıyla, özyinelemeli olsa da, AFAIK, işlev çağrıları C'de gerçekleştiği için python yineleme derinliği ile sınırlı değildir, ancak bu bellekle sınırlı olduğunuz anlamına gelmez, özellikle yığın boyutunun bugün itibariyle zor bir sınırı olduğu OS X'te (OS X Mavericks) ...

biraz daha hızlı bir yaklaşım var, ancak daha az taşınabilir bir yöntem var, sadece girişin temel öğelerinin açıkça belirlenebileceğini varsayabilirseniz kullanın, sonsuz bir özyineleme elde edersiniz ve sınırlı yığın boyutuna sahip OS X, segmentasyon hatasını oldukça hızlı atmak ...

def flat(seqs, ignore={int, long, float, str, unicode}):
    return repeat(seqs, 1) if type(seqs) in ignore or not isinstance(seqs, Iterable) else chain.from_iterable(imap(flat, seqs))

burada türü kontrol etmek için setler kullanıyoruz, bu yüzden bir elemanın göz ardı edilip edilmeyeceğini kontrol etmek O (1) ve O (tip sayısı) gerektirir, ancak elbette belirtilen ihmal edilen türlerden türetilen herhangi bir değer başarısız olur , bu yüzden kullanıyor str, unicodebu yüzden dikkatli kullanın ...

testler:

import random

def test_flat(test_size=2000):
    def increase_depth(value, depth=1):
        for func in xrange(depth):
            value = repeat(value, 1)
        return value

    def random_sub_chaining(nested_values):
        for values in nested_values:
            yield chain((values,), chain.from_iterable(imap(next, repeat(nested_values, random.randint(1, 10)))))

    expected_values = zip(xrange(test_size), imap(str, xrange(test_size)))
    nested_values = random_sub_chaining((increase_depth(value, depth) for depth, value in enumerate(expected_values)))
    assert not any(imap(cmp, chain.from_iterable(expected_values), flat(chain(((),), nested_values, ((),)))))

>>> test_flat()
>>> list(flat([[[1, 2, 3], [4, 5]], 6]))
[1, 2, 3, 4, 5, 6]
>>>  

$ uname -a
Darwin Samys-MacBook-Pro.local 13.3.0 Darwin Kernel Version 13.3.0: Tue Jun  3 21:27:35 PDT 2014; root:xnu-2422.110.17~1/RELEASE_X86_64 x86_64
$ python --version
Python 2.7.5

1

Herhangi bir kütüphane kullanmadan:

def flat(l):
    def _flat(l, r):    
        if type(l) is not list:
            r.append(l)
        else:
            for i in l:
                r = r + flat(i)
        return r
    return _flat(l, [])



# example
test = [[1], [[2]], [3], [['a','b','c'] , [['z','x','y']], ['d','f','g']], 4]    
print flat(test) # prints [1, 2, 3, 'a', 'b', 'c', 'z', 'x', 'y', 'd', 'f', 'g', 4]

1

Kullanma itertools.chain:

import itertools
from collections import Iterable

def list_flatten(lst):
    flat_lst = []
    for item in itertools.chain(lst):
        if isinstance(item, Iterable):
            item = list_flatten(item)
            flat_lst.extend(item)
        else:
            flat_lst.append(item)
    return flat_lst

Veya zincir olmadan:

def flatten(q, final):
    if not q:
        return
    if isinstance(q, list):
        if not isinstance(q[0], list):
            final.append(q[0])
        else:
            flatten(q[0], final)
        flatten(q[1:], final)
    else:
        final.append(q)

1

İç içe listeyi herhangi bir derinlikte çözmek için özyinelemeli kullandım

def combine_nlist(nlist,init=0,combiner=lambda x,y: x+y):
    '''
    apply function: combiner to a nested list element by element(treated as flatten list)
    '''
    current_value=init
    for each_item in nlist:
        if isinstance(each_item,list):
            current_value =combine_nlist(each_item,current_value,combiner)
        else:
            current_value = combiner(current_value,each_item)
    return current_value

Bu yüzden combine_nlist işlevini tanımladıktan sonra düzleştirme yapmak bu işlevi kullanmak kolaydır. Veya tek bir işlevde birleştirebilirsiniz. Çözümümü beğendim çünkü herhangi bir iç içe listeye uygulanabilir.

def flatten_nlist(nlist):
    return combine_nlist(nlist,[],lambda x,y:x+[y])

sonuç

In [379]: flatten_nlist([1,2,3,[4,5],[6],[[[7],8],9],10])
Out[379]: [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]

"herhangi bir derinlikte iç içe liste" doğru değil. Sadece göreceksiniz deneyin: current_value = combiner(current_value,each_item) RecursionError: maximum recursion depth exceeded
cglacet

hmmm 1000'den fazla katman içeren listeyi düzleştirmeye mi çalışıyorsunuz?
Oldyoung

Tabii ki, özyinelemeli ve yinelemeli çözümlerle ilgili tartışmanın konusu budur. Katman sayısının 1000'den az olduğunu önceden biliyorsanız, en basit çözüm işe yarayacaktır. "Herhangi bir derinlik" dediğinizde, bu liste derinlik> 1000 olan listeyi içerir.
cglacet

1

En kolay yol, morph kütüphanesini kullanarak kullanmaktır pip install morph.

Kod:

import morph

list = [[[1, 2, 3], [4, 5]], 6]
flattened_list = morph.flatten(list)  # returns [1, 2, 3, 4, 5, 6]

1

Zaten çok sayıda harika cevap olduğunu biliyorum ama soruyu çözmek için fonksiyonel programlama yöntemini kullanan bir cevap eklemek istedim. Bu cevapta çift özyineleme kullanıyorum:

def flatten_list(seq):
    if not seq:
        return []
    elif isinstance(seq[0],list):
        return (flatten_list(seq[0])+flatten_list(seq[1:]))
    else:
        return [seq[0]]+flatten_list(seq[1:])

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

çıktı:

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

1

Bunun daha hızlı veya daha etkili olup olmadığından emin değilim, ancak yaptığım şey bu:

def flatten(lst):
    return eval('[' + str(lst).replace('[', '').replace(']', '') + ']')

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

Buradaki flattenişlev listeyi bir dizgiye dönüştürür, tüm köşeli parantezleri çıkarır , köşeli parantezleri uçlara geri ekler ve listeye geri döndürür.

Her ne kadar, listenizde köşeli parantezlerin olacağını biliyor olsaydınız, örneğin [[1, 2], "[3, 4] and [5]"], başka bir şey yapmanız gerekir.


Bu, derin listelerin işlenememesi nedeniyle basit bir çözüme göre bir avantajı yoktur, örneğin "Özyineleme Hatası: bir nesnenin repr değeri alınırken maksimum özyineleme derinliği aşıldı".
cglacet

1

Bu basit bir python2 düzleştirme uygulamasıdır

flatten=lambda l: reduce(lambda x,y:x+y,map(flatten,l),[]) if isinstance(l,list) else [l]

test=[[1,2,3,[3,4,5],[6,7,[8,9,[10,[11,[12,13,14]]]]]],]
print flatten(test)

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

1

Bu, bir listeyi veya sözlüğü (veya liste veya sözlük sözlükleri listesi) düzleştirir. Değerlerin dize olduğunu varsayar ve her öğeyi ayırıcı argümanla birleştiren bir dize oluşturur. İsterseniz, sonucu daha sonra bir liste nesnesine bölmek için ayırıcıyı kullanabilirsiniz. Sonraki değer bir liste veya dize ise özyineleme kullanır. Sözlük nesnesinden anahtarları veya değerleri (anahtarı false olarak ayarlayın) isteyip istemediğinizi söylemek için key argümanını kullanın.

def flatten_obj(n_obj, key=True, my_sep=''):
    my_string = ''
    if type(n_obj) == list:
        for val in n_obj:
            my_sep_setter = my_sep if my_string != '' else ''
            if type(val) == list or type(val) == dict:
                my_string += my_sep_setter + flatten_obj(val, key, my_sep)
            else:
                my_string += my_sep_setter + val
    elif type(n_obj) == dict:
        for k, v in n_obj.items():
            my_sep_setter = my_sep if my_string != '' else ''
            d_val = k if key else v
            if type(v) == list or type(v) == dict:
                my_string += my_sep_setter + flatten_obj(v, key, my_sep)
            else:
                my_string += my_sep_setter + d_val
    elif type(n_obj) == str:
        my_sep_setter = my_sep if my_string != '' else ''
        my_string += my_sep_setter + n_obj
        return my_string
    return my_string

print(flatten_obj(['just', 'a', ['test', 'to', 'try'], 'right', 'now', ['or', 'later', 'today'],
                [{'dictionary_test': 'test'}, {'dictionary_test_two': 'later_today'}, 'my power is 9000']], my_sep=', ')

verim:

just, a, test, to, try, right, now, or, later, today, dictionary_test, dictionary_test_two, my power is 9000

0

Özyinelemeyi seviyorsanız, bu sizi ilgilendiren bir çözüm olabilir:

def f(E):
    if E==[]: 
        return []
    elif type(E) != list: 
        return [E]
    else:
        a = f(E[0])
        b = f(E[1:])
        a.extend(b)
        return a

Aslında bunu bir süre önce yazdığım bazı uygulama Şeması kodundan uyarladım.

Zevk almak!


0

Python'da yeniyim ve lisp kökenliyim. Bu ben ile geldi (lulz için var isimlerine göz atın):

def flatten(lst):
    if lst:
        car,*cdr=lst
        if isinstance(car,(list,tuple)):
            if cdr: return flatten(car) + flatten(cdr)
            return flatten(car)
        if cdr: return [car] + flatten(cdr)
        return [car]

Çalışıyor gibi görünüyor. Ölçek:

flatten((1,2,3,(4,5,6,(7,8,(((1,2)))))))

İadeler:

[1, 2, 3, 4, 5, 6, 7, 8, 1, 2]
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.