Làm phẳng một danh sách không thường xuyên của danh sách


440

Vâng, tôi biết chủ đề này đã được đề cập trước đó ( ở đây , ở đây , ở đây , ở đây ), nhưng như xa như tôi biết, tất cả các giải pháp, ngoại trừ một, thất bại trên một danh sách như thế này:

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

Trường hợp đầu ra mong muốn là

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

Hoặc có lẽ thậm chí tốt hơn, một iterator. Giải pháp duy nhất tôi thấy rằng hoạt động cho một lồng tùy ý được tìm thấy trong câu hỏi này :

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)

Đây có phải là mô hình tốt nhất? Tôi đã bỏ qua một cái gì đó? Có vấn đề gì không?


16
Thực tế là có rất nhiều câu trả lời và rất nhiều hành động cho câu hỏi này thực sự gợi ý rằng đây nên là một chức năng tích hợp ở đâu đó, phải không? Nó đặc biệt quá tệ với trình biên
dịch.ast

3
Tôi sẽ nói rằng những gì Python thực sự cần là đệ quy không bị phá vỡ hơn là một nội dung khác.
đất sét

2
@Mittenchops: hoàn toàn không đồng ý, thực tế là những người làm việc với các API rõ ràng xấu / cấu trúc dữ liệu quá phức tạp (chỉ là một lưu ý: listcó ý định đồng nhất) không có nghĩa đó là lỗi của Python và chúng tôi cần tích hợp sẵn cho nhiệm vụ đó
Azat Ibrakov

1
Nếu bạn có thể đủ khả năng thêm một gói vào dự án của mình - tôi cho rằng giải pháp more_itertools.collapse sẽ làm điều đó tốt nhất. Từ câu trả lời này: stackoverflow.com/a/40938883/3844376
viddik13

Câu trả lời:


382

Sử dụng các hàm tạo có thể làm cho ví dụ của bạn dễ đọc hơn một chút và có thể tăng hiệu suất.

Con trăn 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

Tôi đã sử dụng ABC lặp được thêm vào 2.6.

Con trăn 3

Trong Python 3, basestringkhông còn nữa, nhưng bạn có thể sử dụng một tuple strbytes để có được hiệu ứng tương tự ở đó.

Các yield fromnhà điều hành trả về một mục từ một máy phát điện tại một thời điểm. Cú pháp này để ủy quyền cho một trình phát phụ đã được thêm vào trong 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

6
Trong tất cả các đề xuất trên trang này, đây là người duy nhất san phẳng danh sách này l = ([[chr(i),chr(i-32)] for i in xrange(ord('a'), ord('z')+1)] + range(0,9))trong tích tắc khi tôi thực hiện điều này list(flatten(l)). Tất cả những người khác, sẽ bắt đầu làm việc và mất mãi mãi!
nemeisfixx

7
Điều này cũng làm phẳng từ điển. Có lẽ bạn muốn sử dụng collections.Sequencethay vì collections.Iteratable?
josch

1
Điều này không hoạt động với những thứ không được liệt kê ban đầu, ví dụ for i in flatten(42): print (i). Điều này có thể được khắc phục bằng cách di chuyển isinstancemệnh đề -test và mệnh đề khác bên ngoài for el-loop. (Sau đó, bạn có thể ném bất cứ thứ gì vào nó, và nó sẽ tạo ra một danh sách phẳng từ nó)
RolKau

6
Đối với Python 3.7, việc sử dụng collections.Iterablebị phản đối. Sử dụng collections.abc.Iterablethay thế.
dawg

5
Thật vậy, đệ quy là không bao giờ cần thiết. Trong trường hợp cụ thể này, sử dụng đệ quy không phải là giải pháp tốt nhất vì nó sẽ bị sập trong các danh sách được lồng sâu (độ sâu> 1000). Nhưng nếu bạn không nhắm đến việc có một cái gì đó an toàn, thì có chức năng đệ quy sẽ tốt hơn vì chúng dễ đọc / ghi hơn.
cglacet

50

Giải pháp của tôi:

import collections


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

Một chút súc tích hơn, nhưng khá nhiều như nhau.


5
Bạn có thể làm điều này mà không cần nhập bất cứ thứ gì nếu bạn chỉ try: iter(x)kiểm tra xem nó có thể lặp lại hay không Nhưng tôi không nghĩ phải nhập mô-đun stdlib là một nhược điểm đáng tránh.
abarnert

8
Đáng lưu ý rằng giải pháp này chỉ hoạt động nếu tất cả các mục thuộc loạiint
alfasin 7/12/14

1
Có thể làm cho nó ngắn gọn hơn, def flatten(x): return [a for i in x for a in flatten(i)] if isinstance(x, collections.Iterable) else [x]- nhưng khả năng đọc có thể chủ quan ở đây.
Không có

4
điều này không hoạt động trên chuỗi vì chuỗi cũng lặp lại. Thay điều kiện bằngif isinstance(x, collections.Iterable) and not isinstance(x, basestring)
aandis

36

Trình tạo bằng cách sử dụng đệ quy và gõ vịt (cập nhật cho 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]

1
Cảm ơn, điều đó rất tốt cho Python 3. Đối với 2.x trước đó là cần thiết: for i in flatten(item): yield i
dansalmo

list (flatten ([['X'], 'Y'])) không thành công trên biến thể 2.X
sten

@ user1019129 xem nhận xét của tôi ở trên của bạn
dansalmo

vâng, nó thất bại với chu trình .. tôi nghĩ bởi vì một chuỗi cũng là một "mảng" -of-chars
sten

35

Phiên bản trình tạo của giải pháp không đệ quy của @ unutbu, theo yêu cầu của @Andrew trong một nhận xét:

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

Phiên bản đơn giản hóa của máy phát điện này:

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)

đó là một giao dịch đặt hàng trước của cây được hình thành bởi các danh sách lồng nhau. chỉ có lá được trả lại. Lưu ý rằng việc thực hiện này sẽ tiêu thụ cấu trúc dữ liệu gốc, tốt hơn hoặc xấu hơn. Có thể thú vị khi viết một cái vừa bảo tồn cây gốc, nhưng cũng không phải sao chép các mục trong danh sách.
Andrew Wagner

6
Tôi nghĩ rằng bạn cần kiểm tra các chuỗi - ví dụ: thêm "chứ không phải isinstance (l [0], basestring)" như trong giải pháp của Cristian. Nếu không, bạn nhận được một vòng lặp vô hạn xung quanh l [0: 1] = l [0]
c-nhím

Đây là một ví dụ tốt về việc tạo một trình tạo, nhưng như c-nhím đề cập, thuật toán tự nó thất bại khi chuỗi chứa chuỗi.
Daniel 'Dang' Griffith

28

Đây là phiên bản chức năng của flatten đệ quy xử lý cả bộ dữ liệu và danh sách, và cho phép bạn đưa vào bất kỳ kết hợp các đối số vị trí nào. Trả về một trình tạo tạo ra toàn bộ chuỗi theo thứ tự, arg by arg:

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

Sử dụng:

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
giải pháp tuyệt vời, tuy nhiên sẽ được nhiều hữu ích nếu bạn thêm một số bình luận để mô tả những gì e, a, ntham khảo
Kristof Pal

2
@WolfgangKuehne: Cố gắng argscho n, intermediate(hoặc ngắn hơn midhoặc bạn có thể thích element) cho aresultcho e, vì vậy:flatten = lambda *args: (result for mid in args for result in (flatten(*mid) if isinstance(mid, (tuple, list)) else (mid,)))
Tạm dừng cho đến khi có thông báo mới.

Điều này là nhanh hơn đáng kể so với compiler.ast.flatten. Mã lớn, nhỏ gọn, hoạt động cho bất kỳ loại đối tượng (tôi nghĩ).
bcdan

Wow đây sẽ là câu trả lời được bình chọn nhiều nhất và được chấp nhận ... hoạt động như một cơ duyên!
U10-Chuyển tiếp

27

Phiên bản này flattentránh giới hạn đệ quy của python (và do đó hoạt động với các iterables lồng nhau, sâu tùy ý). Nó là một trình tạo có thể xử lý các chuỗi và các vòng lặp tùy ý (thậm chí là vô hạn).

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

Dưới đây là một số ví dụ minh họa việc sử dụng nó:

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]

Mặc dù flattencó thể xử lý các trình tạo vô hạn, nhưng nó không thể xử lý lồng vô hạn:

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

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

1
có sự đồng thuận nào về việc nên sử dụng ABC Iterable hay ABC Sequence không?
wim

sets, dicts, deques, listiterators, generators, Filehandles, và các lớp học tùy chỉnh với __iter__định nghĩa là tất cả các trường hợp collections.Iterable, nhưng không phải collections.Sequence. Kết quả của việc làm phẳng a dictlà một chút iffy, nhưng nếu không, tôi nghĩ collections.Iterablelà một mặc định tốt hơn collections.Sequence. Đó chắc chắn là tự do hơn.
unutbu

@wim: Một vấn đề với việc sử dụng collections.Iterablelà điều này bao gồm các trình tạo vô hạn. Tôi đã thay đổi câu trả lời của tôi xử lý trường hợp này.
unutbu

1
Điều này dường như không hoạt động cho các ví dụ thứ 3 và thứ 4. Nó ném StopIteration. Ngoài ra, có vẻ như while True: first = next(remainder) có thể được thay thế bởi for first in remainder:.
Georgy

@Georgy điều này có thể được sửa chữa với việc đóng gói cơ thể của flatten trong a try-except StopIteration block.
baduker

12

Đây là một câu trả lời khác thậm chí còn thú vị hơn ...

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)

Về cơ bản, nó chuyển đổi danh sách lồng nhau thành một chuỗi, sử dụng biểu thức chính quy để loại bỏ cú pháp lồng nhau, sau đó chuyển đổi kết quả trở lại danh sách (làm phẳng).


Nếu bạn cố gắng khái quát hóa điều này thành một cái gì đó không phải là giá trị int, thì sẽ rất vui, ví dụ, [['C=64', 'APPLE ]['], ['Amiga', 'Mac', 'ST']]:) Mặt khác, đưa ra một danh sách có chứa chính nó, nó sẽ làm tốt hơn một chút so với các câu trả lời khác, đưa ra một ngoại lệ thay vì chỉ lặp đi lặp lại cho đến khi bạn hết bộ nhớ / đệ quy cho đến khi bạn cạn kiệt stack stack
abarnert

Dấu nhắc ban đầu là về việc làm phẳng một danh sách các số nguyên. Nếu bạn chỉ thay đổi mức độ hiểu danh sách thành d = [x cho x trong c] thì nó sẽ hoạt động tốt cho mẫu của bạn.
đất sét

Đầu tiên, [x for x in c]chỉ là một cách chậm chạp và dài dòng để tạo một bản sao c, vậy tại sao bạn lại làm vậy? Thứ hai, mã của bạn rõ ràng sẽ chuyển đổi 'APPLE ]['thành 'APPLE ', vì nó không xử lý trích dẫn, nó chỉ giả sử bất kỳ dấu ngoặc nào là dấu ngoặc danh sách.
abarnert

Hà! Cách bình luận của bạn được định dạng trên máy tính của tôi, tôi thậm chí không nhận ra đó là Apple II khi nó xuất hiện trên các máy tính cũ. Trong mọi trường hợp, câu trả lời của tôi cho cả hai câu hỏi của bạn là bài tập này - đối với tôi - chỉ đơn thuần là một thử nghiệm để tìm ra giải pháp sáng tạo để làm phẳng danh sách. Tôi không chắc chắn tôi sẽ khái quát nó để làm phẳng mọi danh sách ngoài kia.
đất sét

Bạn chỉ cần arr_str = str(arr)và sau đó [int(s) for s in re.findall(r'\d+', arr_str)]thực sự. Xem github.com/jorgeorpinel/flatten_nested_lists/blob/master/iêu
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

Bạn có thể sử dụng deepflattentừ gói bên thứ 3 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]

Đó là một trình vòng lặp, do đó bạn cần lặp lại nó (ví dụ bằng cách gói nó listhoặc sử dụng nó trong một vòng lặp). Trong nội bộ, nó sử dụng một cách tiếp cận lặp lại thay vì một cách tiếp cận đệ quy và nó được viết dưới dạng phần mở rộng C để có thể nhanh hơn các cách tiếp cận python thuần túy:

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

Tôi là tác giả của iteration_utilitiesthư viện.


7

Thật thú vị khi cố gắng tạo ra một hàm có thể làm phẳng danh sách không đều trong Python, nhưng tất nhiên đó là những gì Python dành cho (để làm cho việc lập trình trở nên thú vị). Trình tạo sau hoạt động khá tốt với một số cảnh báo:

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

Nó sẽ san bằng kiểu dữ liệu mà bạn có thể muốn bỏ lại một mình (như bytearray, bytesstrđối tượng). Ngoài ra, mã dựa trên thực tế là yêu cầu một trình vòng lặp từ một lần lặp không tăng lên 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]
>>>

Biên tập:

Tôi không đồng ý với việc thực hiện trước đó. Vấn đề là bạn không thể làm phẳng thứ gì đó không lặp lại được. Nó khó hiểu và gây ấn tượng sai về lập luận.

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

Trình tạo sau gần giống như trình tạo đầu tiên nhưng không gặp vấn đề gì khi cố gắng làm phẳng một đối tượng không thể lặp lại. Nó thất bại như người ta mong đợi khi một đối số không phù hợp được đưa ra cho nó.

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

Kiểm tra trình tạo hoạt động tốt với danh sách được cung cấp. Tuy nhiên, mã mới sẽ đưa ra TypeErrorkhi một đối tượng không thể lặp được đưa cho nó. Ví dụ được hiển thị dưới đây của hành vi mới.

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

Mặc dù một câu trả lời tao nhã và rất pythonic đã được chọn, tôi sẽ trình bày giải pháp của mình chỉ để xem xét:

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

Xin cho biết mã này tốt hay xấu như thế nào?


1
Sử dụng isinstance(i, (tuple, list)). Khởi tạo các biến rỗng là một lá cờ để tôi tìm kiếm các cấu trúc mã thay thế, điển hình là hiểu, tạo, đệ quy, v.v.
dansalmo

3
return type(l)(ret)cũng sẽ giúp bạn trở lại cùng loại container như đã được thông qua. :)
dash-tom-bang

@ dash-tom-bang Bạn có thể vui lòng giải thích ý nghĩa của nó trong một chi tiết nhỏ.
Xolve

1
Nếu bạn vượt qua trong một danh sách, bạn có thể muốn một danh sách trở lại. Nếu bạn vượt qua trong một tuple, bạn có thể muốn một tuple trở lại. Nếu bạn vượt qua sự nhầm lẫn của cả hai, bạn sẽ nhận được bất cứ thứ gì kèm theo bên ngoài.
dash-tom-bang

4

Tôi thích câu trả lời đơn giản. Không có máy phát điện. Không có giới hạn đệ quy hoặc đệ quy. Chỉ cần lặp lại:

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

Điều này hoạt động với hai danh sách: vòng lặp for bên trong và vòng lặp while.

Vòng lặp bên trong lặp lại thông qua danh sách. Nếu nó tìm thấy một phần tử danh sách, nó (1) sử dụng list.extend () để làm phẳng phần đó một cấp độ lồng và (2) chuyển giữ KeepChecking thành True. keepchecking được sử dụng để kiểm soát vòng lặp while. Nếu vòng lặp bên ngoài được đặt thành true, nó sẽ kích hoạt vòng lặp bên trong cho một lượt đi khác.

Những lượt đi tiếp tục xảy ra cho đến khi không tìm thấy danh sách lồng nhau. Khi vượt qua cuối cùng xảy ra ở nơi không tìm thấy, keepChecking không bao giờ bị vấp thành true, điều đó có nghĩa là listIsNested vẫn sai và vòng lặp bên ngoài thoát ra.

Danh sách làm phẳng sau đó được trả lại.

Chạy thử nghiệm

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

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


Tôi cũng thích đơn giản. Trong trường hợp này, bạn lặp đi lặp lại danh sách nhiều lần như có các tổ hoặc cấp độ. Có thể nhận được đắt tiền.
Telliott99

@ Telliott99: Bạn đúng nếu danh sách của bạn thực sự lớn và / hoặc được lồng vào độ sâu lớn. Tuy nhiên, nếu đó không phải là trường hợp, thì giải pháp đơn giản cũng hoạt động tốt, và không có phép thuật sâu sắc của một số câu trả lời khác. Có một nơi để hiểu các trình tạo đệ quy nhiều giai đoạn, nhưng tôi không tin rằng đó phải là nơi bạn nhìn đầu tiên. (Tôi đoán bạn biết tôi rơi vào đâu trong cuộc tranh luận "Tệ hơn là tốt hơn".)
đất sét

@ Telliott99: Hoặc nói theo cách khác, bạn sẽ không phải "thử Grok" giải pháp của tôi. Nếu hiệu suất không phải là nút cổ chai, điều gì quan trọng nhất với bạn khi là lập trình viên?
đất sét

Các giải pháp đơn giản có ít logic hơn. Recursion là một cấu trúc lập trình khá cơ bản mà bất cứ ai coi mình là lập trình viên nên hoàn toàn thoải mái. Các trình tạo rất nhiều theo cách của Python và (cùng với sự hiểu biết) là điều mà bất kỳ lập trình viên Python chuyên nghiệp nào cũng phải mò mẫm ngay lập tức.
dash-tom-bang

1
Tôi đồng ý về đệ quy. Khi tôi viết câu trả lời của mình, trăn vẫn phá vỡ đệ quy ở 1000 chu kỳ. Họ đã thay đổi điều này? Đối với việc là một lập trình viên python chuyên nghiệp, tôi thì không. Hơn nữa, tôi tưởng tượng nhiều người lập trình trong python không làm như vậy toàn thời gian.
đất sét

4

Đây là một chức năng đơn giản làm phẳng danh sách độ sâu tùy ý. Không có đệ quy, để tránh chồng tràn.

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

Đúng! Rất giống với mã của tôi tại github.com/jorgeorpinel/flatten_nested_lists/blob/master/
mẹo

3

Tôi ngạc nhiên không ai nghĩ về điều này. Đệ quy chết tiệt Tôi không nhận được câu trả lời đệ quy mà những người tiên tiến ở đây đã đưa ra. dù sao đây là nỗ lực của tôi về điều này. hãy cẩn thận với trường hợp sử dụng của 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)

đầu ra:

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

3

Tôi đã không đi qua tất cả các câu trả lời đã có sẵn ở đây, nhưng đây là một câu hỏi tôi đã nghĩ ra, mượn từ cách xử lý danh sách đầu tiên và nghỉ ngơi của lisp

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

đây là một trường hợp đơn giản và không đơn giản -

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

Nó không phải là một lớp lót. Cho dù bạn cố gắng lắp nó thành bao nhiêu, thì đó def foo():là một dòng riêng biệt. Ngoài ra, điều này là rất khó đọc.
cs95

Tôi đã hủy mã một dòng, và thực hiện tái cấu trúc thêm. (chỉnh sửa đang chờ đánh giá ngang hàng khi tôi viết bài này) Phương pháp đặc biệt này có vẻ rất dễ đọc đối với tôi, mặc dù mã ban đầu cần một số cấu trúc lại.
Emilio M Bumachar

3

Khi cố gắng trả lời một câu hỏi như vậy, bạn thực sự cần đưa ra những hạn chế của mã mà bạn đề xuất như một giải pháp. Nếu đó chỉ là về màn trình diễn thì tôi sẽ không bận tâm lắm, nhưng hầu hết các mã được đề xuất là giải pháp (bao gồm câu trả lời được chấp nhận) không thể làm phẳng bất kỳ danh sách nào có độ sâu lớn hơn 1000.

Khi tôi nói hầu hết các mã tôi có nghĩa là tất cả các mã sử dụng bất kỳ hình thức đệ quy nào (hoặc gọi một hàm thư viện chuẩn là đệ quy). Tất cả các mã này đều thất bại vì với mỗi cuộc gọi đệ quy được thực hiện, ngăn xếp (cuộc gọi) tăng thêm một đơn vị và ngăn xếp cuộc gọi python (mặc định) có kích thước 1000.

Nếu bạn không quá quen thuộc với ngăn xếp cuộc gọi, thì có thể những điều sau đây sẽ có ích (nếu không bạn chỉ có thể cuộn đến Thực hiện ).

Kích thước ngăn xếp cuộc gọi và lập trình đệ quy (tương tự dungeon)

Tìm kho báu và thoát

Hãy tưởng tượng bạn bước vào một hầm ngục khổng lồ với các phòng được đánh số , tìm kiếm một kho báu. Bạn không biết địa điểm nhưng bạn có một số chỉ dẫn về cách tìm kho báu. Mỗi dấu hiệu là một câu đố (độ khó khác nhau, nhưng bạn không thể dự đoán mức độ khó của chúng). Bạn quyết định suy nghĩ một chút về một chiến lược để tiết kiệm thời gian, bạn thực hiện hai quan sát:

  1. Thật khó (lâu) để tìm kho báu vì bạn sẽ phải giải những câu đố (có khả năng khó) để đến đó.
  2. Khi kho báu được tìm thấy, quay trở lại lối vào có thể dễ dàng, bạn chỉ cần sử dụng cùng một đường dẫn theo hướng khác (mặc dù điều này cần một chút bộ nhớ để nhớ lại đường dẫn của bạn).

Khi vào ngục tối, bạn nhận thấy một cuốn sổ nhỏ ở đây. Bạn quyết định sử dụng nó để ghi lại mọi phòng bạn thoát sau khi giải câu đố (khi vào phòng mới), bằng cách này bạn sẽ có thể quay lại lối vào. Đó là một ý tưởng thiên tài, bạn thậm chí sẽ không chi một xu thực hiện chiến lược của mình.

Bạn vào ngục tối, giải quyết thành công 1001 câu đố đầu tiên, nhưng ở đây có một điều bạn chưa lên kế hoạch, bạn không còn chỗ trống trong cuốn sổ bạn đã mượn. Bạn quyết định từ bỏ nhiệm vụ của mình vì bạn không muốn có kho báu hơn là bị mất mãi mãi bên trong ngục tối (điều đó có vẻ thông minh thực sự).

Thực hiện một chương trình đệ quy

Về cơ bản, đó chính xác là điều tương tự như tìm kho báu. Hầm ngục là bộ nhớ của máy tính , mục tiêu của bạn bây giờ không phải là tìm kho báu mà là tính toán một số chức năng (tìm f (x) cho một x cho trước ). Các chỉ dẫn đơn giản là các thường trình con sẽ giúp bạn giải quyết f (x) . Chiến lược của bạn giống như chiến lược ngăn xếp cuộc gọi , sổ ghi chép là ngăn xếp, các phòng là địa chỉ trả về của các chức năng:

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

Vấn đề bạn gặp phải trong ngục tối sẽ giống nhau ở đây, ngăn xếp cuộc gọi có kích thước hữu hạn (ở đây 1000) và do đó, nếu bạn nhập quá nhiều chức năng mà không quay lại thì bạn sẽ điền vào ngăn xếp cuộc gọi và gặp lỗi như "Thưa nhà thám hiểm, tôi rất xin lỗi nhưng sổ ghi chép của bạn đã đầy" : RecursionError: maximum recursion depth exceeded. Lưu ý rằng bạn không cần đệ quy để điền vào ngăn xếp cuộc gọi, nhưng rất có thể chương trình không đệ quy gọi 1000 hàm mà không bao giờ quay lại. Điều quan trọng là phải hiểu rằng một khi bạn quay trở lại từ một hàm, ngăn xếp cuộc gọi sẽ được giải phóng khỏi địa chỉ được sử dụng (do đó tên "stack", địa chỉ trả về được đẩy vào trước khi nhập hàm và rút ra khi quay lại). Trong trường hợp đặc biệt của đệ quy đơn giản (một hàmftự gọi nó một lần - lặp đi lặp lại -) bạn sẽ nhập đi flặp lại cho đến khi tính toán kết thúc (cho đến khi tìm thấy kho báu) và trở về fcho đến khi bạn quay lại nơi bạn đã gọi fở nơi đầu tiên. Ngăn xếp cuộc gọi sẽ không bao giờ được giải phóng khỏi bất cứ điều gì cho đến khi kết thúc, nơi nó sẽ được giải phóng khỏi tất cả các địa chỉ trả lại lần lượt.

Làm thế nào để tránh vấn đề này?

Điều đó thực sự khá đơn giản: "không sử dụng đệ quy nếu bạn không biết nó có thể đi sâu đến đâu". Điều đó không phải lúc nào cũng đúng như trong một số trường hợp, đệ quy Đuôi cuộc gọi có thể được tối ưu hóa (TCO) . Nhưng trong python, đây không phải là trường hợp và thậm chí chức năng đệ quy "được viết tốt" sẽ không tối ưu hóa việc sử dụng ngăn xếp. Có một bài viết thú vị từ Guido về câu hỏi này: Loại bỏ đệ quy đuôi .

Có một kỹ thuật mà bạn có thể sử dụng để thực hiện bất kỳ chức năng đệ quy nào, đó là kỹ thuật mà chúng tôi có thể gọi là mang sổ ghi chép của riêng bạn . Ví dụ, trong trường hợp cụ thể của chúng tôi, chúng tôi chỉ đơn giản là đang khám phá một danh sách, vào một phòng tương đương với việc vào danh sách phụ, câu hỏi bạn nên tự hỏi là làm thế nào tôi có thể quay lại từ danh sách vào danh sách phụ huynh? Câu trả lời không phức tạp, lặp lại như sau cho đến khi stacktrống:

  1. đẩy danh sách hiện tại addressindextrong stackkhi nhập danh sách con mới (lưu ý rằng địa chỉ danh sách + chỉ mục cũng là một địa chỉ, do đó chúng tôi chỉ sử dụng chính xác kỹ thuật tương tự được sử dụng bởi ngăn xếp cuộc gọi);
  2. mỗi khi một mục được tìm thấy, yieldnó (hoặc thêm chúng vào danh sách);
  3. khi danh sách được khám phá đầy đủ, hãy quay lại danh sách cha bằng cách sử dụng stack trả về address(và index) .

Cũng lưu ý rằng điều này tương đương với một DFS trong cây trong đó một số nút là danh sách con A = [1, 2]và một số là các mục đơn giản: 0, 1, 2, 3, 4(for L = [0, [1,2], 3, 4]). Cây trông như thế này:

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

Thứ tự đặt trước truyền tải DFS là: L, 0, A, 1, 2, 3, 4. Hãy nhớ rằng, để thực hiện một DFS lặp, bạn cũng "cần" một ngăn xếp. Việc triển khai tôi đã đề xuất trước khi có các trạng thái sau (đối với stackflat_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]

Trong ví dụ này, kích thước tối đa của ngăn xếp là 2, vì danh sách đầu vào (và do đó là cây) có độ sâu 2.

Thực hiện

Để thực hiện, trong python bạn có thể đơn giản hóa một chút bằng cách sử dụng các trình vòng lặp thay vì các danh sách đơn giản. Các tham chiếu đến các trình vòng lặp (phụ) sẽ được sử dụng để lưu trữ các địa chỉ trả về danh sách phụ (thay vì có cả địa chỉ danh sách và chỉ mục). Đây không phải là một sự khác biệt lớn nhưng tôi cảm thấy điều này dễ đọc hơn (và cũng nhanh hơn một chút):

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)

Ngoài ra, lưu ý rằng trong is_list_liketôi có isinstance(item, list), có thể thay đổi để xử lý nhiều loại đầu vào hơn, ở đây tôi chỉ muốn có phiên bản đơn giản nhất trong đó (lặp lại) chỉ là một danh sách. Nhưng bạn cũng có thể làm điều đó:

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

Điều này coi các chuỗi là "các mục đơn giản" và do đó flatten_iter([["test", "a"], "b])sẽ trả về ["test", "a", "b"]và không ["t", "e", "s", "t", "a", "b"]. Lưu ý rằng trong trường hợp đó, iter(item)được gọi hai lần cho mỗi mục, hãy giả vờ đó là một bài tập cho người đọc để làm cho điều này sạch hơn.

Kiểm tra và nhận xét về các triển khai khác

Cuối cùng, hãy nhớ rằng bạn không thể in một danh sách lồng nhau vô hạn Lbằng cách sử dụng print(L)vì bên trong nó sẽ sử dụng các lệnh gọi đệ quy đến __repr__( RecursionError: maximum recursion depth exceeded while getting the repr of an object). Vì lý do tương tự, các giải pháp flattenliên quan strsẽ thất bại với cùng một thông báo lỗi.

Nếu bạn cần kiểm tra giải pháp của mình, bạn có thể sử dụng chức năng này để tạo danh sách lồng đơn giản:

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

Mà cho: build_deep_list(5)>>> [4, [3, [2, [1, [0]]]]].


2

Đây là cách compiler.ast.flattenthực hiện trong 2.7.5:

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

Có nhiều phương pháp tốt hơn, nhanh hơn (Nếu bạn đã đến đây, bạn đã thấy chúng rồi)

Cũng lưu ý:

Không dùng nữa kể từ phiên bản 2.6: Gói trình biên dịch đã bị xóa trong Python 3.


2

hoàn toàn hack nhưng tôi nghĩ nó sẽ hoạt động (tùy thuộc vào data_type của bạn)

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

2

Chỉ cần sử dụng một funcythư việ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: nó sử dụng giải pháp đệ quy: liên kết đến nguồn
Georgy

1

Đây là một cách tiếp cận py2 khác, tôi không chắc nó nhanh nhất hay thanh lịch nhất hay an toàn nhất ...

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

Nó có thể bỏ qua bất kỳ loại cụ thể (hoặc dẫn xuất) nào bạn muốn, nó trả về một trình vòng lặp, do đó bạn có thể chuyển đổi nó sang bất kỳ vùng chứa cụ thể nào như danh sách, tuple, dict hoặc đơn giản là sử dụng nó để giảm dung lượng bộ nhớ, tốt hơn hoặc xấu hơn nó có thể xử lý các đối tượng không thể lặp ban đầu như int ...

Lưu ý rằng hầu hết các công việc nặng được thực hiện trong C, vì theo như tôi biết đó là cách thức thực hiện itertools, vì vậy trong khi nó được đệ quy, AFAIK không bị giới hạn bởi độ sâu đệ quy python vì các lệnh gọi hàm đang diễn ra trong C, mặc dù điều này không có nghĩa là bạn bị giới hạn bởi bộ nhớ, đặc biệt là trong OS X nơi kích thước ngăn xếp của nó có giới hạn cứng như ngày nay (OS X Mavericks) ...

có một cách tiếp cận nhanh hơn một chút, nhưng phương pháp ít di động hơn, chỉ sử dụng nó nếu bạn có thể giả định rằng các yếu tố cơ bản của đầu vào có thể được xác định rõ ràng nếu không, bạn sẽ nhận được đệ quy vô hạn và OS X với kích thước ngăn xếp giới hạn của nó, sẽ ném một lỗi phân khúc khá nhanh ...

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

Ở đây chúng tôi đang sử dụng các bộ để kiểm tra loại nên phải mất O (1) so với O (số loại) để kiểm tra xem có nên bỏ qua một phần tử hay không, mặc dù tất nhiên mọi giá trị với loại dẫn xuất của loại bỏ qua đã nêu sẽ thất bại , đây là lý do tại sao nó sử dụng str, unicodevì vậy hãy sử dụng nó một cách thận trọng ...

kiểm tra:

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

Không sử dụng bất kỳ thư viện:

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

Sử dụng 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

Hoặc không có chuỗi:

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

Tôi đã sử dụng đệ quy để giải quyết danh sách lồng nhau với bất kỳ độ sâu nào

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

Vì vậy, sau khi tôi xác định hàm Combo_nlist, thật dễ dàng để sử dụng chức năng này để vỗ. Hoặc bạn có thể kết hợp nó thành một chức năng. Tôi thích giải pháp của tôi vì nó có thể được áp dụng cho bất kỳ danh sách lồng nhau nào.

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

kết quả

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]

"Danh sách lồng nhau với bất kỳ độ sâu" không đúng. Chỉ cần thử bạn sẽ thấy: current_value = combiner(current_value,each_item) RecursionError: maximum recursion depth exceeded
cglacet

hmmm Tôi đang cố gắng làm phẳng danh sách với hơn 1000 lớp?
Oldyoung

Tất nhiên, đó là toàn bộ điểm của cuộc thảo luận về các giải pháp đệ quy và lặp lại. Nếu bạn biết trước rằng số lớp là <hơn 1000 thì giải pháp đơn giản nhất sẽ hoạt động. Khi bạn nói "bất kỳ độ sâu" nào, điều này bao gồm danh sách có độ sâu> 1000.
cglacet

1

Cách dễ nhất là sử dụng thư viện hình thái bằng cách sử dụng pip install morph.

Mã này là:

import morph

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

1

Tôi biết rằng đã có nhiều câu trả lời tuyệt vời nhưng tôi muốn thêm một câu trả lời sử dụng phương pháp lập trình chức năng để giải quyết câu hỏi. Trong câu trả lời này, tôi sử dụng đệ quy kép:

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

đầu ra:

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

1

Tôi không chắc điều này có nhất thiết phải nhanh hơn hay hiệu quả hơn không, nhưng đây là những gì tôi làm:

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

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

Các flattenchức năng ở đây biến danh sách vào một chuỗi, lấy ra tất cả các dấu ngoặc vuông, gắn dấu ngoặc vuông trở lại vào đầu, và biến nó trở lại vào một danh sách.

Mặc dù, nếu bạn biết bạn sẽ có dấu ngoặc vuông trong danh sách của mình theo chuỗi, như [[1, 2], "[3, 4] and [5]"], bạn sẽ phải làm một cái gì đó khác.


Điều này không có lợi thế so với giải pháp đơn giản vì điều này không xử lý được danh sách sâu, tức là "RecursionError: vượt quá độ sâu đệ quy tối đa trong khi nhận được repr của một đối tượng".
cglacet

1

Đây là một thực hiện đơn giản của flatten trên python2

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

Điều này sẽ làm phẳng một danh sách hoặc từ điển (hoặc danh sách các danh sách hoặc từ điển của từ điển, vv). Nó giả định rằng các giá trị là các chuỗi và nó tạo ra một chuỗi nối từng mục với một đối số dấu phân cách. Nếu bạn muốn, bạn có thể sử dụng dấu phân cách để phân chia kết quả thành một đối tượng danh sách sau đó. Nó sử dụng đệ quy nếu giá trị tiếp theo là danh sách hoặc chuỗi. Sử dụng đối số khóa để cho biết bạn muốn các khóa hoặc các giá trị (đặt khóa thành sai) từ đối tượng từ điể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=', ')

sản lượng:

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

0

Nếu bạn thích đệ quy, đây có thể là một giải pháp mà bạn quan tâm:

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

Tôi thực sự đã điều chỉnh nó từ một số mã Scheme thực hành mà tôi đã viết cách đây một thời gian.

Thưởng thức!


0

Tôi mới biết về python và đến từ một nền tảng tốt. Đây là những gì tôi đã đưa ra (kiểm tra tên var cho lulz):

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]

Có vẻ để làm việc. Kiểm tra:

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

trả về:

[1, 2, 3, 4, 5, 6, 7, 8, 1, 2]
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.