Нет Multiline Lambda в Python: Почему бы и нет?

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

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

Ответ 1

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

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

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

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

Это просто, возможно, больше примеров.

Ответ 2

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

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

Ответ 3

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

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

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

Пример:

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

Ответ 4

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

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

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

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

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

Ответ 5

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

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

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

Ответ 6

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

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]

Ответ 7

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

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. Это работает?

Ответ 8

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

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

>>> 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

Ответ 9

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

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

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

Ответ 10

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

Пример:

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

Output: 30

Ответ 11

Что касается уродливых хаков, вы всегда можете использовать комбинацию 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')

Ответ 12

Я просто немного поиграл, чтобы попытаться разобраться в словах с помощью 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-толковании: fooobar.com/questions/225926/...

Ответ 13

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