programing

Python에 Multiline Lambda 없음:왜 안 되나요?

javaba 2022. 10. 6. 21:26
반응형

Python에 Multiline Lambda 없음:왜 안 되나요?

Python에서는 여러 줄의 람다를 추가할 수 없다고 들었습니다. 왜냐하면 그것들은 Python의 다른 구문과 구문적으로 충돌하기 때문입니다.오늘 버스에서 이런 생각을 하다가 Python이 여러 줄의 람다와 충돌하는 구조를 생각해 낼 수 없다는 것을 깨달았습니다.내가 그 언어를 꽤 잘 알고 있다는 것을 고려하면, 이것은 나를 놀라게 했다.

Guido가 언어에 여러 줄의 람다를 포함하지 않는 이유가 있었을 것입니다만, 궁금해서입니다.여러 줄의 람다를 포함시키는 것이 애매한 상황은 무엇일까요?제가 들은 말이 사실인가요, 아니면 파이썬이 여러 줄의 람다를 허용하지 않는 다른 이유가 있나요?

Guido van Rossum(Python의 발명가)은 오래된 블로그 포스트에서 직접 이 질문에 답했습니다.
기본적으로, 그는 이론적으로는 가능하지만 제안된 모든 솔루션은 비-피톤적이라는 것을 인정합니다.

그러나 이 퍼즐에 대해 제안된 솔루션의 복잡성은 매우 큽니다.파서(더 정확히는 렉서)가 들여쓰기 감응 모드와 들여쓰기 감응 모드를 왔다 갔다 할 수 있어야 하며 이전 모드와 식별 수준을 유지할 수 있습니다.기술적으로 이 모든 것을 해결할 수 있습니다(이미 일반화될 수 있는 들여쓰기 수준이 쌓여 있습니다).하지만 이 모든 것이 Rube Goldberg의 정교한 장치라는 직감을 떨쳐버릴 수는 없습니다.

다음 사항을 확인합니다.

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

리턴인가요?(y, [1,2,3]) '돌아가다됩니까?y아니면 새 줄의 콤마가 잘못되어 구문 오류가 발생한 것입니까?이톤 이가 가하 하하 하? ???

parens 내에서는 python에게 들여쓰기는 중요하지 않기 때문에 multilines에 대해 명확하게 작업할 수 없습니다.

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

이것은 일반적으로 매우 보기 흉합니다(단, 경우에 따라서는 다른 방법이 더 보기 흉할 수 있습니다).따라서 회피책은 다음과 같이 중괄호 식을 작성하는 것입니다.

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

하지만 어떤 할당도 받지 않기 때문에 미리 자료를 준비해야 합니다.이것이 도움이 되는 것은 PySide 포장지입니다.그곳에서는, 콜백이 짧은 경우가 있습니다.추가 멤버 기능을 쓰는 것은 더 보기 흉할 수 있습니다.보통은 이게 필요 없을 거예요.

예제:

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

관련 링크:

한동안 저는 레이아의 개발을 따라가고 있었습니다.처음에는 Python의 들여쓰기 기반 구문과 Ruby 블록이 모두 Erlang 위에 있을 예정이었습니다.그러나 디자이너는 결국 들여쓰기 감도를 포기하게 되었고, 그가 이 결정에 대해 쓴 이 게시물에는 들여쓰기 + 여러 줄 블록과 관련된 문제에 대한 토의와 Guido의 디자인 문제/결정에 대한 인식 증대가 포함되어 있습니다.

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

또한 Python의 Ruby 스타일 블록에 대한 흥미로운 제안이 있습니다.Guido가 실제로 그것을 격추하지 않고 응답을 투고하고 있습니다(다만, 그 후의 격추에 대해서는 확실하지 않습니다).

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

영광스럽지만 무서운 해킹을 하나 보여드리죠

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]

나는 내 프로젝트 중 일부에서 좀 더 간단한 이 더러운 해킹을 연습한 것에 대해 죄책감을 느끼고 있다.

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

나는 네가 비단뱀처럼 지낼 수 있는 방법을 찾길 바라. 하지만 만약 네가 이것을 집행부를 사용하고 지구본을 조작하는 것보다 덜 고통스럽게 해야 한다면.

[편집] 이 질문은 12년이 지나도 계속 활성화 되어 있기 때문에저는 4년 정도마다 답변을 수정하는 전통을 이어갈 것입니다.

첫 번째 질문은 여러 줄의 람다가 Python과 어떻게 충돌하는가 하는 것이었습니다.승인된 답변은 간단한 예시로 방법을 보여줍니다.몇 년 전에 링크한 높은 평가를 받은 답변은 "왜 Python의 일부가 아닌가?"라는 질문에 대한 답변입니다. 이 답변은 기존의 "clashing" 예로는 Python에서 여러 줄의 lambda를 구현할 수 없다고 생각하는 사람들에게 더 만족스러울 수 있습니다.

이 답변의 이전 반복에서는 Python에 여러 줄의 lamda를 그대로 구현하는 방법에 대해 설명했습니다.그 후 그 부분은 삭제했습니다.왜냐하면 나쁜 관행들이 산재해 있었기 때문입니다.필요에 따라서, 이 회답의 편집 이력에 표시됩니다.

하지만 "왜 안 돼?"라는 대답은 "로섬이 말했으니까"라는 것이 여전히 좌절의 원인이 될 수 있다.다음으로 사용자 balpha에 의해 제시된 카운터 예에 따라 설계할 수 있는지 알아보겠습니다.

map(lambda x:
        y=x+1 # <-- this line defines the outmost indent level*
        for i in range(12):
            y+=12
        return y
   , [1,2,3])

#*By convention it is always one-indent past the 'l' in lambda

반환값에 대해서는 python에서는 다음 항목을 사용할 수 없습니다.

def f():
  return 3
, [1,2,3]

따라서 동일한 논리에 따라 "1, 2, 3"은 반환값의 일부가 되지 않아야 합니다.대신, 다음과 같이 해 봅시다.

map(lambda x:
        y=x+1                    # part of lambda block
        for i in range(12):      # part of lambda block
            y+=12                # part of lambda block
        return y, [1,2,3])       # part of lambda block

이것은 좀 더 까다롭지만 람다 블록은 명확하게 정의된 시작(토큰 '람다')이 있지만 명확한 끝은 없기 때문에 람다 블록의 일부와 같은 줄에 있는 모든 것도 람다 블록의 일부라고 생각합니다.

닫힌 괄호 또는 둘러싸인 요소에 의해 예상되는 토큰 수에 따라 추론을 식별할 수 있는 기능을 생각할 수 있습니다.일반적으로 위의 표현은 해석하기가 완전히 불가능하다고 생각되지 않지만 다소 어려울 수 있습니다.

단순화하기 위해 블록의 일부가 아닌 모든 문자를 분리할 수 있습니다.

map(lambda x:
        y=x+1                    # part of lambda block
        for i in range(12):      # part of lambda block
            y+=12                # part of lambda block
        return y                 # part of lambda block
, [1,2,3]) # argument separator, second argument, and closing paren for map

원래 위치로 돌아가면 마지막 줄이 람다 블록의 가장 낮은 들여쓰기 깊이 뒤에 있기 때문에 이번에는 명확합니다.단일 라인 람다는 현재와 동일하게 동작하는 특수한 경우(컬러 뒤에 바로 새 라인이 없는 것으로 확인됨)입니다.

이것이 꼭 Python의 일부가 되어야 한다고 말하는 것은 아니지만, 이것은 아마도 언어의 일부 변경으로 가능한 빠른 삽화이다.

[편집] 이거 읽어주세요여러 줄의 람다가 왜 문제가 되지 않는지를 설명합니다.

간단히 말해서, 비조화적이야.Guido van Rossum 블로그 투고:

식 중간에 들여쓰기 기반 블록을 삽입하는 솔루션은 허용되지 않습니다.문장 그룹화에 대한 대체 구문(예: 중괄호 또는 시작/끝 키워드)을 동일하게 사용할 수 없기 때문에 여러 줄의 람다는 거의 해결할 수 없는 퍼즐이 됩니다.

다른 해결책에 대해서도 의견을 개진하겠습니다.

단순한 한 줄 람다는 일반 함수와 어떻게 다릅니까?할당이 부족하거나 루프와 같은 구성(for, while), try-except 절만 생각할 수 있습니다.그게 다에요?심지어 삼진 연산자도 있어 - 멋지다!자, 그럼 이 문제들 하나하나에 대해 알아보도록 하겠습니다.

과제들

어떤 lisp를 했습니다.let로컬 바인딩을 허용합니다.실제로 상태를 변경하지 않는 모든 할당은 다음 항목에서만 수행할 수 있습니다.let하지만 모든 lisp 프로그래머들은 알고 있다.let형식은 람다 함수를 호출하는 것과 절대적으로 동일합니다.즉,

(let ([x_ x] [y_ y])
  (do-sth-with-x-&-y x_ y_))

와 같다

((lambda (x_ y_)
   (do-sth-with-x-&-y x_ y_)) x y)

그래서 람다는 충분하고도 남습니다!새로운 과제를 만들고 싶을 때마다 람다를 하나 더 추가하고 호출합니다.다음 예를 생각해 보겠습니다.

def f(x):
    y = f1(x)
    z = f2(x, y)
    return y,z

람다 버전은 다음과 같습니다.

f = lambda x: (lambda y: (y, f2(x,y)))(f1(x))

심지어 이 모든 것을let기능(데이터에 대한 작업 후에 데이터가 기록되는 것을 원하지 않을 경우)을 선택합니다.그리고 더 많은 괄호를 넣기 위해 카레도 할 수 있습니다.

let = curry(lambda args, f: f(*args))
f_lmb = lambda x: let((f1(x),), lambda y: (y, f2(x,y)))
# or:
f_lmb = lambda x: let((f1(x),))(lambda y: (y, f2(x,y)))

# even better alternative:
let = lambda *args: lambda f: f(*args)
f_lmb = lambda x: let(f1(x))(lambda y: (y, f2(x,y)))

아직까지는 좋아.하지만 우리가 다시 할당해야 한다면, 즉, 상태를 바꿔야 한다면?문제가 되는 일이 루프에 관계되지 않는 한, 우리는 상태를 바꾸지 않고 절대적으로 행복하게 살 수 있다고 생각합니다.

루프

루프에 대한 직접적인 대체 기능은 없지만, 우리의 요구에 맞는 꽤 일반적인 함수를 작성할 수 있다고 생각합니다.다음 fibonacci 함수를 확인합니다.

def fib(n):
    k = 0
    fib_k, fib_k_plus_1 = 0, 1
    while k < n:
        k += 1
        fib_k_plus_1, fib_k = fib_k_plus_1 + fib_k, fib_k_plus_1
    return fib_k

람다에 관해서는 불가능하죠.하지만 조금이나마 유용한 함수를 작성하면 다음과 같은 경우 처리가 완료됩니다.

def loop(first_state, condition, state_changer):
    state = first_state
    while condition(*state):
        state = state_changer(*state)
    return state

fib_lmb = lambda n:\
            loop(
              (0,0,1),
              lambda k, fib_k, fib_k_plus_1:\
                k < n,
              lambda k, fib_k, fib_k_plus_1:\
                (k+1, fib_k_plus_1, fib_k_plus_1 + fib_k))[1]

그리고 물론, 항상 이 기능을 사용하는 것을 고려해야 합니다.map,reduce기타 고차적 기능(가능한 경우)을 제공합니다.

시험 예외 및 기타 제어 구조

이러한 문제에 대한 일반적인 접근법은 코드 블록을 인수를 받지 않는 람다로 대체하여 느린 평가를 사용하는 것으로 보입니다.

def f(x):
    try:    return len(x)
    except: return 0
# the same as:
def try_except_f(try_clause, except_clause):
    try: return try_clause()
    except: return except_clause()
f = lambda x: try_except_f(lambda: len(x), lambda: 0)
# f(-1) -> 0
# f([1,2,3]) -> 3

물론 이것은 try-except 절을 완전히 대체하는 것은 아니지만 항상 보다 일반적인 절로 만들 수 있습니다.그나저나, 그 접근법으로는if기능처럼 행동하라!

요약: 언급된 모든 것이 다소 부자연스럽고 그다지 아름답지 않게 느껴지는 것은 당연하다.그래도 - 효과가 있어!그리고 아무것도 없이evals그리고 다른 진부한 기술들도 모두 작동하게 될 거야.나는 또한 당신이 이것을 어디에나 사용해야 한다고 주장하는 것이 아니다.대부분의 경우 일반적인 함수를 정의하는 것이 좋습니다.나는 불가능한 것은 없다는 것을 보여주었다.

@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를 반환하는 여러 줄의 람다입니다.그일 어떻게 되가요?

(토픽에 아직 관심이 있는 분들을 위해)

이 점을 고려하십시오(구토할 정도로 추하지만 "멀티라인" 람다 내의 추가 문장에서 문장의 반환 값 사용도 포함;-).

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

여기 여러 라인 람다의 더 흥미로운 구현이 있습니다.python이 코드를 구조화하기 위한 방법으로 indents를 사용하는 방법 때문에 달성할 수 없습니다.

그러나 다행히도 배열과 괄호를 사용하여 들여쓰기 형식을 해제할 수 있습니다.

이미 지적된 바와 같이 코드를 다음과 같이 작성할 수 있습니다.

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)

그러면 스칼라 번호 목록이 반환되므로 여러 값을 1로 줄이는 데 사용할 수 있습니다.

람다를 그렇게 많이 보유하는 것은 확실히 효율적이지 않지만, 제약이 있는 경우에는 즉시 작업을 완료하고 나중에 실제 기능으로 다시 쓰는 것이 좋습니다.

Python 3.8/3.9에는 Assignment Expression이 있어 람다에서 사용할 수 있어 기능이 대폭 확장되었습니다.

예: 코드

#%%
x = 1
y = 2

q = list(map(lambda t: (
    tx := t*x,
    ty := t*y,
    tx+ty
)[-1], [1, 2, 3]))

print(q)

[3, 6, 9]를 인쇄합니다.

할 수 .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')

질문인 , 이라고 할 수 있습니다.lambda어떤 콜의 결과가 다른 콜에 의해 소비되는 문제.

표준 라이브러리 기능만을 기반으로 둔더 방식을 사용하지 않기 때문에 슈퍼 해킹이 아니길 바랍니다.

.x = 3 다음 첫 에 '어느 정도'를 붙입니다.1 두 에 '이렇게'를 붙입니다.26출력으로 사용합니다.

from functools import reduce

reduce(lambda data, func: func(data), [
    lambda x: x + 1,
    lambda x: x + 2
], 3)

## Output: 6

나는 조금 놀다가 리덕션으로 이해하려고 했는데, 라이너 해킹을 하나 생각해 냈다.

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

슬래시슬래시)를 .\) 함수에

예제:

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

Output: 30

저는 python으로 시작하지만 Javascript에서 나오는 가장 확실한 방법은 함수로서의 표현을 추출하는 것입니다.

식 「」 「」, 「」를 곱합니다.(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/http-data-function

그것이 람다 표현식 자체의 멀티라인 처리 방법이었는지에 대한 질문에 정확히 대답하지 못할 수도 있지만, 누군가 (나처럼) 표현식을 디버깅하는 방법을 보면서 이 스레드를 얻었을 때 도움이 될 것 같습니다.

람다 항목 간에 변수를 여러전달하는 안전한 방법 중 하나:

print((lambda: [
    locals().__setitem__("a", 1),
    locals().__setitem__("b", 2),
    locals().__setitem__("c", 3),
    locals().get("a") + locals().get("b") + locals().get("c")
])()[-1])

★★★★★6

Python 3.8 이후 로컬바인딩을 위한 다른 방법이 있습니다.

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

루프의 경우

lambda x: (
    y := x ** 2,
    [y := y + x for _ in range(10)],
    y
)[-1]

브런치인 경우

lambda x: (
    y := x ** 2,
    x > 5 and [y := y + x for _ in range(10)],
    y
)[-1]

또는

lambda x: (
    y := x ** 2,
    [y := y + x for _ in range(10)] if x > 5 else None,
    y
)[-1]

While Loop(루프 중)

import itertools as it
lambda x: (
    l := dict(y = x ** 2),
    cond := lambda: l['y'] < 100,
    body := lambda: l.update(y = l['y'] + x),
    *it.takewhile(lambda _: cond() and (body(), True)[-1], it.count()),
    l['y']
)[-1]

또는

import itertools as it
from types import SimpleNamespace as ns
lambda x: (
    l := ns(y = x ** 2),
    cond := lambda: l.y < 100,
    body := lambda: vars(l).update(y = l.y + x),
    *it.takewhile(lambda _: cond() and (body(), True)[-1], it.count()),
    l.y
)[-1]

또는

import itertools as it
lambda x: (
    y := x ** 2,
    *it.takewhile(lambda t: t[0],
    ((
    pred := y < 100,
    pred and (y := y + x))
    for _ in it.count())),
    y
)[-1]

는 가장 한 함수로서 한로 되어 하기 에, 함수의 간단한 로서, 람다 함수는 한 줄로 되어 있어야 .an entrance, then return

언급URL : https://stackoverflow.com/questions/1233448/no-multiline-lambda-in-python-why-not

반응형