سؤال

أتساءل ما إذا كان هناك اختصار لإجراء قائمة بسيطة من قائمة القوائم في بيثون.

يمكنني أن أفعل ذلك في for حلقة، ولكن ربما هناك بعض "بطانة واحدة" باردة؟ حاولت ذلك خفض, ، لكنني أحصل على خطأ.

رمز

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

رسالة خطأ

Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
  File "<stdin>", line 1, in <lambda>
AttributeError: 'NoneType' object has no attribute 'extend'
هل كانت مفيدة؟

المحلول

إعطاء قائمة القوائم l,

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

مما يعني:

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

أسرع من الاختصارات المنشورة حتى الآن. فيl هي القائمة تتسطح.)

هنا هي الوظيفة المقابلة:

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

كدليل، يمكنك استخدام timeit الوحدة في المكتبة القياسية:

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

Explanation: الاختصارات القائمة على + (بما في ذلك الاستخدام الضمني في sum)، من الضرورة، O(L**2) عندما يكون هناك سيكرسون - نظرا لأن قائمة النتيجة الوسيطة تستمر في الحصول على وقت أطول، في كل خطوة يتم تخصيص كائن قائمة نتائج ناتجة وسيطة جديدة، ويجب نسخ جميع العناصر الموجودة في النتيجة المتوسطة السابقة أكثر (بالإضافة إلى عدد قليل منها في نهايةالمطاف). لذلك، بالنسبة للبساطة وبدون فقدان فعالا للعمومية، أقول إن لديك SABILISE LOCE ODED I PODENS لكل منها: يتم نسخ العناصر الأولى الأولى والأعلى L-1 مرات، والثاني البنود L-2 مرات، وهلم جرا؛ إجمالي عدد النسخ هو أوقات X من X من 1 إلى L المستثناة، أي I * (L**2)/2.

يقوم الفهم القائمة فقط بإنشاء قائمة واحدة، مرة واحدة، ونسخ كل عنصر (من مكان إقامته الأصلية إلى قائمة النتائج) أيضا مرة واحدة بالضبط.

نصائح أخرى

يمكنك استخدام itertools.chain():

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

أو، على Python> = 2.6، استخدم itertools.chain.from_iterable() الذي لا يتطلب تفريغ القائمة:

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

هذا النهج هو أكثر قابلية للقراءة أكثر من [item for sublist in l for item in sublist] ويبدو أنه أسرع أيضا:

[me@home]$ python -mtimeit -s'l=[[1,2,3],[4,5,6], [7], [8,9]]*99;import itertools' 'list(itertools.chain.from_iterable(l))'
10000 loops, best of 3: 24.2 usec per loop
[me@home]$ python -mtimeit -s'l=[[1,2,3],[4,5,6], [7], [8,9]]*99' '[item for sublist in l for item in sublist]'
10000 loops, best of 3: 45.2 usec per loop
[me@home]$ python -mtimeit -s'l=[[1,2,3],[4,5,6], [7], [8,9]]*99' 'sum(l, [])'
1000 loops, best of 3: 488 usec per loop
[me@home]$ python -mtimeit -s'l=[[1,2,3],[4,5,6], [7], [8,9]]*99' 'reduce(lambda x,y: x+y,l)'
1000 loops, best of 3: 522 usec per loop
[me@home]$ python --version
Python 2.7.3

ملاحظة من المؤلف: هذا غير فعال. ولكن متعة، ل moniids رائع. إنه غير مناسب لإنتاج رمز Python.

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

هذا يلخص فقط عناصر التكرار التي تم تمريرها في الحجة الأولى، وتعامل مع الوسيطة الثانية باعتبارها القيمة الأولية للمبلغ (إن لم يكن قد تعطى، 0 يستخدم بدلا من ذلك، وسوف تعطيك هذه الحالة خطأ).

لأنك تلخيص قوائم متداخلة، تحصل فعلا [1,3]+[2,4] نتيجة ل sum([[1,3],[2,4]],[]), ، وهو ما يساوي [1,3,2,4].

لاحظ أن يعمل فقط في قوائم القوائم. لقوائم قوائم القوائم، ستحتاج إلى حل آخر.

اختبرت معظم الحلول المقترحة مع الفتحة (مشروع حيوانات أليفة من الألغام، أساسا غلاف حولها timeit)، ووجد

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

أن تكون أسرع حلا. فيoperator.iadd سريع بنفس القدر.)

enter image description here


رمز لإعادة إنتاج المؤامرة:

import functools
import itertools
import numpy
import operator
import perfplot


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


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


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


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


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


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


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


perfplot.show(
    setup=lambda n: [list(range(10))] * n,
    kernels=[
        forfor, sum_brackets, functools_reduce, functools_reduce_iconcat,
        itertools_chain, numpy_flat, numpy_concatenate
        ],
    n_range=[2**k for k in range(16)],
    logx=True,
    logy=True,
    xlabel='num lists'
    )
from functools import reduce #python 3

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

ال extend() الطريقة في المثال الخاص بك يعدل x بدلا من إرجاع قيمة مفيدة (والتي reduce() تتوقع).

طريقة أسرع للقيام reduce الإصدار سيكون

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

هنا نهج عام ينطبق عليه أعداد, سلاسل, متداخل قوائم و مختلط حاويات.

رمز

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


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

ملاحظات:

التجريبي

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

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

مرجع

  • تم تعديل هذا الحل من وصفة في بيزلي، دال و ب. جونز. وصفة 4.14، بيثون كوكبوك 3rd ed.، O'Reilly Media Inc. Sebastopol، CA: 2013.
  • وجدت سابقا لذلك ما بعد, ربما المظاهرة الأصلية.

إذا كنت ترغب في تسوية بنية بيانات حيث لا تعرف مدى عمقها، فيمكنك استخدامها iteration_utilities.deepflatten1

>>> from iteration_utilities import deepflatten

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

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

إنه مولد لذلك تحتاج إلى إلقاء النتيجة إلى list أو تكرر صراحة فوقه.


لتسطيح مستوى واحد فقط، وإذا كان كل عنصر من العناصر هو نفسه قاتلة يمكنك أيضا استخدامها iteration_utilities.flatten والذي نفسه مجرد غلاف رفيع حولها itertools.chain.from_iterable:

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

فقط لإضافة بعض توقيت (بناء على إجابة Nico Schlömer التي لم تتضمن الوظيفة المقدمة في هذه الإجابة):

enter image description here

إنه مؤامرة سجل تسجيل الدخول لاستيعاب مجموعة كبيرة من القيم الممزدة. من أجل التفكير النوعي: أقل هو أفضل.

تظهر النتائج أنه إذا كان التكرار يحتوي فقط على عدد قليل من المؤخرات الداخلية بعد ذلك sum سوف تكون أسرع، ولكن لمقابلات التوالي فقط itertools.chain.from_iterable, iteration_utilities.deepflatten أو الفهم المتداخل له أداء معقول مع itertools.chain.from_iterable كونها أسرع (كما لاحظت بالفعل من قبل Nico Schlömer).

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

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

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

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

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

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

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

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

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


from simple_benchmark import benchmark

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

b.plot()

1 إخلاء المسئولية: أنا مؤلف تلك المكتبة

آخذ بياني مرة أخرى. المبلغ ليس الفائز. على الرغم من أنه أسرع عندما تكون القائمة صغيرة. لكن الأداء يتحلل بشكل كبير مع قوائم أكبر.

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

لا يزال الإصدار المبلغ يعمل لأكثر من دقيقة ولم يتم ذلك معالجة بعد!

للقوائم المتوسطة:

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

باستخدام قوائم صغيرة ومزينة: رقم = 1000000

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

يبدو أن هناك ارتباك مع operator.addفي عند إضافة قائمتين معا، المصطلح الصحيح لذلك concat, ، لا تضيف. operator.concat هو ما تحتاجه لاستخدامه.

إذا كنت تفكر في وظيفي، فهذه سهلة مثل هذا ::

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

ترى تقليل احترام نوع التسلسل، لذلك عند تزويد Tuple، يمكنك استعادة Tuple. دعونا نحاول مع قائمة ::

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

آها، ستعود قائمة.

ماذا عن الأداء ::

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

from_iterable هو سريع جدا! لكنها لا تقارن للحد من concat.

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

لماذا تستخدم تمديد؟

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

هذا يجب أن يعمل بشكل جيد.

النظر في تثبيت more_itertools صفقة.

> pip install more_itertools

انها السفن مع التنفيذ ل flatten (مصدر, ، من وصفات itertools):

import more_itertools


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

اعتبارا من الإصدار 2.4، يمكنك تسطيح كمبيوتر قريب أكثر تعقيدا مع more_itertools.collapse (مصدر, ، ساهم بها abarnet).

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

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

السبب في أن وظيفتك لم تنجح: يمتد تمديد الصفيف في مكانه ولا يعاده. لا يزال بإمكانك إرجاع X من Lambda، باستخدام بعض الخدعة:

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

ملاحظة: تمديد أكثر كفاءة من + في القوائم.

لا إعادة اختراع العجلة إذا كنت تستخدم جانغو:

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

...بانداس:

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

...itertools.:

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

...matplotlib.

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

...unipath.:

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

...setuptools.:

>>> from setuptools.namespaces import flatten
>>> list(flatten(l))
def flatten(l, a):
    for i in l:
        if isinstance(i, list):
            flatten(i, a)
        else:
            a.append(i)
    return a

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

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

ميزة سيئة لوظيفة Anil أعلاه هي أنه يتطلب من المستخدم دائما تحديد الوسيطة الثانية يدويا لتكون قائمة فارغة []. وبعد يجب أن يكون هذا بدلا من الافتراضي. نظرا للطريقة العمل كائنات Python، يجب تعيين هذه داخل الوظيفة، وليس في الحجج.

إليك وظيفة العمل:

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

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

اختبارات:

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

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

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

matplotlib.cbook.flatten() ستعمل على قوائم متداخلة حتى لو كانوا عش عمق أكثر من المثال.

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

نتيجة:

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

هذا هو 18x أسرع من الأسطوانة ._. تتسطح:

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

الإجابة المقبولة لا تعمل من أجلي عند التعامل مع قوائم قائمة على النصوص بأطوال المتغير. فيما يلي نهج بديل يعمل من أجلي.

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

الإجابة المقبولة التي فعلت ليس الشغل:

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

الحل الجديد المقترح ذلك فعل يعمل لدي:

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

الإصدار العريز

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

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

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

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

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

فيما يبدو أبسط بالنسبة لي:

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

يمكن للمرء أيضا استخدام numpy عريضة:

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

تحرير 11/02/2016: يعمل فقط عندما يكون لدى الأسلاك الأبعاد متطابقة.

يمكنك استخدام Numpy:
flat_list = list(np.concatenate(list_of_list))

رمز بسيط ل underscore.py حزمة مروحة

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

يحل جميع مشاكل المسطحة (أي عنصر قائمة أو التعشيش المعقدة)

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

يمكنك تثبيت underscore.py مع pip.

pip install underscore.py
def flatten(alist):
    if alist == []:
        return []
    elif type(alist) is not list:
        return [alist]
    else:
        return flatten(alist[0]) + flatten(alist[1:])
flat_list = []
for i in list_of_list:
    flat_list+=i

يعمل هذا الرمز أيضا بشكل جيد لأنه مجرد تمديد القائمة على طول الطريق. على الرغم من أنه مماثل كثيرا ولكن لديك واحدة فقط لحلقة. لذلك لديها تعقيد أقل من إضافة 2 للحلقات.

إذا كنت على استعداد للتخلي عن كمية صغيرة من السرعة لإلقاء نظرة نظافة، فيمكنك استخدامها numpy.concatenate().tolist() أو numpy.concatenate().ravel().tolist():

import numpy

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

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

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

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

يمكنك معرفة المزيد هنا في المستندات numpy.concatenate. و numpy.ravel.

أسرع حلا وجدته (للحصول على قائمة كبيرة على أي حال):

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

منجز! يمكنك بسهولة تشغيله مرة أخرى في قائمة تنفيذ القائمة (L)

قد لا تكون هذه الطريقة الأكثر فعالية لكنني اعتقدت أن أضع بطانة واحدة (في الواقع بطانة اثنين). ستعمل كلا الإصدارين على قوائم التسلسل الهرمي التعسفي، وتستغل ميزات اللغة (Python3.5) ومصاعدة.

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

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

الناتج هو

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

هذا يعمل في العمق أولا. تتلاشى العودية حتى تجد عنصر غير قائمة، ثم تمديد المتغير المحلي flist ثم تعودها إلى الوالد. حينما flist يتم إرجاعها، تمتد إلى الوالد flist في الفهم القائمة. لذلك، في الجذر، يتم إرجاع قائمة مسطحة.

يخلق المرء أعلاه العديد من القوائم المحلية وإرجاعها المستخدمة لتوسيع قائمة الوالدين. أعتقد أن الطريق حول هذا قد يخلق gloabl flist, ، مثل أدناه.

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

make_list_flat(a)
print (flist)

الناتج مرة أخرى

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

على الرغم من أنني لست متأكدا في هذا الوقت حول الكفاءة.

ملحوظة: ينطبق أدناه على Python 3.3+ لأنه يستخدم yield_from. six هي أيضا حزمة طرف ثالث، على الرغم من أنها مستقرة. بالتناوب، يمكنك استخدام sys.version.


في حالة ما اذا obj = [[1, 2,], [3, 4], [5, 6]], ، كل الحلول هنا جيدة، بما في ذلك الفهم القائمة و itertools.chain.from_iterable.

ومع ذلك، فكر في هذه الحالة أكثر تعقيدا قليلا:

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

هناك العديد من المشاكل هنا:

  • عنصر واحد، 6, ، هو مجرد العددية؛ انها غير قابلة للتوجيهية، لذلك سوف تفشل الطرق المذكورة أعلاه هنا.
  • عنصر واحد، 'abc', يكون قابلة للتوجيهية (كل شيء strS هي). ومع ذلك، فإن القراءة بين الخطوط قليلا، لا تريد أن تعاملها على هذا النحو - تريد أن تعاملها كعنصر واحد.
  • العنصر النهائي، [8, [9, 10]] هو نفسه متداخل قابلة للتكرار. قائمة الفهم الأساسية و chain.from_iterable فقط استخراج "1 مستوى أسفل".

يمكنك علاج هذا كما يلي:

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

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


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

هنا، يمكنك التحقق من أن العنصر الفرعي (1) هو قابلة للتوجيه Iterable, ، abc من itertools, ، ولكن أيضا تريد ضمان ذلك (2) العنصر هو ليس "تشبه السلسلة".

نهج آخر غير عادي يعمل على قوائم ميسرة ومتجانسة من الأعداد الصحيحة:

from typing import List


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

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

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

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

طريقة متكررة بسيطة باستخدام reduce من functools و ال add المشغل في القوائم:

>>> from functools import reduce
>>> from operator import add
>>> flatten = lambda lst: [lst] if type(lst) is int else reduce(add, [flatten(ele) for ele in lst])
>>> flatten(l)
[1, 2, 3, 4, 5, 6, 7, 8, 9]

الوظيفة flatten يأخذ في lst كمعلمة. انها الحلقات كل عناصر lst حتى الوصول إلى الأعداد الصحيحة (يمكن أن تتغير أيضا int ل float, str, ، وما إلى ذلك لأنواع البيانات الأخرى)، والتي تتم إضافتها إلى قيمة العودة للحصول على العودية الخارجية.

العودية، على عكس الأساليب مثل for حلقات وموناد، هي أنه كذلك الحل العام لا يقتصر على عمق القائمة. وبعد على سبيل المثال، يمكن تسطير قائمة بعمق 5 بنفس طريقة l:

>>> l2 = [[3, [1, 2], [[[6], 5], 4, 0], 7, [[8]], [9, 10]]]
>>> flatten(l2)
[3, 1, 2, 6, 5, 4, 0, 7, 8, 9, 10]
مرخصة بموجب: CC-BY-SA مع الإسناد
لا تنتمي إلى StackOverflow
scroll top