리스트 이해의 람다 함수


149

왜 다른 다음과 같은 두 가지 지능형리스트의 출력은, 비록입니다 flambda동일한 기능?

f = lambda x: x*x
[f(x) for x in range(10)]

[lambda x: x*x for x in range(10)]

둘 다 기억 type(f)하고 type(lambda x: x*x)같은 유형을 반환하십시오.


[lambda x: x*x for x in range(10)]외부 루프 함수를 반복적으로 호출하지 않기 때문에 첫 번째 것보다 빠릅니다.
riza

@Selinap : ... 아니, 루프를 통해 매번 새로운 기능을 사용하는 브랜드를 만들고 있습니다. ...이 새로운 기능을 생성하는 오버 헤드는 호출 속도가 느립니다 (어쨌든 내 시스템에서).
Gerrat

@ Gerrat : 오버 헤드가 있어도 여전히 빠릅니다. 그러나 물론 [x*x for x in range(10)]더 좋습니다.
riza

34
방금 구글 foobar 액세스를 얻기 위해 여기에 입력했습니다 :)
Gal Margalit

답변:


267

첫 번째 함수는 하나의 람다 함수를 만들고 10 번 호출합니다.

두 번째는 함수를 호출하지 않습니다. 10 가지 람다 함수를 만듭니다. 그것들을 모두 목록에 넣습니다. 처음과 동일하게 만들려면 :

[(lambda x: x*x)(x) for x in range(10)]

아니면 더 나은 :

[x*x for x in range(10)]

13
또는 map(lambda x: x*x, range(10))아마도 OP가 처음에 의미했던 것입니다.
Daniel Roseman

네, 람다 x : x * x .. (x)는 교리 인 것 같습니다.
staticor

λ는 X : X * X 내지 X (10)]를 하스켈에서 functur 기본적
모세 브 에리

@DanielRoseman, 또는 list(map(lambda x: x*x, range(10)))당신에게 줄 것이다[0, 1, 4, 9, 16, 25, 36, 49, 64, 81]
rrlamichhane

108

이 질문은 "유명한"및 "명백한"파이썬 구문의 매우 어려운 부분, 즉 우선 순위, 람다 또는 목록 이해력에 영향을줍니다.

OP의 목적은 0에서 9까지의 제곱 목록을 생성하는 것이 아니라고 생각합니다.이 경우 더 많은 솔루션을 제공 할 수 있습니다.

squares = []
for x in range(10): squares.append(x*x)
  • 이것은 명령형 구문의 좋은 방법입니다.

그러나 요점이 아닙니다. 요점은 W (hy) TF입니다.이 모호한 표현이 반 직관적입니까? 그리고 나는 마지막에 당신을위한 바보 사건을 가지고 있으므로, 너무 빨리 내 대답을 무시하지 마십시오 (나는 면접에서했습니다).

따라서 OP의 이해력은 람다 목록을 반환했습니다.

[(lambda x: x*x) for x in range(10)]

이것은 물론 제곱 함수의 10 가지 다른 사본입니다.

>>> [lambda x: x*x for _ in range(3)]
[<function <lambda> at 0x00000000023AD438>, <function <lambda> at 0x00000000023AD4A8>, <function <lambda> at 0x00000000023AD3C8>]

노트람다의 메모리 주소를 하십시오-모두 다릅니다!

물론이 표현의 "최적"(haha) 버전을 가질 수 있습니다.

>>> [lambda x: x*x] * 3
[<function <lambda> at 0x00000000023AD2E8>, <function <lambda> at 0x00000000023AD2E8>, <function <lambda> at 0x00000000023AD2E8>]

보다? 같은 시간 3 회 람다.

변수 _로 사용 했습니다 for. 그것은과는 아무 상관이 없습니다 x의를lambda (그것은 전적으로 가려한다!). 알 겠어요?

구문 우선 순위가 그다지 중요하지 않은 이유는 무엇입니까?

[lambda x: (x*x for x in range(10))]

이는 수 : [[0, 1, 4, ..., 81]]하거나 [(0, 1, 4, ..., 81)], 또는 내가 가장 논리적 찾을 수있는 ,이는 것 list(1 개) 요소의 - a는 generator값을 반환. 그것은 사실이 아니며, 언어는 이런 식으로 작동하지 않습니다.

하지만 만약에 ...

for변수 를 가리지 않고 lambdas 에서 사용 하면 어떻게됩니까 ???

그럼 쓰레기가 발생합니다. 이거 봐요:

[lambda x: x * i for i in range(4)]

이것은 물론 수단입니다.

[(lambda x: x * i) for i in range(4)]

그러나 그것은 의미하지 않습니다.

[(lambda x: x * 0), (lambda x: x * 1), ... (lambda x: x * 3)]

이건 미친 짓이야!

목록 이해의 람다는이 이해의 범위를 넘어서는 폐쇄입니다. 어휘 폐쇄, 그래서 그들은 참조 i참조를 통해, 그리고 그 값들은 평가 때!

따라서이 표현은 다음과 같습니다.

[(lambda x: x * i) for i in range(4)]

대략 다음과 같습니다.

[(lambda x: x * 3), (lambda x: x * 3), ... (lambda x: x * 3)]

파이썬 디 컴파일러를 사용하여 더 많은 것을 볼 수 있다고 확신합니다 (예를 들어 dis모듈을 의미 합니다).하지만 Python-VM과 무관 한 토론으로는 충분합니다. 면접 질문에 너무 많은.

이제 list승수 람다 를 만드는 방법은 실제로 연속 정수를 곱하는 것입니까? 받아 들인 대답과 마찬가지로 직접 이해 관계를 i다른 것으로 묶어서 목록 이해 표현식 에서 lambda호출 해야합니다.

전에:

>>> a = [(lambda x: x * i) for i in (1, 2)]
>>> a[1](1)
2
>>> a[0](1)
2

후:

>>> a = [(lambda y: (lambda x: y * x))(i) for i in (1, 2)]
>>> a[1](1)
2
>>> a[0](1)
1

(외부 람다 변수도 = i가 있었지만 이것이 더 명확한 해결책이라고 결정했습니다. y우리는 어느 마녀가 어느 마녀인지 알 수 있도록 소개했습니다 .)

2019-08-30 편집 :

@sheridp의 답변에도있는 @josoler의 제안에 따라 목록 이해 "루프 변수"의 값을 객체 내부에 "내장"할 수 있습니다. 키는 적시에 액세스해야합니다. 위의 "After"섹션은 다른 것으로 래핑 lambda하고 현재 값인 즉시 호출하여 수행합니다 i. 또 다른 방법은 (조금 더 읽기 쉬운- 'WAT'효과를 생성하지 않음) 객체 i내부 의 값을 저장 partial하고 "내부"(원본) lambda를 인수로 사용하는 것입니다.partial 로 사용하는 것입니다. 통화 시간), 즉 :

2 : 후

>>> from functools import partial
>>> a = [partial(lambda y, x: y * x, i) for i in (1, 2)]
>>> a[0](2), a[1](2)
(2, 4)

훌륭하지만 여전히 약간의 비틀기가 있습니다! 코드 리더에서 더 쉽게 만들고 싶지 않다고 말하고 요소를 키워드 인수로partial . 이름을 바꾸자 :

2.5 이후 :

>>> a = [partial(lambda coef, x: coef * x, coef=i) for i in (1, 2)]
>>> a[0](1)
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
TypeError: <lambda>() got multiple values for argument 'coef'

와트?

>>> a[0]()
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
TypeError: <lambda>() missing 1 required positional argument: 'x'

잠깐만 ... 인수 개수를 1 씩 바꾸고 "너무 많음"에서 "너무 작음"으로 바뀌고 있습니까?

음, 진짜 WAT는 우리가 통과 할 때, 아니다 coefpartial이 방법으로는 위치 후에 와야하므로,이 키워드 인수는,이된다 x과 같이, 인수 :

3시 이후

>>> a = [partial(lambda x, coef: coef * x, coef=i) for i in (1, 2)]
>>> a[0](2), a[1](2)
(2, 4)

중첩 된 람다보다 마지막 버전을 선호하지만 각 버전마다 선호합니다.


22
그것은 잔인하고 특이한 면접 질문입니다.
szeitlin

1
동료가 묻지 않았다면 아마이 답변을 찾지 못할 것입니다.
piggybox

8
와. 나는이 터무니없는 행동에 심하게 물렸다. 게시물 주셔서 감사합니다!
Ant Ant

1
훌륭한 답변입니다. 방금이 문제에 부딪 쳤습니다. 한편으로는 파이썬 제한을 가리 키지 만 다른 한편으로는 코드 냄새 표시기 일 수도 있습니다. 장난감 프로젝트에이 솔루션을 사용하고 있지만 프로덕션 환경에서 재구성하는 신호일 수 있습니다.
ahota

2
명확성과 완전성을 위해 다음과 같은 마지막 목록 이해를 작성할 수 있습니다.[partial(lambda i, x: i * x, i) for i in (1, 2)]
josoler

19

가장 큰 차이점은 첫 번째 예제는 실제로 lambda를 호출 f(x)하지만 두 번째 예제는 그렇지 않습니다.

첫 번째 예제는에 해당하고 [(lambda x: x*x)(x) for x in range(10)]두 번째 예제는에 해당합니다 [f for x in range(10)].


11

첫번째

f = lambda x: x*x
[f(x) for x in range(10)]

f()범위의 각 값에 대해 실행되므로 각 값에 f(x)대해 수행됩니다.

두 번째

[lambda x: x*x for x in range(10)]

목록의 각 값에 대해 람다를 실행하여 해당 함수를 모두 생성합니다.


11

사람들은 좋은 대답을했지만 내 의견으로는 가장 중요한 부분을 언급하는 것을 잊었습니다. 두 번째 예 X에서 목록 이해 Xlambda기능 은 기능 의 기능 과 동일하지 않으며 완전히 관련이 없습니다. 두 번째 예는 실제로 다음과 같습니다.

[Lambda X: X*X for I in range(10)]

내부 반복 range(10)은 목록에 10 개의 유사한 람다 함수 (10 개의 개별 함수이지만 완전히 유사 함-각 입력의 거듭 제곱 2를 반환 함) 만 생성합니다.

반면에 첫 번째 예제는 반복의 X가 결과와 상호 작용하기 때문에 완전히 다른 방식으로 작동합니다. 각 반복마다 값이 X*X있으므로 결과는 다음과 같습니다.[0,1,4,9,16,25, 36, 49, 64 ,81]


이것은 중요한 포인트입니다. 나는 당신을 공감하고 내 대답에 그것을 자세히 설명했다.
Tomasz Gandor

6

다른 대답은 정확하지만 나중에 다른 매개 변수를 가진 함수 목록을 만들려고하면 나중에 실행할 수 있습니다 . 다음 코드는이를 수행합니다.

import functools
a = [functools.partial(lambda x: x*x, x) for x in range(10)]

b = []
for i in a:
    b.append(i())

In [26]: b
Out[26]: [0, 1, 4, 9, 16, 25, 36, 49, 64, 81]

예제가 고안되었지만, 각각 다른 것을 인쇄하는 함수 목록을 원할 때 유용합니다.

import functools
a = [functools.partial(lambda x: print(x), x) for x in range(10)]

for i in a:
    i()
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.