파이썬에 여러 줄 람다가없는 이유는 무엇입니까?


335

멀티 라인 람다는 파이썬의 다른 구문 구문과 구문 적으로 충돌하기 때문에 파이썬에서 추가 할 수 없다고 들었습니다. 나는 오늘 버스에서 이것에 대해 생각하고 있었고 여러 줄 람다가 충돌하는 단일 파이썬 구조를 생각할 수 없다는 것을 깨달았습니다. 내가 그 언어를 아주 잘 알고 있기 때문에, 이것은 나를 놀라게했다.

이제 Guido가 언어에 여러 줄 람다를 포함시키지 않았지만 호기심에서 벗어날 이유가 있다고 확신합니다. 여러 줄 람다를 포함하는 것이 모호한 상황은 무엇입니까? 내가 들었던 것이 사실입니까, 아니면 파이썬이 여러 줄 람다를 허용하지 않는 다른 이유가 있습니까?


12
tl; dr 버전 : Python은 {} 블록이없는 게으른 언어이므로 일관된 구문 설계를 유지하기 위해 허용되지 않았습니다.
Andrew

11
또한 : 나는 대답에서 이것을 언급하지 않은 사람에 대해 완전히 놀랐습니다 ... 파이썬에서 \ 문자로 줄을 끝내고 다음 줄로 계속할 수 있습니다 ...이 정보는이 전체 질문을 대체합니다 ...
Andrew


"구문 디자인"
니콜라스

이를 위해서는 식 내부에 문을 허용해야합니다. 그렇게하려고한다면 lambda처음에는 표현이 필요하지 않습니다 . def표현식에 간단히 문장을 사용할 수 있습니다 .
chepner

답변:


153

다음을보십시오 :

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

이것이 람다 반환입니까 (y, [1,2,3])(따라서지도는 하나의 매개 변수 만 가져 오며 오류가 발생합니까)? 아니면 반환 y합니까? 아니면 새 줄의 쉼표가 잘못되어 구문 오류입니까? 파이썬은 당신이 원하는 것을 어떻게 알 수 있습니까?

괄호 안에서 들여 쓰기는 파이썬에 중요하지 않으므로 여러 줄로 명확하게 작업 할 수 없습니다.

이것은 단순한 것입니다. 아마도 더 많은 예가있을 것입니다.


107
람다에서 튜플을 반환하려는 경우 괄호를 사용해야 할 수 있습니다. IMO, 이것은 항상 그러한 모호성을 방지하기 위해 시행 되었어야하지만, 오.
mpen

26
이것은 이미 많은 장소에 존재하는 여분의 Parens 세트를 추가하여 해결 해야하는 간단한 모호성입니다. 예를 들어 다른 인수로 둘러싸인 생성기 표현식, 정수 리터럴에 대한 메소드 호출 함수 이름은 숫자로 시작할 수 없으며 물론 단일 행 람다 (여러 줄에 작성된 긴 표현 일 수 있음). 여러 줄 람다는 이러한 경우를 제외하고 보증해야한다는 점에서 특별히 다르지 않습니다. 이것이 실제 답변입니다.
nmclean

3
나는 프렛을 다루지 않는 가질 리언 언어가있는 방법을 좋아하지만 어떻게 든 그것이 불가능하지는 않더라도 매우 어려운 이유가 몇 가지 깊은 이유가있다
nicolas

1
@nicolas 그 한마디에의 파이썬
javadba

파이썬에서 저개발 된 람다를 사용하지 않는 이유.
NONAME

635

귀도 반 로섬 (파이썬 발명가) 은 오래된 블로그 게시물 에서이 정확한 질문에 스스로 대답합니다 .
기본적으로 그는 이론적으로는 가능하지만 제안 된 해결책은 피 토닉이 아니라고 인정합니다.

"하지만이 퍼즐에 대해 제안 된 솔루션의 복잡성은 엄청나 다. 파서 (또는 더 정확하게는 어휘 분석기)가 들여 쓰기에 민감하고 들여 쓰기에 민감하지 않은 모드를 전환 할 수 있어야한다. 기술적으로 모든 문제를 해결할 수 있습니다 (이미 일반화 할 수있는 들여 쓰기 수준 스택이 있습니다). 그러나 그 중 어느 것도 그것이 정교한 Rube Goldberg contraption이라고 생각하지 않습니다 . "


108
이것이 왜 정답이 아닌가? 그것은 기술적 이유에 관한 것이 아니라 발명가가 명확하게 언급 한 바와 같이 디자인 선택입니다.
Dan Abramov

13
OP가 아마 몇 년 동안 로그인하지 않았기 때문에 @ DanAbramov.
Falken 교수 계약은

7
루브 골드버그 참조를 이해하지 않은 사람들을 위해, 참조 : en.wikipedia.org/wiki/Rube_Goldberg_Machine
fjsj

56
귀도의 대답은 파이썬이 블록을 정의하기 위해 들여 쓰기에 의존하지 않기를 바라는 또 다른 이유입니다.
LS

25
나는 "장감"을 디자인 선택이라고 부를지 모르겠다. ;)
Elliot Cameron

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... 그것을 수행하는 것은 자바 스크립트입니다. 담쟁이가 벽에 꽂힌 것처럼 당신에게 뿌리를 내립니다. 나는 Xmas보다 이것을 잊어 버렸으면 좋겠다.
nigel222

아주 영리하고 읽기 쉽습니다. 지금-그 (누락 ..) 과제에 대해 ..
javadba

왜 부끄러워? 파이썬 언어는 근본적으로 불구가되어 -하지만 사용되는 하나 어쨌든 대부분을위한 데이터 과학 . 그래서 우리는 조정합니다. 부작용 (종종 단순히 인쇄 / 로깅!)과 과제 (종종 중간 변수에 충분 함)를 수행하는 방법을 찾는 것은 언어에 의해 잘 처리되어야합니다. 그러나 그들은 심지어 지원되지 않습니다 (적어도 PEP지침 을 따르는 경우 )
javadba

17

몇 가지 관련 링크 :

잠시 동안, 나는 처음에 Erlang을 기반으로 Ruby 블록과 함께 파이썬의 들여 쓰기 기반 구문을 갖게 될 Reia의 개발을 따르고있었습니다. 그러나 디자이너는 들여 쓰기 민감도를 포기하고이 결정에 대해 들여 쓰기 + 여러 줄 블록으로 인해 발생한 문제에 대한 토론과 Guido의 디자인 문제 / 결정에 대해 얻은 감사에 대한 토론이 포함되었습니다.

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

또한, 파이썬에서 루비 스타일 블록에 대한 흥미로운 제안이 있습니다. Guido가 실제로 그것을 쏘지 않고 응답을 게시하는 곳을 가로 질러 뛰어 왔습니다.

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


12

[편집] 이 답변을 읽으십시오 . 여러 줄 람다가 왜 아닌지 설명합니다.

간단히 말해, 그것은 비유 론적입니다. Guido van Rossum의 블로그 게시물에서 :

식 중간에 들여 쓰기 기반 블록을 포함하는 솔루션을 사용할 수 없습니다. 명령문 그룹화 (예 : 중괄호 또는 시작 / 종료 키워드)에 대한 대체 구문도 똑같이 용납 할 수 없으므로 여러 줄 람다를 해결할 수없는 퍼즐로 만듭니다.


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]



대단해! 나는 다음에 파이썬을 쓸 때 이것을 사용할 것이라고 생각한다. 나는 주로 Lisp와 JS 프로그래머이며 멀티 라인 람다가 부족합니다. 이것이 그것을 얻는 방법입니다.
Christopher Dumas

7

내 프로젝트 중 일부 에서이 더러운 핵을 연습하는 것이 유죄입니다.

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

나는 pythonic을 유지할 수있는 방법을 찾을 수 있기를 바랍니다. 그러나 그렇게해야한다면 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])지도 함수에 충분한 인수가 없기 때문에 맨 위가 반환 되고 맨 아래가 오류 여야한다고 생각합니다. 또한 코드에는 추가 괄호가 있습니다.
Samy Bencherif

python2.7.13을 실행하는 pycharm에 삭제하면 구문 오류가 발생합니다.
simbo1905

추가 괄호
Samy Bencherif

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()함수를 만들 때 기본 인수가 한 번만 만들어지기 때문에 첫 번째 줄이 없으면 메모리 누수 가 발생합니다.
Matthew D. Scholefield

1

\람다 함수에 여러 줄이있는 경우 슬래시 ( ) 를 사용할 수 있습니다

예:

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

Output: 30

문제는 하나 이상의 리터럴 행이 아닌 둘 이상의 표현식을 사용하는 것과 관련이 있습니다.
Tomas Zubiri

1

나는 파이썬으로 시작하지만 자바 스크립트에서 오는 가장 명백한 방법은 표현식을 함수로 추출하는 것입니다.

예를 들어 곱셈 식은 (x*2)함수로 추출되므로 여러 줄을 사용할 수 있습니다.

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])?
치열

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로 dict comprehension을 만들기 위해 조금 놀고 있었고이 하나의 라이너 해킹을 생각해 냈습니다.

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 comprehension에서 수행 한 것과 똑같은 일을하려고했습니다 .https : //stackoverflow.com/a/11068265


0

다음은 멀티 라인 람다의보다 흥미로운 구현입니다. 파이썬이 코드를 구조화하는 방법으로 들여 쓰기를 사용하는 방식으로 인해 달성 할 수 없습니다.

그러나 운 좋게도 배열과 괄호를 사용하여 들여 쓰기 서식을 비활성화 할 수 있습니다.

이미 지적했듯이 다음과 같이 코드를 작성할 수 있습니다.

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

이 방법을 사용하면 약간 lisp / scheme 같은 것을 작성할 수 있습니다.

따라서 다음과 같이 쓸 수 있습니다.

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
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.