2012-07-04 3 views
3

Что плохого в следующем ?:Как использовать сократить со списком списков

lss = reduce(list.extend, [[1],[2]], []) 

причины:

Traceback (most recent call last): 
    File "<pyshell#230>", line 1, in <module> 
    lss = reduce(list.extend, [[1],[2]], []) 
TypeError: descriptor 'extend' requires a 'list' object but received a 'NoneType' 

Я не уверен, где NoneType откуда.

+0

Что вы желаемый результат/Что список вы пытаетесь расширить? –

ответ

13

Попробуйте вместо этого:

lss = reduce(lambda acc, ele : acc + ele, [[1],[2]], []) 

lss 
> [1, 2] 

Проблема заключается в том, что extend() возвращается None (вот где NoneType исходит от), и это не будет работать с тем, что вы хотите сделать - функция передается reduce()должен вернуть значение: накопленный результат до сих пор.

+0

Я понимаю, что могу это сделать. Я все равно хотел бы знать, что не так с использованием метода list.extend. Похоже, он должен работать. - на это ответил ваш ответ. – cammil

+1

@cammil, как я уже говорил выше: 'extend()' will _always_ return 'None'; для 'reduce()' для работы функции, которая запускается _has to_, ​​возвращает значение: накопленный результат до сих пор. –

+2

В случае, если кто-то задается вопросом * почему * 'list.extend()' возвращает 'None': это потому, что в Python функции, которые мутируют структуру данных, всегда возвращают' None', потому что Python выполняет команду Command/Query Separation. http://en.wikipedia.org/wiki/Command-query_separation – steveha

5

Я думаю, что стоит отметить, что:

sum([[1],[2]], []) 

также будет работать, и я уверен, что будет быстрее, переходя затем лямбда, чтобы уменьшить.

мне было интересно, как к скорости различных методов, так что я сделал некоторые испытания:

reduce(lambda a,b:a+b, x, [])    3644.38161492 
reduce(list.__add__, x, [])     3609.44079709 
sum(x,[])         3526.84987307 
y = [];for z in x: y.extend(z)    143.370306969 
y = [];map(y.extend,x)      71.7020270824 
y = [None]*400;del y[:];map(y.extend,x)  66.2245891094 
list(itertools.chain(*x))     102.285979986 
list(itertools.chain.from_iterable(x))  96.6231369972 
[a for b in x for a in b]     203.764872074 

И PyPy (Потому что, почему бы и нет)

reduce(lambda a,b:a+b, x, [])    4797.5895648 
reduce(list.__add__, x, [])     4794.01214004 
sum(x,[])         4748.02929902 
y = [];for z in x: y.extend(z)    56.9253079891 
y = [];map(y.extend,x)      73.8642170429 
y = [None]*400;del y[:];map(y.extend,x)  152.157783031 
list(itertools.chain(*x))     633.854824066 
list(itertools.chain.from_iterable(x))  629.917827129 
[a for b in x for a in b]      89.6922459602 

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

Выводы:

  1. Использование лямбда в вашем сокращении происходит медленно
  2. Специализированный sum функция быстрее, чем уменьшение
  3. Добавление списков происходит медленно.
  4. Накладные расходы на петле Python значительны.
+0

Если вы установите x = x * 100, вы можете начать видеть реальные различия в скорости между алгоритмами. – DSM

+0

@DSM, вы правы, поэтому я сделал это. –

+0

+1 интересные тайминги – jamylak

1

Как отмечает Óscar López, list.extend возвращает None и, таким образом, не могут быть использованы с reduce. В качестве альтернативы предложенному использования функции lambda, вы можете также использовать list.__add__ с reduce:

>>> reduce(list.__add__, [[1],[2]], []) 
[1, 2] 
1

Вы можете использовать itertools.chain

сделать итератор, который не возвращает элементы из первой Iterable до его будет исчерпан, а затем переходит к следующему итерируемому, пока все неиспользуемые итерации не будут исчерпаны. Используется для обработки последовательных последовательностей как одиночная последовательность . Эквивалент:

def chain(*iterables): 
    # chain('ABC', 'DEF') --> A B C D E F 
    for it in iterables: 
     for element in it: 
      yield element