web-dev-qa-db-ja.com

不規則なリストのリストを平坦化する

はい、このテーマは以前に説明されていました( herehereherehere )私の知る限り、1つを除くすべてのソリューションは、次のようなリストで失敗します。

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

目的の出力は

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

または、おそらくさらに良いのはイテレーターです。私が見た唯一の解決策は、任意のネストで機能することです この質問では

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)

これは最高のモデルですか?私は何かを見落としていましたか?問題はありますか?

394
telliott99

ジェネレーター関数を使用すると、サンプルが少し読みやすくなり、おそらくパフォーマンスが向上します。

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

2.6で追加された Iterable ABC を使用しました。

Python 3

Python 3では、basestringはもうありませんが、strbytesのタプルを使用して同じ効果を得ることができます。

yield from演算子は、ジェネレータから1つずつアイテムを返します。この サブジェネレーターに委任する構文 は3.3で追加されました

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
343
Cristian

私の解決策:

import collections


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

もう少し簡潔ですが、ほとんど同じです。

47
Josh Lee

コメントで@Andrewが要求した@unutbuの非再帰的ソリューションのジェネレーターバージョン:

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

このジェネレーターのわずかに簡略化されたバージョン:

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)
34
Alex Martelli

再帰とカモタイピングを使用したジェネレーター(Python 3用に更新):

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]
33
dansalmo

このバージョンのflattenは、Pythonの再帰制限を回避します(したがって、任意の深さのネストされた反復可能オブジェクトで動作します)。これは、文字列と任意のイテラブル(無限のものでも)を処理できるジェネレータです。

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

以下に、その使用法を示すいくつかの例を示します。

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]

flattenは無限のジェネレーターを処理できますが、無限のネストは処理できません。

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

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

ここに、タプルとリストの両方を処理し、位置引数の任意の組み合わせをスローできる再帰的フラット化の機能バージョンがあります。 argごとに順番にシーケンス全体を生成するジェネレーターを返します。

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

使用法:

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]
23
samplebias

さらに興味深い回答があります...

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)

基本的に、ネストされたリストを文字列に変換し、正規表現を使用してネストされた構文を取り除き、結果を(フラット化された)リストに変換します。

13
clay
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
10
kev

サードパーティのパッケージの deepflatten を使用できます 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]

これはイテレータなので、反復する必要があります(たとえば、listでラップするか、ループで使用します)。内部的には、再帰的なアプローチの代わりに反復的なアプローチを使用し、C拡張として記述されているため、純粋なPythonアプローチよりも高速になります。

>>> %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)

私はiteration_utilitiesライブラリの作成者です。

7
MSeifert

Pythonで不規則なリストをフラット化できる関数を作成するのは楽しかったですが、もちろんそれは(プログラミングを楽しくするための)Pythonの目的です。次のジェネレーターは、いくつかの注意事項でかなりうまく機能します。

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

そのままにしておきたいデータ型(bytearraybytesstrオブジェクトなど)をフラット化します。また、コードは、反復不可能なオブジェクトから反復子を要求すると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]
>>>

編集:

以前の実装には同意しません。問題は、反復可能でないものを平坦化できないことです。紛らわしく、議論の印象が間違っています。

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

次のジェネレーターは最初のジェネレーターとほとんど同じですが、反復不可能なオブジェクトをフラット化しようとする問題はありません。不適切な引数が与えられた場合に予想されるように失敗します。

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

ジェネレーターのテストは、提供されたリストで正常に機能します。ただし、新しいコードは、反復不可能なオブジェクトが与えられたときにTypeErrorを発生させます。新しい動作の例を以下に示します。

>>> 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
>>>
6
Noctis Skytower

エレガントで非常にPython的な答えが選択されていますが、レビューのためだけに私のソリューションを提示します:

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

このコードの良し悪しを教えてください。

5
Xolve

これは、任意の深さのリストを平坦化する単純な関数です。スタックオーバーフローを回避するための再帰なし。

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
4
Wilfred Hughes

単純な答えが好きです。発電機はありません。再帰または再帰の制限はありません。ちょうど反復:

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

これは、内側のforループと外側のwhileループの2つのリストで機能します。

内側のforループはリストを反復処理します。リスト要素が見つかった場合、(1)list.extend()を使用してその部分を1レベルのネストにフラット化し、(2)keepCheckingをTrueに切り替えます。 keepcheckingは、外側のwhileループを制御するために使用されます。外側のループがtrueに設定されると、別のパスで内側のループがトリガーされます。

これらのパスは、ネストされたリストが見つからなくなるまで発生し続けます。パスが見つからない場所で最終的にパスが発生した場合、keepCheckingがtrueにトリップすることはありません。つまり、listIsNestedはfalseのままで、外側のwhileループは終了します。

その後、フラット化されたリストが返されます。

テスト走行

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

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

4
clay

2.7.5でのcompiler.ast.flattenの実装は次のとおりです。

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

より優れた、より高速な方法があります(ここに到達した場合は、すでにそれらを見ています)

また注意してください:

バージョン2.6以降非推奨:コンパイラパッケージはPython 3で削除されました。

2
pradyunsg

ここですでに利用可能なすべての回答を行ったわけではありませんが、LISPの最初と残りのリストの処理方法を借用した1つのライナーがあります

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

ここに1つの単純なケースと1つのそれほど単純でないケースがあります

>>> 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]
>>> 
2
Shreyas

誰もこれを考えていないことに驚いています。いまいましい再帰ここの上級者が作った再帰的な答えは得られません。とにかくこれは私の試みです。警告は、OPのユースケースに非常に固有のものです

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)

出力:

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

最も簡単な方法は、pip install morphを使用して morph ライブラリを使用することです。

コードは次のとおりです。

import morph

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

完全にハッキーですが、うまくいくと思います(data_typeによって異なります)

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

これは、python2でのflattenの単純な実装です。

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
Statham

ここに別のpy2アプローチがあります、その最速か最もエレガントで安全かはわかりません...

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))

希望する特定の(または派生した)型を無視し、イテレータを返すため、リスト、タプル、dictなどの特定のコンテナに変換したり、メモリフットプリントを削減するために単純に消費したりできます。 intなどの初期の反復不可能なオブジェクトを処理できます...

私が知っている限りitertoolsがどのように実装されているかを知っている限り、それは再帰的ですが、関数呼び出しがCで行われているため、Pythonの再帰の深さに制限されていないため、ほとんどの重い持ち上げはCで行われますメモリに縛られているという意味ではありません。特に、現在スタックサイズに厳しい制限があるOS X(OS X Mavericks)では...

やや高速なアプローチがありますが、移植性の低い方法です。入力の基本要素を明示的に決定できると想定できる場合にのみ使用してください。無限再帰が発生し、スタックサイズが制限されたOS Xセグメンテーション違反をかなり迅速にスローします...

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))

ここでは、セットを使用して型をチェックしているため、要素を無視する必要があるかどうかを確認するためにO(1) vs O(型の数)が必要ですが、派生型の値はもちろん指定された無視された型は失敗します、これがstrunicodeを使用する理由ですので、注意して使用してください...

テスト:

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
Samy Vilar

そのような質問に答えようとするとき、あなたは本当にあなたが解決策として提案するコードの制限を与える必要があります。パフォーマンスについてだけであれば、あまり気にしませんが、解決策として提案されたコードのほとんど(受け入れられた回答を含む)は、深さが1000を超えるリストをフラット化できません。

ほとんどのコードと言うとき、あらゆる形式の再帰を使用する(または再帰的な標準ライブラリ関数を呼び出す)すべてのコードを意味します。再帰呼び出しが行われるたびに、(呼び出し)スタックが1単位増加し、(デフォルト)python呼び出しスタックのサイズが1000であるため、これらのコードはすべて失敗します。

呼び出しスタックにあまり詳しくない場合は、次の方法が役立つかもしれません(そうでなければ、Implementationまでスクロールできます)。

呼び出しスタックサイズと再帰プログラミング(ダンジョンの類推)

宝物を見つけて終了する

あなたが巨大な部屋に番号が付けられたダンジョンに入り、宝を探していると想像してください。場所はわかりませんが、宝物を見つける方法について表示があります。それぞれの兆候は謎です(難易度は異なりますが、どれほど難しいかを予測することはできません)。時間を節約するための戦略について少し考えることにし、2つの観察を行います。

  1. そこにたどり着くために(潜在的に難しい)謎を解かなければならないので、宝物を見つけるのは難しい(長い)です。
  2. 宝物が見つかったら、入り口に戻るのは簡単かもしれませんが、同じパスを他の方向に使用するだけです(ただし、パスを思い出すには少しのメモリが必要です)。

ダンジョンに入ると、小さなノートブックがここに表示されます。謎を解いた後(新しい部屋に入るとき)に退出するすべての部屋を書き留めるために使用することにします。これにより、入り口に戻ることができます。それは天才的なアイデアです。あなたは1セントも払わない戦略を実行します。

あなたはダンジョンに入り、最初の1001の謎を大成功で解決しますが、ここにはあなたが計画していなかったものがあり、借りたノートにはスペースが残っていません。 abandonダンジョン内で永遠に失われるよりも宝物を持たないほうがいいので、あなたの探求を決定します(確かに賢く見えます)。

再帰プログラムの実行

基本的に、それは宝を見つけることとまったく同じです。ダンジョンはコンピュータのメモリです。あなたの目標は宝を見つけることではなく、何らかの関数を計算する(find f(x)与えられたx)。指示は単にf(x)の解決に役立つサブルーチンです。あなたの戦略はcall stack戦略と同じで、ノートブックはスタック、部屋は関数のリターンアドレスです:

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))

ダンジョンで発生した問題はここでも同じです。コールスタックは有限サイズ(ここでは1000)であるため、戻ることなく多くの関数を入力すると、コールスタックがいっぱいになり、表示されるエラーが発生します。のような 「冒険者の皆さん、ごめんなさい。でもノートはいっぱいです」RecursionError: maximum recursion depth exceeded。呼び出しスタックを満たすために再帰を必要としないことに注意してください。しかし、非再帰プログラムが戻ることなく1000関数を呼び出すことはほとんどありません。関数から戻ると、呼び出しスタックは使用されているアドレスから解放されることも理解することが重要です(そのため、「スタック」という名前のため、関数に入る前に戻りアドレスがプッシュインされ、戻るときにプルアウトされます)。単純な再帰の特別な場合(それ自体を1回呼び出す関数f)-計算が終了するまで(トレジャーが見つかるまで)fを何度も入力し、fから戻るまで最初にfを呼び出した場所に戻ります。呼び出しスタックは、すべての戻りアドレスから順番に解放される最後まで、何も解放されません。

この問題を回避する方法は?

それは実際には非常に簡単です。「再帰の深さがわからない場合は再帰を使用しないでください」。 Tail Call recursion is Optimized(TCO) のように、場合によっては常にそうとは限りません。しかし、Pythonではこれは当てはまりません。「よく書かれた」再帰関数でさえ、notスタックの使用を最適化します。この質問について、Guidoから興味深い投稿があります: Tail Recursion Elimination

再帰関数を反復するために使用できるテクニックがあります。このテクニックは、独自のノートブックと呼ぶことができます。たとえば、特定のケースでは、リストを探索するだけで、部屋に入ることはサブリストを入力することと同等です。あなたが自問すべき質問はリストから親リストに戻るにはどうすればよいですか? =答えはそれほど複雑ではありません。stackが空になるまで以下を繰り返します。

  1. 新しいサブリストを入力するときに、現在のリストaddressおよびindexstackにプッシュします(リストアドレス+インデックスもアドレスであるため、コールスタックで使用されるのとまったく同じ手法を使用します)。
  2. アイテムが見つかるたびに、yield it(またはリストに追加);
  3. リストが完全に探索されたら、stackreturn address(およびindexを使用して親リストに戻ります。

また、これは、一部のノードがA = [1, 2]であり、一部が単純な項目であるツリーのDFSと同等であることに注意してください:0, 1, 2, 3, 4L = [0, [1,2], 3, 4]の場合)。ツリーは次のようになります。

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

DFSトラバーサルの事前注文は、L、0、A、1、2、3、4です。反復DFSを実装するには、スタックも「必要」です。前に提案した実装では、次の状態になります(stackおよびflat_listについて)。

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]

この例では、入力リスト(したがってツリー)の深さが2であるため、スタックの最大サイズは2です。

実装

実装では、Pythonで、単純なリストの代わりにイテレーターを使用することにより、少し単純化できます。 (サブ)イテレータへの参照は、(リストアドレスとインデックスの両方を持つ代わりに)サブリストリターンアドレスを格納するために使用されます。これは大きな違いではありませんが、私はこれがより読みやすい(そして少し速い)と感じています:

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)

また、is_list_likeにはisinstance(item, list)があり、これを変更してより多くの入力タイプを処理できることに注意してください。ここでは、(反復可能)が単なるリストである最も単純なバージョンが必要です。しかし、あなたはそれをすることもできます:

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

これは文字列を「単純なアイテム」と見なすため、flatten_iter([["test", "a"], "b])["test", "a", "b"]ではなく["t", "e", "s", "t", "a", "b"]を返します。その場合、iter(item)が各アイテムで2回呼び出されることに注意してください。読者がこのクリーナーを作成するための練習だとしましょう。

他の実装のテストと注意

最後に、print(L)を使用して無限にネストされたリストLを印刷できないことに注意してください。これは、内部で__repr__RecursionError: maximum recursion depth exceeded while getting the repr of an object)の再帰呼び出しを使用するためです。同じ理由で、flattenを含むstrのソリューションは、同じエラーメッセージで失敗します。

ソリューションをテストする必要がある場合は、この関数を使用して単純なネストされたリストを生成できます。

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

これは、build_deep_list(5) >>> [4, [3, [2, [1, [0]]]]]を提供します。

1
cglacet

私はすでに多くの素晴らしい答えがあることを知っていますが、質問を解くための関数型プログラミング手法を使用する答えを追加したかったです。この答えでは、二重再帰を利用しています:

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]]))

出力:

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

これが必然的に速くなるのか、それとも効果的になるのかはわかりませんが、これは私がやっていることです:

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

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

ここのflatten関数は、リストを文字列に変換し、角括弧のallを取り出し、角括弧を両端に付加して、リストに戻します。

ただし、[[1, 2], "[3, 4] and [5]"]のような文字列のリストに角かっこがあるとわかっている場合は、何か他のことをする必要があります。

1
diligar

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

またはチェーンなし:

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
Saksham Varma

funcy ライブラリを使用するだけです:pip install funcy

import funcy


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

再帰を使用して任意の深さのネストされたリスト

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

したがって、関数compose_nlistを定義した後、この関数を使用して簡単にフラット化できます。または、1つの関数に結合できます。ネストされたリストに適用できるため、私のソリューションが気に入っています。

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

結果

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]
1
Oldyoung

Python 3を使用した反復ソリューション

このソリューションは、strおよびbytesを除くすべてのオブジェクトで機能する場合があります。

from collections import Iterable
from collections import Iterator


def flat_iter(obj):
    stack = [obj]
    while stack:
        element = stack.pop()
        if element and isinstance(element, Iterator):
            stack.append(element)
            try:
                stack.append(next(element))
            except StopIteration:
                stack.pop()
        Elif isinstance(element, Iterable) and not isinstance(element, (str, bytes)):
            stack.append(iter(element))
        else:
            yield element


tree_list = [[(1,2,3),(4,5,6, (7,8, 'next element is 5')), (5,6), [[[3,4,5],'foo1'],'foo2'],'foo3']]

not_iterable = 10

it1 = flat_iter(tree_list)
it2 = flat_iter(not_iterable)

print(list(it1))
print(list(it2))

[1、2、3、4、5、6、7、8、「次の要素は5」、5、6、3、4、5、「foo1」、「foo2」、「foo3」]

[10]

0
DeaD_EyE

私は愚かな男なので、「愚かな」ソリューションを提供します。その再帰はすべて私の脳を傷つけます。

flattened_list = []
nested_list = [[[1, 2, 3], [4, 5]], 6]

def flatten(nested_list, container):
    for item in nested_list:
        if isintance(item, list):
            flatten(item)
        else:
            container.append(i)

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

私はそれが副作用を使用していることを理解していますが、それは再帰の私の理解の最高です

0
halcyonjuly7

このようなものはここに投稿されておらず、同じテーマに関する非公開の質問からここに来ましたが、なぜこのようなことをしないのですか(分割したいリストのタイプがわかっている場合):

>>> a = [1, 2, 3, 5, 10, [1, 25, 11, [1, 0]]]    
>>> g = str(a).replace('[', '').replace(']', '')    
>>> b = [int(x) for x in g.split(',') if x.strip()]

要素のタイプを知る必要がありますが、これは一般化でき、速度の点ではより高速になると思います。

0
Bogdan

再帰またはネストされたループはありません。数行。適切にフォーマットされ、読みやすい:

def flatten_deep(arr: list):
""" Flattens arbitrarily-nested list `arr` into single-dimensional. """

while arr:
    if isinstance(arr[0], list):  # Checks whether first element is a list
        arr = arr[0] + arr[1:]  # If so, flattens that first element one level
    else:
        yield arr.pop(0)  # Otherwise yield as part of the flat array

flatten_deep(L)

https://github.com/jorgeorpinel/flatten_nested_lists/blob/master/flatten.py にある自分のコードから

0
Jorge Orpinel

ライブラリを使用しない場合:

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]
0
alfasin

これにより、リストまたは辞書(またはリストのリストまたは辞書の辞書など)がフラット化されます。値が文字列であると想定し、セパレーター引数で各アイテムを連結する文字列を作成します。必要に応じて、後でセパレータを使用して結果をリストオブジェクトに分割できます。次の値がリストまたは文字列の場合、再帰を使用します。 key引数を使用して、ディクショナリオブジェクトのキーまたは値(キーをfalseに設定)のどちらを使用するかを指定します。

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=', ')

収量:

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

私はpythonを初めて使い、LISPのバックグラウンドから来ました。これは私が思いついたものです(lulzのvar名を確認してください):

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]

動作しているようです。テスト:

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

戻り値:

[1, 2, 3, 4, 5, 6, 7, 8, 1, 2]
0
Michael Puckett

Python-

from collections import Iterable

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

def flatten(thing):
    result = []

    if isinstance(thing, Iterable):
        for item in thing:
            result.extend(flatten(item))
    else:
        result.append(thing)

    return result


flat = flatten(L)
print(flat)
0
Fuji Komalan

Pythonの「type」関数を使用することもできます。リストを反復するとき、アイテムがリストであるかどうかをチェックします。そうでない場合は「追加」し、そうでない場合は「拡張」します。サンプルコードは次のとおりです-

l=[1,2,[3,4],5,[6,7,8]]
x=[]
for i in l:
    if type(i) is list:
        x.extend(i)
    else:
        x.append(i)
print x

出力:

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

Append()およびextend()の詳細については、このWebサイトを確認してください。 https://docs.python.org/2/tutorial/datastructures.html

0
noobcoder

diligar's answer と同様

weird_list=[[1, 2, 3], [4, 5, 6], [7], [8, 9]]
Nice_list = list(map(int, ''.join([e for e in str(weird_list) if e not in '[ ]']).split(',')))
0

前の回答 から、この関数は考えられるほとんどの場合を平坦化します。これはpython 2.3まで機能すると信じています。

def flatten(item, keepcls=(), keepobj=()):
    if not hasattr(item, '__iter__') or isinstance(item, keepcls) or item in keepobj:
        yield item
    else:
        for i in item:
            for j in flatten(i, keepcls, keepobj + (item,)):
                yield j

円形リスト

>>> list(flatten([1, 2, [...], 3]))
[1, 2, [1, 2, [...], 3], 3]

深さ優先リスト

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

ネストされた繰り返しリスト

>>> list(flatten([[1,2],[1,[1,2]],[1,2]]))
[1, 2, 1, 1, 2, 1, 2]

dictsを含むリスト(またはフラット化しない他のオブジェクト)

>>> list(flatten([1,2, {'a':1, 'b':2}, 'text'], keepcls=(dict, str)))
[1, 2, {'a': 1, 'b': 2}, 'text']

任意のイテラブル

>>> list(flatten((x for x in [1,2, set([3,(4,5),6])])))
[1, 2, 4, 5, 3, 6]

keepclsにいくつかのデフォルトクラスを保持して、関数の呼び出しをより簡潔にすることもできます。

0
wihlke

再帰が好きなら、これはあなたにとって興味深い解決策かもしれません:

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

私は実際に、しばらく前に書いたいくつかの練習用Schemeコードをこれに適合させました。

楽しい!

0
inspectorG4dget

別の質問 に対する私自身の答えから恥知らずに取られました。

この機能

  • isinstanceを使用しません。これは悪であり、アヒルのタイピングを中断するためです。
  • reduceを再帰的に使用します。 reduceを使用した回答が必要です。
  • 要素がネストされたリスト、またはネストされていない原子のリスト、または原子(再帰制限の対象)である任意のネストされたリストで動作します。
  • LBYLしません。
  • ただし、文字列をアトムとして含むネストされたリストではありません。

以下のコード:

def flattener(left, right):
    try:
        res = reduce(flattener, right, left)
    except TypeError:
        left.append(right)
        res = left
    return res


def flatten(seq):
    return reduce(flattener, seq, [])


>>> nested_list = [0, [1], [[[[2]]]],
                   [3, [], [4, 5]],
                   [6, [7, 8],
                    9, [[[]], 10,
                        []]],
                   11, [], [],
                   [12]]
>>> flatten(nested_list)
[0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12]
0
Cong Ma