LINUX.ORG.RU

[python] Объясните код

 


0

3

Объясните мне, пожалуйста, что и как происходит в этом коде:

def transposed(lists):
   if not lists: return []
   return map(lambda *row: list(row), *lists)

Здесь происходит транспонирование матрицы (списка списков), однако ко мне не пришло понимание того, как это работает.

Разложите, пожалуйста, по полочкам данный код.

★★

функция map() применяет фунцию, переданную как первый аргумент (в данном случае list()) ко второму аргументу (lists)

anonymous
()
Ответ на: комментарий от anonymous

вернее не так, первый аргумент функции map() - лямбда-функция, которая возвращает list(lists), но сути не меняет.

anonymous
()
Ответ на: комментарий от anonymous

Но если так, то для каждого элемента lists (а это есть список) вызывается функция list(), которая возвращает этот же список?

И что означают звездочки в коде?

bk_ ★★
() автор топика
Ответ на: комментарий от bk_

Звездочка в определении функции пакует аргументы в список, в вызове - распаковывает список.

xetf ★★
()

Ну, чтобы понять, что этот код делает, надо прочитать документацию на функцию map, а также про распаковку аргументов.

В любом случае - этот код - пример бесполезности, а я бы сказал даже и вредности map. Мало того, что неподготовленный человек сходу никогда это не поймет, но, к тому же, это еще и тормозит:

>>> timeit.timeit('map(lambda *row: list(row), *lists)', 'lists=[range(10)]*10')
9.6383590698242188
>>> timeit.timeit('[list(i) for i in izip(*lists)]', 'lists=[range(10)]*10; from itertools import izip')
6.2881510257720947

Все эти map, reduce, filter давно пора выкинуть из языка. Только с толку людей сбивают.

shylent
()
Ответ на: комментарий от xetf

Гвидко не осилил лямбды, комбинаторы и итераторы, и решил, что никто другой тоже не осилит.

anonymous
()
Ответ на: комментарий от bk_

И что означают звездочки в коде?

def transposed(lists):
   if not lists: return []
   fun = lambda *row: list(row)
   return map(fun, *lists)

Например lists = [[1, 2], [3, 4]] Тогда функция map будет вызвана так: map(fun, [1,2], [3,4]), т.е. первый вызов fun будет fun(1,3), второй fun(2,4). Звездочка в лямбде означает что в row будет кортеж (или список, или еще что, тут я не уверен) аргументов.

nozh
()

if not lists: return []
Если lists == None или False, то вернут пустой лист ([]).

return map(lambda *row: list(row), *lists)
Здесь всё интереснее.
Если * используется на листе в вызове функции, то оператор * позволяет «разделить» лист на операнды. Пример:

foo = lambda x, y: print(x+y)

nums = [1, 2]


foo(*nums) #делим nums на аргументы -> foo(1, 2)


3

Если * используется на объекте в объявлении функции, то данный оператор позволяет «объединить» все обычные(заданные не через слово(пример: foo(1), но не foo(a=1)) аргументы в лист. Пример:


foo = lambda *xy: print(хy)

foo(1, 2)


(1, 2)

В пайтоне, до тройки, присутствует ещё и оператор **, который работает точно так же. Однако, вместо обычных аргументов, из него получаются аргументы по словам (foo(a=1)), а вместо листов, он делает словари.

В данном случае, будет произведена итерация через каждый из листов(благодаря распаковке) и передача значения каждого итератора исполняемой фунцкии(у тебя это lambda *row). В твоей функции используется упаковка аргументов, т.е. ты получаешь в row лист аргументов(текущие значения всех листов или None, если лист меньше размером).
Как результат, ты получаешь:
[
[lists[0][0], lists[1][0], lists[2][0], ... lists[N][0]],
[lists[0][1], lists[1][1], lists[2][1], ... lists[N][1]],
[lists[0][2], lists[1][2], lists[2][2], ... lists[N][2]],
.............................
[lists[0][N], lists[1][N], lists[2][N], ... lists[N][N]],
]

tia
()
Ответ на: комментарий от xetf

> reduce в python3 выкинули, кстати

И очень правильно сделали (за чейнджлогом py3k не слежу, к сожалению). Зачем нужны бесполезные функции, если есть list comprehensions (квадратные скобочки), generator expressions (круглые скобочки) и модуль itertools?

shylent
()
Ответ на: комментарий от anonymous

Соглашусь, если приведете нормальный пример, где можно использовать lambda expression, но нельзя использовать def statement.

shylent
()

Всем огромное спасибо, разобрался.

bk_ ★★
() автор топика
Ответ на: комментарий от shylent

Оба варианта --- нечитаемый ужос по сравнению с православным

CL-USER> (defun transpose (lists)
           (mapcar #'(lambda (row) (funcall #'list row)) lists))
ugoday ★★★★★
()
Ответ на: комментарий от shylent

>Соглашусь, если приведете нормальный пример, где можно использовать lambda expression, но нельзя использовать def statement.

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

anonymous
()
Ответ на: комментарий от ugoday

И чем твой православный вариант отличается от первого (кроме синтаксиса)?

А правда, что гвидо не осилил многострочную лямбду или я путаю чо?


Правда.

xetf ★★
()
Ответ на: комментарий от ugoday

В python функции - first class citizen (как, впрочем, и любые другие объекты). Т.е. они могут быть использованы в качестве параметров. lambda expression и def statement (многострочный, ага) имеют на выходе абсолютно одно и тоже - function object.

shylent
()
Ответ на: комментарий от ugoday

Что-то много мусора: кавычки, решетки и прочий шлак.

In [1]: zip(*[[1,2,3], [4,5,6], [7,8,9]])
Out[1]: [(1, 4, 7), (2, 5, 8), (3, 6, 9)]

arhibot
()
Ответ на: комментарий от ugoday

> А правда, что гвидо не осилил многострочную лямбду или я путаю чо?

Чорт, я в предыдущем сообщении не заметил, кому отвечаю. Конечно же не осилил, о чем разговор!

shylent
()
Ответ на: комментарий от xetf

И чем твой православный вариант отличается от первого

читаемостью. В питоновой каше без бутылки хрен разберёшься.

ugoday ★★★★★
()
Ответ на: комментарий от ugoday

> кроме именованности/анонимности.

Именно так. А какая разница, собственно? Имя не влияет на поведение. Это же просто один атрибут.

shylent
()
Ответ на: комментарий от arhibot

В топике отличный код.

В таком случае, я надеюсь никогда не встретить плохого питонового кода.

ясный любому питонисту

любой питонист bk_ использует конструкцию

   if not lists: return []

а shylent --- нет. Комментарии экспертов?

ugoday ★★★★★
()
Ответ на: комментарий от shylent

timeit.timeit('map(lambda *row: list(row), *lists)', 'lists=[range(10)]*10')

timeit.timeit('[list(i) for i in izip(*lists)]', 'lists=[range(10)]*10; from itertools import izip'

А почему бы не просто list(zip(*lists)) ?

l0k9j8
()
Ответ на: комментарий от ugoday

Код был мной скопипизжен откуда-то-непомню-откуда. Так что конструкцию использую не я.

И вообще, common lisp рулит.

bk_ ★★
() автор топика
Ответ на: комментарий от shylent

А какая разница, собственно?

У одного есть имя, а у другого --- нету.

Имя не влияет на поведение.

Влияет на читаемость кода. Попробуй писать на нормальных языках с нормальными же лямбдами, заметишь, что в ряде случаев удобнее по месту определить лямбда-вызов, чем определять именованную функцию с тем, чтобы её один раз дёрнуть в коде.

P.S. А в питоне можно определить локальную функцию в контексте другой функции?

ugoday ★★★★★
()
Ответ на: комментарий от l0k9j8

zip и так список отдаст + внутри тюплы, а не списки

arhibot
()
Ответ на: комментарий от l0k9j8

В исходном коде на выходе был список списков. Тут получится список кортежей.

izip вместо zip, чтобы рабоать с итератором вместо копии, хотя в данном случае выигрыш относительно небольшой - 6.0 с izip против 6.9 с zip.

shylent
()
Ответ на: комментарий от ugoday

Корректная обработка крайнего случая. Без этого функция вернёт None для пустого списка.

Для лиспа это не актуально потому что там, в отличие от питона, nil и () — один и тот же объект.

xetf ★★
()
Ответ на: комментарий от ugoday
>>> def a():
...     print "a"
...     def b(s):
...         print "b" * s
...     b(5)
... 
>>> a()
a
bbbbb
>>> b()
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
NameError: name 'b' is not defined

Да, можно.

PolarFox ★★★★★
()
Ответ на: комментарий от ugoday

> А в питоне можно определить локальную функцию в контексте другой функции?

И эти люди троллят о «нормальных языках» %)

tailgunner ★★★★★
()
Ответ на: комментарий от ugoday

>А в питоне можно определить локальную функцию в контексте другой функции?

Да, можно.

xetf ★★
()
Ответ на: комментарий от arhibot

shylent прав.

Следовательно bk_ --- нет. Вот так в «ясном любому питонисту» коде встречаются ошибочные решения.

его решение со списковым генератором красивей твоего.

Скажу откровенно, визуальная эстетическая неопрятность питонового кода и побудила меня влезть в это обсуждение.

Но я не отрицаю, здесь можно написать и красивее чем на cl, если писать на scheme. За счёт отсутствия funcall смотреться будет легче и изящнее.

ugoday ★★★★★
()
Ответ на: комментарий от ugoday

Хочешь я сейчас напишу сложение двух чисел на CL с ошибкой? Мне не сложно.

если писать на scheme.

Можно сразу на питоне.

arhibot
()
Ответ на: комментарий от arhibot

Хочешь я сейчас напишу сложение двух чисел на CL с ошибкой?

Изволь.

Можно сразу на питоне.

вот только не надо троллить.

ugoday ★★★★★
()

Парни, я уссываюсь, вы уж определитесь. Кто-то один из вас должен быть правым.

ugoday ★★★★★
()
Вы не можете добавлять комментарии в эту тему. Тема перемещена в архив.