Возможно ли иметь несколько операторов в лямбда-выражении Python?

112

Я новичок в питоне, пытаюсь добиться следующего:

У меня есть список списков:

lst = [[567,345,234],[253,465,756, 2345],[333,777,111, 555]]

Я хочу отобразить lst в другой список, содержащий только второе наименьшее число из каждого подсписка. Итак, результат должен быть:

[345, 465, 333]

Например, если бы меня интересовало наименьшее число, я мог бы сделать:

map(lambda x: min(x),lst)

Хотел бы я сделать это:

map(lambda x: sort(x)[1],lst)

но сортировка не связывает. (не возвращает None)

и что-то подобное не допускается:

map(lambda x: sort(x); x[1],lst) #hence the multiple statement question

Есть ли способ сделать это с помощью карты в Python, но без определения именованной функции ? (это просто с анонимными блоками в рубине, например)

оттодидакт
источник
наверняка это должно быть возможно ... может быть, в новой версии ...
ZEE
1
Вы не можете выполнять операторы, но вы можете вызывать функции в лямбда-функции, поэтому беспилотный грязный хакер lambda x: sort(x) OR x[1]будет работать: здесь OR оценивает свой первый аргумент (возвращаемое значение None) как логическое значение (=> False), и в этом случае OR возвращает свой второй аргумент. Но, как говорится в ответах, лучше избегать лямбды.
Макс

Ответы:

136

Здесь я могу дать несколько разных ответов, от вашего конкретного вопроса до более общих проблем. Итак, от наиболее конкретного к наиболее общему:

В. Можете ли вы поместить несколько операторов в лямбду?

A. Нет. Но на самом деле вам не нужно использовать лямбду. defВместо этого вы можете поместить утверждения в . то есть:

def second_lowest(l):
    l.sort()
    return l[1]

map(second_lowest, lst)

В. Можно ли получить второй самый низкий элемент из лямбда, отсортировав список?

А. Да. Как указывает ответ alex , sorted()это версия сортировки, которая создает новый список, а не сортирует на месте, и может быть объединена в цепочку. Обратите внимание, что, вероятно, вам следует использовать именно это - плохая практика для вашей карты иметь побочные эффекты в исходном списке.

В. Как мне получить второй самый низкий элемент из каждого списка в последовательности списков?

А. на sorted(l)[1] самом деле не лучший способ для этого. Он имеет сложность O (N log (N)), а решение O (n) существует. Его можно найти в модуле heapq.

>>> import  heapq
>>> l = [5,2,6,8,3,5]
>>> heapq.nsmallest(l, 2)
[2, 3]

Так что просто используйте:

map(lambda x: heapq.nsmallest(x,2)[1],  list_of_lists)

Также обычно считается более понятным использовать понимание списка, которое полностью избегает лямбда:

[heapq.nsmallest(x,2)[1] for x in list_of_lists]
Брайан
источник
3
Я не думаю, что вы правы в отношении решения O (n) в модуле heapq. Все, что вы делаете, - это сортируете список кучей, который составляет O (n log n), а затем находите самые маленькие элементы.
avpx 03
8
Документация ( docs.python.org/2/library/heapq.html#heapq.nsmallest ) действительно предупреждает, что использование heapq.nsmallest () может быть менее эффективным, чем просто использование sorted (), поэтому только измерения могут определить, какое решение самый быстрый в вашем случае. Однако heapq.nsmallest () имеет сложность O (k * log (n) + n), я думаю, с n длиной списка и k количеством наименьших элементов, которые вы хотите извлечь. O (n), чтобы заполнить список, и k раз O (log (n)), чтобы вывести k элементов. Это лучше, чем O (n * log (n)), особенно для малых k.
Vortexfive
2
@Vortexfive, а для константы k(как здесь со «вторым по величине») O(k*log(n)+n)упрощается доO(n)
Дункан
5
Технически вам не нужна лямбда и для однострочников, но это, безусловно, удобнее. Надеюсь, позже будет добавлена ​​лучшая поддержка лямбда.
Джеймс
1
@avpx: это не куча. Мы поддерживаем только 2-элементную кучу из 2 наименьших видимых элементов, а не нагромождаем весь список и выталкиваем все элементы, как это сделала бы heapsort.
user2357112 поддерживает Монику
81

Помещение операторов в список может имитировать несколько операторов:

Например:

lambda x: [f1(x), f2(x), f3(x), x+1]
jmkg
источник
9
Чтобы прояснить, вызовы функций не считаются в Python операторами, они являются выражениями. Итак, этот список - просто список выражений, которые могут быть всеми None.
Yawar
Я думаю, что это работает, потому что при динамическом анализе коллекции, переданной лямбда- выражению, интерпретатор обнаруживает вызываемые подписи и выполняет их ... вы можете протестировать с помощью -> лямбда x: [print (x), print (x + 1), print (x +2)]
ZEE
5
Еще лучше: lambda x: [f1 (x), f2 (x)] [- 1], он вернет результат вычисления из последнего выражения, как, вероятно, и ожидалось.
Антон Овсянников
22

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

(lambda x, f: list((y[1] for y in f(x))))(lst, lambda x: (sorted(y) for y in x))

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

Изменить: путешественник во времени возвращается! Вы также можете злоупотреблять поведением логических выражений (имея в виду правила сокращения и истинность), чтобы связать операции. Использование тернарного оператора дает вам еще больше возможностей. Опять же, у вас не может быть нескольких операторов , но вы, конечно, можете иметь много вызовов функций. В этом примере выполняется произвольный мусор с кучей данных, но он показывает, что вы можете делать некоторые забавные вещи. Операторы print являются примерами функций, которые возвращают None(как и .sort()метод), но они также помогают показать, что lambdaделает.

>>> (lambda x: print(x) or x+1)(10)
10
11
>>> f = (lambda x: x[::2] if print(x) or x.sort() else print(enumerate(x[::-1]) if print(x) else filter(lambda (i, y): print((i, y)) or (i % 3 and y % 2), enumerate(x[::-1]))))
>>> from random import shuffle
>>> l = list(range(100))
>>> shuffle(l)
>>> f(l)
[84, 58, 7, 99, 17, 14, 60, 35, 12, 56, 26, 48, 55, 40, 28, 52, 31, 39, 43, 96, 64, 63, 54, 37, 79, 25, 46, 72, 10, 59, 24, 68, 23, 13, 34, 41, 94, 29, 62, 2, 50, 32, 11, 97, 98, 3, 70, 93, 1, 36, 87, 47, 20, 73, 45, 0, 65, 57, 6, 76, 16, 85, 95, 61, 4, 77, 21, 81, 82, 30, 53, 51, 42, 67, 74, 8, 15, 83, 5, 9, 78, 66, 44, 27, 19, 91, 90, 18, 49, 86, 22, 75, 71, 88, 92, 33, 89, 69, 80, 38]
[0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, 62, 63, 64, 65, 66, 67, 68, 69, 70, 71, 72, 73, 74, 75, 76, 77, 78, 79, 80, 81, 82, 83, 84, 85, 86, 87, 88, 89, 90, 91, 92, 93, 94, 95, 96, 97, 98, 99]
(0, 99)
(1, 98)
(2, 97)
(3, 96)
(4, 95)
(5, 94)
(6, 93)
(7, 92)
(8, 91)
(9, 90)
(10, 89)
(11, 88)
(12, 87)
(13, 86)
(14, 85)
(15, 84)
(16, 83)
(17, 82)
(18, 81)
(19, 80)
(20, 79)
(21, 78)
(22, 77)
(23, 76)
(24, 75)
(25, 74)
(26, 73)
(27, 72)
(28, 71)
(29, 70)
(30, 69)
(31, 68)
(32, 67)
(33, 66)
(34, 65)
(35, 64)
(36, 63)
(37, 62)
(38, 61)
(39, 60)
(40, 59)
(41, 58)
(42, 57)
(43, 56)
(44, 55)
(45, 54)
(46, 53)
(47, 52)
(48, 51)
(49, 50)
(50, 49)
(51, 48)
(52, 47)
(53, 46)
(54, 45)
(55, 44)
(56, 43)
(57, 42)
(58, 41)
(59, 40)
(60, 39)
(61, 38)
(62, 37)
(63, 36)
(64, 35)
(65, 34)
(66, 33)
(67, 32)
(68, 31)
(69, 30)
(70, 29)
(71, 28)
(72, 27)
(73, 26)
(74, 25)
(75, 24)
(76, 23)
(77, 22)
(78, 21)
(79, 20)
(80, 19)
(81, 18)
(82, 17)
(83, 16)
(84, 15)
(85, 14)
(86, 13)
(87, 12)
(88, 11)
(89, 10)
(90, 9)
(91, 8)
(92, 7)
(93, 6)
(94, 5)
(95, 4)
(96, 3)
(97, 2)
(98, 1)
(99, 0)
[(2, 97), (4, 95), (8, 91), (10, 89), (14, 85), (16, 83), (20, 79), (22, 77), (26, 73), (28, 71), (32, 67), (34, 65), (38, 61), (40, 59), (44, 55), (46, 53), (50, 49), (52, 47), (56, 43), (58, 41), (62, 37), (64, 35), (68, 31), (70, 29), (74, 25), (76, 23), (80, 19), (82, 17), (86, 13), (88, 11), (92, 7), (94, 5), (98, 1)]
2рс2ц
источник
7

Используйте функцию сортировки , например:

map(lambda x: sorted(x)[1],lst)
Алекс Васи
источник
ах.Спасибо (какое описательное имя для функции!) По-прежнему интересно узнать о множественном выражении в лямбда-части. Возможно?
ottodidakt
1
Нет, «функции, созданные с помощью лямбда-форм, не могут содержать операторов». docs.python.org/reference/expressions.html#lambda
Алекс Вася
1
-1: лямбда и карта вместо понимания списков? Не очень питонический.
nikow
@nikow [x [1] вместо x в отсортированном (списке)] - питоническим способом?
RaGa__M
@Explorer_N: не список должен быть отсортирован, но элементы списка (которые являются списками) должны быть отсортированы в данной задаче. Так , а: [sorted(x)[1] for x in list_of_lists].
Макс
6

Фактически, вы можете иметь несколько операторов в лямбда-выражении в Python. Это не совсем тривиально, но в вашем примере работает следующее:

map(lambda x: x.sort() or x[1],lst)

Вы должны убедиться, что каждый оператор ничего не возвращает и не оборачивает его в (.. и False). Результат - это то, что вернула последняя оценка.

Пример:

>>> f = (lambda : (print(1) and False) or (print(2) and False) or (print(3) and False))
>>> f()
1
2
3
Лорено Хеер
источник
4

Хакерский способ объединить несколько операторов в один в Python - использовать ключевое слово «and» в качестве оператора короткого замыкания. Затем вы можете использовать этот единственный оператор непосредственно как часть лямбда-выражения.

Это похоже на использование «&&» в качестве оператора короткого замыкания в языках оболочки, таких как bash.

Также обратите внимание: вы всегда можете исправить оператор функции, чтобы он возвращал истинное значение, заключив функцию в оболочку.

Пример:

def p2(*args):
    print(*args)
    return 1 # a true value

junky = lambda x, y: p2('hi') and p2('there') and p2(x) and p2(y)

junky("a", "b")

Если подумать, вероятно, лучше использовать «или» вместо «и», поскольку многие функции возвращают «0» или «Нет» в случае успеха. Затем вы можете избавиться от функции-оболочки в приведенном выше примере:

junky = lambda x, y: print('hi') or print('there') or print(x) or print(y)

junky("a", "b")

Операция 'and' будет оценивать выражения, пока не достигнет первого нулевого возвращаемого значения. после чего происходит короткое замыкание. 1 и 1 и 0 и 1 оценивают: 1 и 1 и 0, и отбрасывают 1

Операция 'или' будет оценивать выражения до тех пор, пока не дойдет до первого ненулевого возвращаемого значения. после чего происходит короткое замыкание.

0 или 0 или 1 или 0 оценивает 0, или 0, или 1, и отбрасывает 0

Билл Мур
источник
3

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

(отсортировано (столбец) [1] для столбца в lst)

странный
источник
2

Позвольте представить вам великолепный, но устрашающий взлом:

import types

def _obj():
  return lambda: None

def LET(bindings, body, env=None):
  '''Introduce local bindings.
  ex: LET(('a', 1,
           'b', 2),
          lambda o: [o.a, o.b])
  gives: [1, 2]

  Bindings down the chain can depend on
  the ones above them through a lambda.
  ex: LET(('a', 1,
           'b', lambda o: o.a + 1),
          lambda o: o.b)
  gives: 2
  '''
  if len(bindings) == 0:
    return body(env)

  env = env or _obj()
  k, v = bindings[:2]
  if isinstance(v, types.FunctionType):
    v = v(env)

  setattr(env, k, v)
  return LET(bindings[2:], body, env)

Теперь вы можете использовать эту LETформу как таковую:

map(lambda x: LET(('_', x.sort()),
                  lambda _: x[1]),
    lst)

который дает: [345, 465, 333]

divs1210
источник
Первоначально опубликовано здесь: gist.github.com/divs1210/d218d4b747b08751b2a232260321cdeb
divs1210
1

Вы можете сделать это за O (n) раз, используя min и index вместо использования sort или heapq.

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

new_list = lst[:lst.index(min(lst))] + lst[lst.index(min(lst))+1:]

Затем возьмите минимальное значение нового списка:

second_smallest = min(new_list)

Теперь все вместе в одной лямбде:

map(lambda x: min(x[:x.index(min(x))] + x[x.index(min(x))+1:]), lst)

Да, это действительно некрасиво, но алгоритмически дешево. Также, поскольку некоторые люди в этом потоке хотят видеть понимание списка:

[min(x[:x.index(min(x))] + x[x.index(min(x))+1:]) for x in lst]
голыйфанатик
источник
1

Именно для этого и используется bindфункция в монаде .

С помощью bindфункции вы можете объединить несколько лямбд в одну лямбду, каждая из которых представляет собой оператор.

jhegedus
источник
1

На самом деле есть способ использовать несколько операторов в лямбде. Вот мое решение:

lst = [[567,345,234],[253,465,756, 2345],[333,777,111, 555]]

x = lambda l: exec("l.sort(); return l[1]")

map(x, lst)
EternalCrafter_606
источник
код внутри execне может быть исследован интеллектуальной IDE
javadba
0

Да. Вы можете определить это таким образом, а затем заключить несколько выражений следующим образом:

Схема начала:

begin = lambda * x: x [-1]

Общий проект Lisp:

progn = лямбда * x: x [-1]

Анастасий
источник
0

Есть лучшие решения без использования лямбда-функции. Но если мы действительно хотим использовать лямбда-функцию, вот общее решение для работы с несколькими операторами: map (lambda x: x [1] if (x.sort ()) else x [1], lst)

Вам все равно, что возвращает оператор.

user11166890
источник
0

Да, это возможно. Попробуйте ниже фрагмент кода.

x = [('human', 1), ('i', 2), ('am', 1), ('.', 1), ('love', 1), ('python', 3), ('', 1),
  ('run', 1), ('is', 2), ('robust', 1), ('hello', 1), ('spark', 2), ('to', 1), ('analysis', 2), ('on', 1), ('big', 1), ('data', 1), ('with', 1), ('analysis', 1), ('great', 1)
]

rdd_filter = rdd1_word_cnt_sum.filter(lambda x: 'python' in x or 'human' in x or 'big' in x)
rdd_filter.collect()
Ravi
источник
-1

Я дам вам другое решение: заставьте лямбда-выражение вызывать функцию.

def multiple_statements(x, y):
    print('hi')
    print('there')
    print(x)
    print(y)
    return 1

junky = lambda x, y: multiple_statements(x, y)

junky('a', 'b');
Билл Мур
источник
7
Еще лучше: junky = multiple_statements.
Соломон Учко