Нет многострочной лямбды в Python: почему бы и нет?


335

Я слышал, что в Python нельзя добавить многострочные лямбды, потому что они синтаксически конфликтуют с другими синтаксическими конструкциями в Python. Сегодня я думал об этом в автобусе и понял, что не могу придумать ни одной конструкции Python, с которой сталкиваются многострочные лямбды. Учитывая, что я довольно хорошо знаю язык, это меня удивило.

Теперь я уверен, что у Гвидо была причина не включать в язык многострочные лямбды, но из любопытства: в какой ситуации включение многострочной лямбды было бы неоднозначным? Верно ли то, что я слышал, или есть какая-то другая причина, по которой Python не допускает многострочные лямбды?


12
tl; dr version: потому что Python - это ленивый язык без блоков {}, и поэтому это было запрещено для сохранения согласованного синтаксического дизайна.
Андрей

11
Кроме того: я очень удивлен, что никто не упомянул об этом в ответах ... Вы можете завершить строки символом \ в Python и перейти к следующей строке ... Эта информация как бы заменяет весь этот вопрос так ...
Эндрю


"синтаксический дизайн"
Николас

Это потребует разрешения операторов внутри выражений. Если вы собираетесь это сделать, вам не нужны lambdaвыражения в первую очередь; Вы можете просто использовать defвыражения в выражениях.
chepner

Ответы:


153

Посмотрите на следующее:

map(multilambda x:
      y=x+1
      return y
   , [1,2,3])

Возвращает ли это лямбда (y, [1,2,3])(таким образом, карта получает только один параметр, что приводит к ошибке)? Или это возвращается y? Или это синтаксическая ошибка, потому что запятая в новой строке не на месте? Как бы Python знал, что вы хотите?

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

Это просто, возможно, есть еще примеры.


107
они могут принудительно использовать скобки, если вы хотите вернуть кортеж из лямбды. ИМО, это должно было всегда выполняться для предотвращения таких двусмысленностей, ну да ладно.
mpen

26
Это простая двусмысленность, которая должна быть решена путем добавления дополнительного набора паренов, который уже существует во многих местах, например, выражения-генераторы, окруженные другими аргументами, вызывающий метод для целочисленного литерала (хотя это не обязательно должно иметь место, поскольку имя функции не может начинаться с цифры), и, конечно же, однострочные лямбды (которые могут быть длинными выражениями, написанными в несколько строк). Многострочные лямбды не будут особенно отличаться от этих случаев, если они исключают их на этой основе. Это настоящий ответ.
nmclean

3
Мне нравится , как есть языки Gazillion , что сделка с ним не лада, но почему - то есть некоторые глубинные причины , почему это якобы очень трудно , если не невозможно
Николас

1
@nicolas это питон в двух словах
Джавадба

Причина, почему я не использую лямбду, поэтому недостаточно развита в Python.
NoName

635

Гидо ван Россум (изобретатель Python) сам отвечает на этот точный вопрос в старом сообщении в блоге .
По сути, он признает, что это теоретически возможно, но любое предлагаемое решение будет непифоническим:

«Но сложность любого предложенного решения для этой загадки для меня огромна: она требует, чтобы синтаксический анализатор (или, точнее, лексер) мог переключаться между режимами, чувствительными к отступам и режимами, нечувствительными к отступам, сохраняя стек предыдущих режимов и уровня отступов. Технически это все можно решить (уже есть стек уровней отступов, которые можно обобщить). Но ни одно из этого не лишает меня интуитивного ощущения, что все это тщательно продуманная штуковина Рубе Голдберга ".


108
Почему это не лучший ответ? Речь идет не о технических причинах, а о выборе дизайна, как четко указано изобретателем.
Дан Абрамов

13
@DanAbramov, потому что ОП не входил в систему годами, наверное.
Профессор Фалькен нарушил контракт

7
Для тех, кто не понимал ссылку на Рубе Голдберга, см .: en.wikipedia.org/wiki/Rube_Goldberg_Machine
fjsj

56
Ответ Гвидо - это еще одна причина, по которой я бы хотел, чтобы Python не зависел от отступов для определения блоков.
LS

25
Я не уверен, что назвал бы «внутреннее чувство» выбором дизайна. ;)
Эллиот Кэмерон

54

Как правило, это очень уродливо (но иногда альтернативы еще более уродливы), поэтому обходной путь заключается в создании выражения в скобках:

lambda: (
    doFoo('abc'),
    doBar(123),
    doBaz())

Он не будет принимать никаких заданий, поэтому вам придется подготовить данные заранее. Место, где я нашел это полезным, - это оболочка PySide, где у вас иногда бывают короткие обратные вызовы. Написание дополнительных функций-членов было бы еще более уродливым. Обычно вам это не нужно.

Пример:

pushButtonShowDialog.clicked.connect(
    lambda: (
    field1.clear(),
    spinBox1.setValue(0),
    diag.show())

2
Мой босс просто просил что-то подобное в нашем приложении PyQt. Потрясающие!
TheGerm

1
Спасибо за это, я также искал хороший способ использовать короткие (но все еще многострочные) лямбды в качестве обратных вызовов для нашего PySide UI.
Майкл Леонард

И теперь я увидел это, он тут же предложил использовать lambda argи setattr(arg, 'attr','value')подрывать «нет назначений ...». И затем есть оценка короткого замыкания andи or... это Javascript, который делает это. Вонзает в тебя корни, как плющ в стену. Я почти надеюсь, что забуду это из-за Рождества.
nigel222

довольно умный - и вполне читаемый. Теперь - о тех (пропавших ..) назначениях ..
Джавадба

@ nigel222 почему стыдно? Язык Python принципиально ограничен, но в любом случае он используется для большей части науки о данных . Поэтому мы вносим коррективы. Поиск способов создания побочных эффектов (часто достаточно просто печати / регистрации!) И заданий (часто достаточно, чтобы просто промежуточные переменные!) Должен быть хорошо обработан языком. Но они даже не поддерживаются (по крайней мере , если вы будете следовать PEPрекомендациям)
javadba

17

Пара релевантных ссылок:

Некоторое время я следил за разработкой Reia, которая изначально собиралась использовать синтаксис Python, основанный на отступах, с блоками Ruby, все на вершине Erlang. Но дизайнер отказался от чувствительности к отступам, и этот пост, который он написал об этом решении, включает в себя обсуждение проблем, с которыми он столкнулся с блоками отступа + многострочный, и повышенную оценку, которую он получил за проблемы / решения Гвидо по дизайну:

http://www.unlimitednovelty.com/2009/03/indentation-sensitivity-post-mortem.html

Кроме того, вот интересное предложение для блоков в стиле Ruby в Python, с которым я столкнулся, когда Гвидо публикует ответ без его фактического уничтожения (хотя и не уверен, что был какой-либо последующий сбой):

http://tav.espians.com/ruby-style-blocks-in-python.html


12

[Редактировать] Прочитайте этот ответ. Это объясняет, почему многострочная лямбда не вещь.

Проще говоря, это не пифонично. Из поста Гвидо ван Россума:

Я считаю неприемлемым любое решение, которое встраивает блок на основе отступа в середину выражения. Поскольку я нахожу альтернативный синтаксис для группировки операторов (например, фигурные скобки или ключевые слова begin / end) одинаково неприемлемым, это в значительной степени делает многострочную лямбду неразрешимой загадкой.


10

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

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(('y', x + 1,
                   'z', x - 1),
                  lambda o: o.y * o.z),
    [1, 2, 3])

который дает: [0, 3, 8]


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

Это круто! Я думаю, что я буду использовать это в следующий раз, когда я напишу Python. Я в первую очередь программист на Лисп и JS, а отсутствие многострочной лямбады вредит. Это способ получить это.
Кристофер Дюма

7

Я виновен в том, что практиковал этот грязный хак в некоторых из моих проектов, который немного проще:

    lambda args...:( expr1, expr2, expr3, ...,
            exprN, returnExpr)[-1]

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


6

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

map(lambda x:
      y = x+1
      z = x-1
      y*z,
    [1,2,3]))

возвращает функцию, которая возвращает (y*z, [1,2,3])

Но

map((lambda x:
      y = x+1
      z = x-1
      y*z)
    ,[1,2,3]))

средства

map(func, [1,2,3])

где func - многострочная лямбда, возвращающая y * z. Это работает?


1
Я думаю, что верхний должен возвращаться, map(func, [1,2,3])а нижний должен быть ошибкой, потому что у функции карты недостаточно аргументов. Также есть некоторые дополнительные скобки в коде.
Сэми Бенчериф

сброс этого в pycharm, выполняющий python2.7.13, приводит к синтаксической ошибке.
simbo1905

дополнительные скобки
Сэми Бенчериф

4

(Для всех, кто еще заинтересован в теме.)

Учтите это (включая даже использование возвращаемых значений операторов в дальнейших операторах внутри «многострочной» лямбды, хотя это уродливо до точки рвоты ;-)

>>> def foo(arg):
...     result = arg * 2;
...     print "foo(" + str(arg) + ") called: " + str(result);
...     return result;
...
>>> f = lambda a, b, state=[]: [
...     state.append(foo(a)),
...     state.append(foo(b)),
...     state.append(foo(state[0] + state[1])),
...     state[-1]
... ][-1];
>>> f(1, 2);
foo(1) called: 2
foo(2) called: 4
foo(6) called: 12
12

Это не работает при повторном вызове с другими параметрами и вызывает утечку памяти, если только первая строка не state.clear()указана, поскольку аргументы по умолчанию создаются только один раз при создании функции.
Мэтью Д. Шолфилд,

1

Вы можете просто использовать slash ( \), если у вас есть несколько строк для вашей лямбда-функции

Пример:

mx = lambda x, y: x if x > y \
     else y
print(mx(30, 20))

Output: 30

Вопрос касается использования более 1 выражения, а не более 1 буквенной строки.
Томас

1

Я начинаю с Python, но исходя из Javascript, наиболее очевидным способом является извлечение выражения как функции ....

Придуманный пример, выражение multiply (x*2)извлекается как функция, и поэтому я могу использовать multiline:

def multiply(x):
  print('I am other line')
  return x*2

r = map(lambda x : multiply(x), [1, 2, 3, 4])
print(list(r))

https://repl.it/@datracka/python-lambda-function

Может быть, это не дает точного ответа на вопрос, было ли это как сделать многострочное выражение в самом лямбда-выражении , но в случае, если кто-то получит эту ветку, смотрящую, как отлаживать выражение (как я), я думаю, что это поможет


2
Зачем мне это делать, а не просто писать map(multiply, [1, 2, 3])?
thothal

0

Что execкасается уродливых хаков, вы всегда можете использовать комбинацию и обычную функцию для определения многострочной функции, например:

f = exec('''
def mlambda(x, y):
    d = y - x
    return d * d
''', globals()) or mlambda

Вы можете обернуть это в функцию как:

def mlambda(signature, *lines):
    exec_vars = {}
    exec('def mlambda' + signature + ':\n' + '\n'.join('\t' + line for line in lines), exec_vars)
    return exec_vars['mlambda']

f = mlambda('(x, y)',
            'd = y - x',
            'return d * d')

0

Я просто немного поигрался, чтобы попытаться разобраться в словах с помощью Reduce, и придумал один взлом лайнера:

In [1]: from functools import reduce
In [2]: reduce(lambda d, i: (i[0] < 7 and d.__setitem__(*i[::-1]), d)[-1], [{}, *{1:2, 3:4, 5:6, 7:8}.items()])                                                                                                                                                                 
Out[3]: {2: 1, 4: 3, 6: 5}

Я просто пытался сделать то же самое, что было сделано в этом Javascript dict понимание: https://stackoverflow.com/a/11068265


0

Вот более интересная реализация многострочных лямбд. Этого невозможно достичь из-за того, что python использует отступы как способ структурирования кода.

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

Как уже отмечалось, вы можете написать свой код так:

lambda args: (expr1, expr2,... exprN)

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

Один из способов сделать это более многословным - это иметь

lambda args: [lambda1, lambda2, ..., lambdaN]

Где каждая лямбда получает аргументы от предыдущего.

def let(*funcs):
    def wrap(args):
        result = args                                                                                                                                                                                                                         
        for func in funcs:
            if not isinstance(result, tuple):
                result = (result,)
            result = func(*result)
        return result
    return wrap

Этот метод позволяет вам написать что-то похожее на схему / шрифт.

Таким образом, вы можете написать такие вещи:

let(lambda x, y: x+y)((1, 2))

Более сложный метод может быть использован для вычисления гипотенузы

lst = [(1,2), (2,3)]
result = map(let(
  lambda x, y: (x**2, y**2),
  lambda x, y: (x + y) ** (1/2)
), lst)

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

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


-3

потому что лямбда-функция должна быть однострочной, как ее самая простая форма функции, an entrance, then return


1
Нет, любой, кто пишет программу, управляемую событиями, скажет вам, что многострочная лямбда важна.
Akangka
Используя наш сайт, вы подтверждаете, что прочитали и поняли нашу Политику в отношении файлов cookie и Политику конфиденциальности.
Licensed under cc by-sa 3.0 with attribution required.