목록 이해 vs지도


732

map()오버리스트 이해 를 선호 하거나 그 반대 의 이유가 있습니까? 그들 중 하나가 다른 것보다 일반적으로 더 효율적이거나 일반적으로 더 많은 파이썬으로 간주됩니까?


8
리스트 이해 대신 맵을 사용하면 PyLint가 경고 메시지 W0141을 참조하십시오 .
lumbric

2
@lumbric, 확실하지 않지만 람다가 맵에 사용되는 경우에만 수행됩니다.
0xc0de

답변:


660

map경우에 따라 현미경으로 더 빠를 수 있습니다 (목적을 위해 람다를 만들지 않지만 map과 listcomp에서 동일한 기능을 사용하는 경우). 다른 경우에는 목록 이해가 더 빠를 수 있으며 대부분의 pythonista는 더 직접적이고 명확하게 생각합니다.

정확히 동일한 기능을 사용할 때 맵의 작은 속도 이점의 예 :

$ python -mtimeit -s'xs=range(10)' 'map(hex, xs)'
100000 loops, best of 3: 4.86 usec per loop
$ python -mtimeit -s'xs=range(10)' '[hex(x) for x in xs]'
100000 loops, best of 3: 5.58 usec per loop

지도에 람다가 필요할 때 성능 비교가 완전히 역전되는 예 :

$ python -mtimeit -s'xs=range(10)' 'map(lambda x: x+2, xs)'
100000 loops, best of 3: 4.24 usec per loop
$ python -mtimeit -s'xs=range(10)' '[x+2 for x in xs]'
100000 loops, best of 3: 2.32 usec per loop

39
그러나 실제로 작업중 인 내부 파이썬 스타일 가이드는 명시 적으로 목록 및 필터와 비교하여 목록 구성을 재구성합니다 (작지만 측정 가능한 성능 개선 맵은 언급 할 수 없지만 ;-).
Alex Martelli

46
Alex의 무한 스타일 포인트를 kibash하지 말고 때로는 map이 더 읽기 쉽습니다. data = map (str, some_list_of_objects). 다른 어떤 것들 ... operator.attrgetter, operator.itemgetter 등
그레그 린드

57
map(operator.attrgetter('foo'), objs)보다 읽기 쉽다 [o.foo for o in objs]?!
Alex Martelli 18

52
@ 알렉스 : 나는 o여기 처럼 불필요한 이름을 소개하지 않는 것을 선호 하며, 당신의 예는 이유를 보여줍니다.
리드 바튼

29
그러나 @GreggLind는 그의 str()예제 와 함께 요점을 가지고 있다고 생각합니다 .
Eric O Lebigot

474

사례

  • 일반적인 경우 : 거의 항상, 당신은 목록 이해를 사용하고자 할 것입니다 파이썬 것입니다. 코드를 읽는 초보자 프로그래머에게 무엇을하고 있는지 분명하기 때문입니다. (이것은 다른 관용구가 적용될 수있는 다른 언어에는 적용되지 않습니다.) 목록 이해가 파이썬에서 사실상 반복되는 표준이기 때문에 파이썬 프로그래머에게하고있는 일이 더 분명 할 것입니다. 그들은 기대된다 .
  • 덜 일반적인 경우 : 그러나 이미 정의 된 함수 가있는 경우 map'unpythonic'으로 간주되지만 사용하는 것이 합리적 입니다. 예를 들어, map(sum, myLists)보다 우아하고 간결합니다 [sum(x) for x in myLists]. 반복하기 위해 두 번 입력 해야하는 더미 변수 (예 : sum(x) for x...또는 sum(_) for _...또는 sum(readableName) for readableName...)를 구성하지 않아도되는 우아함을 얻습니다 . 같은 주장이filter 하고 reduce과에서 아무것도 itertools모듈 : 이미 함수가 편리한 경우, 당신이 가서 어떤 기능을 프로그래밍 할 수 있습니다. 이것은 어떤 상황에서는 가독성을 얻고 다른 상황에서는 읽지 못합니다 (예 : 초보 프로그래머, 여러 주장) ...하지만 코드의 가독성은 어쨌든 주석에 크게 의존합니다.
  • 거의 절대 : map함수 프로그래밍을 할 때, 매핑 map또는 카레 를하는 동안 함수로서 순수한 추상 함수로 함수 를 사용하고 싶거나 함수로서 map이야기하는 map것으로 부터 이점을 얻을 수 있습니다. 예를 들어 Haskell에서 functor 인터페이스는 fmap일반 데이터 구조에 대한 매핑을 일반화합니다. 파이썬 문법은 생성자 스타일을 사용하여 반복에 대해 이야기하도록 강요하기 때문에 파이썬에서는 매우 드물다. 쉽게 일반화 할 수 없습니다. (이것은 때때로 좋고 나쁘다.) 당신은 아마 map(f, *lists)합리적인 일인 희귀 한 파이썬 예제 를 생각 해낼 수있다. 내가 올릴 수있는 가장 가까운 예 sumEach = partial(map,sum)는 다음과 매우 비슷한 하나의 라이너입니다.

def sumEach(myLists):
    return [sum(_) for _ in myLists]
  • 그냥 for -loop 사용 : 물론 for-loop를 사용할 수도 있습니다. 함수형 프로그래밍 관점에서 볼 때 우아하지는 않지만 때로는 로컬이 아닌 변수는 파이썬과 같은 명령형 프로그래밍 언어에서 코드를 더 명확하게 만듭니다. 사람들은 그런 방식으로 코드를 읽는 데 매우 익숙하기 때문입니다. For 루프는 일반적으로 목록 이해와 같은 목록을 작성하지 않고 복잡한 작업을 수행하고 맵이 (예 : 합산 또는 트리 만들기 등) 최적화되는 경우에만 가장 효율적입니다. 기억력 측면에서 효율적입니다 (필요한 병리학 적 가비지 수집 딸꾹질을 제외하고 최악의 일정한 요인을 예상 할 수있는 시간 측면에서는 반드시 그런 것은 아닙니다).

"파이썬주의"

나는 pythonic이 항상 내 눈에 우아하다는 것을 알지 못하기 때문에 "pythonic"이라는 단어를 싫어합니다. 그럼에도 불구 map하고 filter매우 유용 등과 유사한 기능 (itertools 모듈) 아마 스타일의 측면에서 unpythonic 간주됩니다.

게으름

대부분의 함수형 프로그래밍 구문과 같이 효율성 측면에서 MAP CAN BE LAZY 는 실제로 파이썬에서는 게으르다. 즉, python3 에서이 작업을 수행 할 수 있으며 컴퓨터에 메모리가 부족하지 않고 저장하지 않은 모든 데이터가 손실됩니다.

>>> map(str, range(10**100))
<map object at 0x2201d50>

목록 이해력으로 시도해보십시오.

>>> [str(n) for n in range(10**100)]
# DO NOT TRY THIS AT HOME OR YOU WILL BE SAD #

리스트 이해도 본질적으로 게으르지 만, 파이썬은 그것들을 비 게으른 것으로 구현하기로 선택했습니다 . 그럼에도 불구하고, 파이썬은 다음과 같이 생성기 표현식의 형태로 게으른 목록 이해를 지원합니다.

>>> (str(n) for n in range(10**100))
<generator object <genexpr> at 0xacbdef>

기본적으로 [...]구문을 생성자와 같은 목록 생성자에 전달하는 것으로 생각할 수 있습니다 list(x for x in range(5)).

간단한 예

from operator import neg
print({x:x**2 for x in map(neg,range(5))})

print({x:x**2 for x in [-y for y in range(5)]})

print({x:x**2 for x in (-y for y in range(5))})

목록 이해는 지연되지 않으므로 더 많은 메모리가 필요할 수 있습니다 (생성자 이해를 사용하지 않는 한). 대괄호 [...]는 특히 괄호가 엉망 일 때 특히 명확합니다. 반면에 때로는 타이핑하는 것처럼 장황하게 보일 수 [x for x in...있습니다. 반복자 변수를 짧게 유지하는 한 일반적으로 코드를 들여 쓰지 않으면 목록 이해가 더 명확 해집니다. 그러나 항상 코드를 들여 쓸 수 있습니다.

print(
    {x:x**2 for x in (-y for y in range(5))}
)

또는 헤어지다 :

rangeNeg5 = (-y for y in range(5))
print(
    {x:x**2 for x in rangeNeg5}
)

Python3의 효율성 비교

map 이제 게으르다 :

% python3 -mtimeit -s 'xs=range(1000)' 'f=lambda x:x' 'z=map(f,xs)'
1000000 loops, best of 3: 0.336 usec per loop            ^^^^^^^^^

따라서 모든 데이터를 사용하지 않거나 필요한 데이터 양을 미리 알지 못하면 mappython3 (및 python2 또는 python3의 생성자 표현식)에서 필요한 마지막 순간까지 값을 계산하지 않아도됩니다. 일반적으로 이것은을 사용하는 것보다 오버 헤드보다 중요합니다 map. 단점은 대부분의 기능적 언어와 달리 파이썬에서 매우 제한적이라는 것입니다. 파이썬 생성기 표현식은 순서 만 평가할 수 있기 때문에 왼쪽에서 오른쪽으로 "순서대로"데이터에 액세스하는 경우에만이 이점을 얻을 수 있습니다 x[0], x[1], x[2], ....

그러나 이제 우리는 미리 만들어진 기능이 있다고 가정 해 봅시다 f우리가 원하는을 map, 우리가의 게으름을 무시하고 map즉시로 평가를 강제로 list(...). 우리는 매우 흥미로운 결과를 얻습니다 :

% python3 -mtimeit -s 'xs=range(1000)' 'f=lambda x:x' 'z=list(map(f,xs))'                                                                                                                                                
10000 loops, best of 3: 165/124/135 usec per loop        ^^^^^^^^^^^^^^^
                    for list(<map object>)

% python3 -mtimeit -s 'xs=range(1000)' 'f=lambda x:x' 'z=[f(x) for x in xs]'                                                                                                                                      
10000 loops, best of 3: 181/118/123 usec per loop        ^^^^^^^^^^^^^^^^^^
                    for list(<generator>), probably optimized

% python3 -mtimeit -s 'xs=range(1000)' 'f=lambda x:x' 'z=list(f(x) for x in xs)'                                                                                                                                    
1000 loops, best of 3: 215/150/150 usec per loop         ^^^^^^^^^^^^^^^^^^^^^^
                    for list(<generator>)

결과는 AAA / BBB / CCC 형식이며 여기서 A는 python 3.?.?가 포함 된 2010 년 1 월 Intel 워크 스테이션에서 수행되고 B와 C는 python 3.2.1이 포함 된 2013 년경 AMD 워크 스테이션에서 수행됩니다. 매우 다른 하드웨어로. 결과적으로지도 및 목록 이해력은 성능면에서 비슷하며 다른 임의의 요인에 의해 가장 큰 영향을받습니다. 우리는 지능형리스트 기대하면서 우리가 말할 수있는 유일한 것은, 이상한, 그 것으로 보인다 [...]발전기 표현보다 더 잘 수행하기 위해 (...), map발전기 표현 (다시 모든 값이 / 평가 사용된다고 가정) 것이 더 효율적입니다.

이러한 테스트는 매우 간단한 기능 (식별 기능)을 가정한다는 것을 인식하는 것이 중요합니다. 그러나 기능이 복잡하면 프로그램의 다른 요소와 비교하여 성능 오버 헤드가 무시할 수 있기 때문에 이것은 좋습니다. (와 같은 다른 간단한 것들로 테스트하는 것은 여전히 ​​흥미로울 수 있습니다 f=lambda x:x+x)

파이썬 어셈블리를 읽는 데 능숙하다면, dis모듈을 사용하여 실제로 그 장면이 어떻게 진행되고 있는지 확인할 수 있습니다.

>>> listComp = compile('[f(x) for x in xs]', 'listComp', 'eval')
>>> dis.dis(listComp)
  1           0 LOAD_CONST               0 (<code object <listcomp> at 0x2511a48, file "listComp", line 1>) 
              3 MAKE_FUNCTION            0 
              6 LOAD_NAME                0 (xs) 
              9 GET_ITER             
             10 CALL_FUNCTION            1 
             13 RETURN_VALUE         
>>> listComp.co_consts
(<code object <listcomp> at 0x2511a48, file "listComp", line 1>,)
>>> dis.dis(listComp.co_consts[0])
  1           0 BUILD_LIST               0 
              3 LOAD_FAST                0 (.0) 
        >>    6 FOR_ITER                18 (to 27) 
              9 STORE_FAST               1 (x) 
             12 LOAD_GLOBAL              0 (f) 
             15 LOAD_FAST                1 (x) 
             18 CALL_FUNCTION            1 
             21 LIST_APPEND              2 
             24 JUMP_ABSOLUTE            6 
        >>   27 RETURN_VALUE

 

>>> listComp2 = compile('list(f(x) for x in xs)', 'listComp2', 'eval')
>>> dis.dis(listComp2)
  1           0 LOAD_NAME                0 (list) 
              3 LOAD_CONST               0 (<code object <genexpr> at 0x255bc68, file "listComp2", line 1>) 
              6 MAKE_FUNCTION            0 
              9 LOAD_NAME                1 (xs) 
             12 GET_ITER             
             13 CALL_FUNCTION            1 
             16 CALL_FUNCTION            1 
             19 RETURN_VALUE         
>>> listComp2.co_consts
(<code object <genexpr> at 0x255bc68, file "listComp2", line 1>,)
>>> dis.dis(listComp2.co_consts[0])
  1           0 LOAD_FAST                0 (.0) 
        >>    3 FOR_ITER                17 (to 23) 
              6 STORE_FAST               1 (x) 
              9 LOAD_GLOBAL              0 (f) 
             12 LOAD_FAST                1 (x) 
             15 CALL_FUNCTION            1 
             18 YIELD_VALUE          
             19 POP_TOP              
             20 JUMP_ABSOLUTE            3 
        >>   23 LOAD_CONST               0 (None) 
             26 RETURN_VALUE

 

>>> evalledMap = compile('list(map(f,xs))', 'evalledMap', 'eval')
>>> dis.dis(evalledMap)
  1           0 LOAD_NAME                0 (list) 
              3 LOAD_NAME                1 (map) 
              6 LOAD_NAME                2 (f) 
              9 LOAD_NAME                3 (xs) 
             12 CALL_FUNCTION            2 
             15 CALL_FUNCTION            1 
             18 RETURN_VALUE 

[...]보다 구문 을 사용하는 것이 좋습니다 list(...). 슬프게도 map클래스는 분해하기에 약간 불투명하지만 속도 테스트로 인해 만들 수 있습니다.


5
"매우 유용한 itertools 모듈은 아마도 스타일면에서 파이썬이 아닌 것으로 간주됩니다". 흠. 그래서 어떤 의미에서 나는 그것이 무엇을 의미하는지 상관 없어,하지만 난 그것을 사용 않는 사람들에게 그것의 공정이 것을 "Pythonicness"내장 명령에 따라 말을 생각하지 않는다 중 하나없는 용어 "파이썬"와 같은 수행 mapfilter표준 라이브러리와 함께 itertools본질적으로 나쁜 스타일입니다. GVR는 실제로 어느 끔찍한 실수했다거나 전적으로 성능을 위해,이 경우 당연한 결론은 ;-) 것 "Pythonicness가"라고하는 것은 바보 같은 잊어하는 것입니다 말한다 않는 한
스티브 Jessop

4
@SteveJessop : 실제로 Guido는mapfilter Droping / 이 Python 3에 대한 좋은 아이디어 라고 생각 했으며 다른 Pythonista의 반란만으로도 내장 네임 스페이스에 유지했습니다 ( reduce로 이동하는 동안 functools). 나는 개인적으로 동의 ( mapfilter미세 사전 정의로, 특히 내장 함수, A는 경우 단지 그들을 사용하지 않을 lambda필요하다)하지만, GVR은 기본적 년 동안하지 파이썬을 촉구했다.
ShadowRanger

@ShadowRanger : 사실이지만, GvR은 이제 제거 할 계획 itertools이었습니까? 이 답변에서 인용 한 부분은 나를 괴롭히는 주요 주장입니다. 나는 그의 이상적인 세계에서 여부, 모르는 mapfilter이동 것 itertools(또는 functools) 또는 완전히 이동하지만, 둘 중 하나가 말한다 일단의 경우 itertools전체를 unPythonic이다, 나는 정말 "파이썬"이 뭔지 모르 그 의미는 "GvR이 사람들이 사용하도록 권장하는 것과"비슷할 수는 없다고 생각합니다.
Steve Jessop

2
@SteveJessop : 나는 map/ 만 주소 지정 filter하지 않았습니다 itertools. 함수 프로그래밍 완벽하게 파이썬이다 ( itertools, functools그리고 operator모든 염두에 함수형 프로그래밍으로 특별히 설계되었으며, 파이썬의 모든 시간의 기능 숙어를 사용), 그리고 itertools그것은, 특히있어 고통이 자신을 구현하는 것입니다 기능을 제공 map하고 filter발전기식이되고 중복 귀도는 그들을 미워하게 만들었습니다. itertools항상 괜찮 았습니다.
ShadowRanger

1
방법이 있다면이 답변을 좋아할 수 있습니다. 잘 설명했다.
NelsonGon

95

파이썬 2 : 당신은 리스트 이해력 대신에 map그리고 filter이해력을 사용해야합니다 .

목적 당신은 그들이하지 않은 경우에도 그들을 좋아해야하는 이유 이유는 "파이썬은"이것이다 :
그들은 인수 등의 기능 / 람다 요구하는 새로운 범위를 소개를 .

나는 이것에 두 번 이상 물렸다.

for x, y in somePoints:
    # (several lines of code here)
    squared = [x ** 2 for x in numbers]
    # Oops, x was silently overwritten!

그러나 대신에 나는 말했다 :

for x, y in somePoints:
    # (several lines of code here)
    squared = map(lambda x: x ** 2, numbers)

그러면 모든 것이 잘되었을 것입니다.

같은 범위에서 동일한 변수 이름을 사용하는 것에 대해 바보라고 말할 수 있습니다.

나는 아니었다. 코드는 원래 괜찮 았습니다. 두 코드는 x같은 범위에 없었습니다. 내부 블록을 코드의 다른 섹션으로 옮긴
후에야 문제가 발생했습니다 (읽기 : 유지 관리 중 문제, 개발 아님).

예, 이 실수하지 않으면 목록 이해력이 더 우아합니다.
그러나 개인적인 경험 (그리고 다른 사람들이 같은 실수를하는 것을 보았을 때)에서 나는이 버그가 코드에 들어갔을 때 겪어야 할 고통이 가치가 없다고 생각합니다.

결론:

map및을 사용하십시오 filter. 미묘한 진단하기 어려운 범위 관련 버그를 방지합니다.

사이드 노트 :

사용을 고려하는 것을 잊지 마십시오 imapifilter(에 itertools그들은 상황에 맞는 적절한 경우)!


7
이것을 지적 해 주셔서 감사합니다. 목록 이해가 같은 범위에 있고 문제가 될 수 있다는 것이 분명히 나에게 발생하지 않았습니다. 그 말로, 다른 답변 중 일부는 목록 이해가 대부분 기본 접근 방식이어야하지만 이것이 기억해야 할 것임을 분명히합니다. 또한 함수 (및 범위)를 작게 유지하고 철저한 단위 테스트를 수행하고 단언 문을 사용하는 것이 좋습니다.
TimothyAWiseman

13
@wim : 이전 버전과의 호환성을 유지하려는 경우 Python 3에만 적용되지만 Python 2에만 관한 것입니다. 나는 그것에 대해 알고 있었고 지금은 얼마 동안 (예, 몇 개월 이상) 파이썬을 사용하고 있었지만 나에게 일어났습니다. 나보다 똑똑한 사람들이 같은 함정에 빠지는 것을 보았습니다. 당신이 너무 밝거나 경험이있어서 이것이 당신에게 문제가되지 않는다면 나는 행복합니다. 대부분의 사람들이 당신과 같지 않다고 생각합니다. 만약 그렇다면, 파이썬 3에서 그것을 고치려는 욕구가 없을 것입니다.
user541686

12
죄송합니다 .2012 년 후반에 파이썬 3이 등장한 직후에 이것을 작성했습니다. 답변은 인기가없는 파이썬 코딩 스타일을 추천하는 것처럼 읽습니다. 붙여 넣기 코드. 나는 밝거나 경험이 있다고 주장하지 않았으며, 대담한 주장이 당신의 이유에 의해 정당화된다는 것에 동의하지 않습니다.
wim

8
@wim : 응? 파이썬 2는 여전히 많은 곳에서 사용되고 있습니다. 파이썬 3가 존재한다는 사실은 그것을 바꾸지 않습니다. 그리고 "몇 달 이상 파이썬을 사용한 사람에게는 정확하게 미묘한 버그가 아닙니다"라는 말은 문자 그대로 "이것은 경험이없는 개발자에게만 해당됩니다"(명백하게는 아닙니다)를 의미합니다. 그리고 기록을 위해, 당신 은 코드를 복사하지 않고 움직이고 있다고 굵게 말했기 때문에 대답을 분명히 읽지 못했습니다 . 복사-붙여 넣기 버그는 언어마다 상당히 균일합니다. 이런 종류의 버그는 스코핑 때문에 파이썬에 더 독특합니다. 미묘하고 잊기 쉽고 놓치기 쉽습니다.
user541686

3
map그리고 / 또는로 전환하는 논리적 인 이유는 아닙니다 filter. 무엇보다도, 문제를 피하기위한 가장 직접적이고 논리적 인 번역 은 JeromeJ가 이미 지적했듯이 누출되지 않는 map(lambda x: x ** 2, numbers)생성기 표현 list(x ** 2 for x in numbers)에 대한 것이 아닙니다. Mehrdad를보고, 개인적으로 공감하지 마십시오. 나는 당신의 추론에 강력히 동의하지 않습니다.
wim

46

실제로, map리스트 이해는 파이썬 3 언어에서 상당히 다르게 행동합니다. 다음 Python 3 프로그램을 살펴보십시오.

def square(x):
    return x*x
squares = map(square, [1, 2, 3])
print(list(squares))
print(list(squares))

"[1, 4, 9]"줄을 두 번 인쇄해야하지만 대신 "[1, 4, 9]"뒤에 "[]"가 인쇄됩니다. 처음 보았을 때 squares세 가지 요소의 시퀀스로 작동하지만 두 번째는 빈 요소로 동작합니다.

파이썬 2에서 언어 map는 목록 이해가 두 언어에서와 같이 평범한 오래된 목록을 반환합니다. 요점은 mapPython 3 (및 imapPython 2) 의 반환 값이 목록이 아니라 반복 자라는 것입니다!

목록을 반복 할 때와 달리 반복자를 반복 할 때 요소가 소비됩니다. 이것이 squares마지막 print(list(squares))줄 에서 비어있는 이유 입니다.

요약:

  • 이터레이터를 다룰 때, 그것들은 스테이트 풀 (stateful)이고 그것들을 순회 할 때 돌연변이한다는 것을 기억해야합니다.
  • 목록은 명시 적으로 변경했을 때만 변경되므로 더 예측 가능합니다. 그들은 컨테이너입니다 입니다.
  • 그리고 보너스 : 숫자, 문자열 및 튜플은 전혀 변경 될 수 없으므로 훨씬 더 예측 가능합니다. 그것들은 가치 입니다.

이것은 아마도 목록 이해에 대한 가장 좋은 주장 일 것입니다. 파이썬 맵은 기능 맵이 아니라 기능 구현의 주름진 빨간 머리 의붓 자식입니다. 내가 정말 이해를 싫어하기 때문에 매우 슬프다.
semiomant

@ semiomiant 나는 python3과 같은 게으른지도가 python2와 같은 열망 적 인지도보다 더 '기능적'이라고 말합니다. 예를 들어, 하스켈의지도는 게으르다 (하스켈의 모든 것은 게으르다 ...). 어쨌든 게으른 맵은 맵 체인에 더 좋습니다. 맵에 맵에 적용된 맵이 있으면 python2의 각 중간 맵 호출에 대한 목록이 있지만 python3에는 결과 목록이 하나뿐이므로 메모리 효율성이 향상됩니다. .
MnZrK

필자가 원하는 것은 map반복자가 아닌 데이터 구조를 생성하는 것입니다. 그러나 게으른 반복자는 게으른 데이터 구조보다 쉽습니다. 생각할 거리. 감사합니다 @MnZrK
semiomant

map이 iterator가 아닌 iterable을 반환한다고 말하고 싶습니다.
user541686

16

나는 목록 이해가 일반적으로 내가하려는 일보다 더 표현력이 뛰어나다는 것을 알았 map습니다. 둘 다 완료하지만 전자는 복잡한 것이 무엇인지 이해하려고 노력하는 정신적 부담을 덜어줍니다.lambda 표현이 .

Guido lambda가 파이썬으로 받아들이는 것에 대해 가장 후회하는 것으로 s와 기능적 기능을 나열하는 곳에서 (내가 손으로 찾을 수없는) 인터뷰가 있습니다 . 그래서 그들은 미덕에 의해 비 파이썬 적이라는 주장을 할 수 있습니다 그것의.


9
그래, 한숨이지만 파이썬 3에서 람다를 완전히 제거하려는 Guido의 원래 의도는 그것에 대해 로비 활동을 한 것입니다. 그래서 그는 저의 멍청한 지원에도 불구하고 다시 돌아갔습니다. 아, 람다는 많은 단순한 경우 에 너무 편리하다고 생각 합니다. 문제는 SIMPLE 의 범위를 초과 하거나 이름에 할당 될 때입니다 (후자의 경우 def!-의 바보 같은 사본입니다).
Alex Martelli

1
당신이 생각하는 인터뷰는 amk.ca/python/writing/gvr-interview입니다 . Guido는 "때때로 기부금을받는 데 너무 빠르다가 나중에 실수라는 것을 깨달았습니다. 하나의 예는 다음과 같습니다. lambda 함수와 같은 함수형 프로그래밍 기능 중 일부 lambda는 작은 익명 함수 (맵, 필터 및 축소와 같은 내장 함수)를 생성하여 목록과 같은 시퀀스 유형에 대해 함수를 실행할 수있는 키워드입니다. "
J. Taylor

3
@Alex, 나는 당신의 경험이 없지만 람다보다 훨씬 더 복잡한 목록 이해를 보았습니다. 물론 언어 기능을 남용하는 것은 항상 저항하기 어려운 유혹입니다. 목록 이해가 (사 전적으로) 람다보다 남용하기 쉽다는 것이 흥미 롭습니다.하지만 왜 그런지 잘 모르겠습니다. 또한 "혼잡"이 항상 나쁜 것은 아니라는 점을 지적하겠습니다. "이 행이 수행 할 수있는 작업"의 범위를 줄이면 독자가 더 쉽게 이해할 수 있습니다. 예를 들어, constC ++ 의 키워드는 이러한 라인에서 큰 승리입니다.
스튜어트 버그

> 귀도. 귀도가 그의 마음에서 벗어났다는 또 다른 증거입니다. 물론 lambda사용하기 어려우며 제한하기에 너무 불명예 스럽습니다.
javadba

16

가능한 사례는 다음과 같습니다.

map(lambda op1,op2: op1*op2, list1, list2)

대:

[op1*op2 for op1,op2 in zip(list1,list2)]

zip ()은 불행히도 불필요한 오버 헤드라고 생각합니다.지도 대신 목록 이해를 고집하는 경우 탐닉해야합니다. 누군가 긍정적 으로든 부정적 으로든 이것을 명확히하면 좋을 것입니다.


"[zip (list1, list2)의 op1, op2에서 op1 * op2]]"| s / form / for / 및 zip이없는 동등한 목록 : (읽기 어려운) [범위 (len (list1)))의 i에 대해 list1 [i] * list2 [i]
약화

2
두 번째 코드 인용 부인 @andz와 @weakish의 주석에도 "for"가 아니어야합니다. 이해력을 나열하는 새로운 구문 적 접근 방식을 발견했다고 생각했습니다.
물리학 마이클

4
매우 늦은 코멘트를 추가하기 위해 다음 zip을 사용하여 게으를 수 있습니다.itertools.izip
tacaswell

5
난 아직도 선호하는 것 같아요 map(operator.mul, list1, list2). 이해가 어색 해지는 것은 바로이 매우 간단한 왼쪽 표현입니다.
Yann Vernier

1
나는지도가 그 기능에 대한 입력으로 여러 iterable을 취할 수 있다는 것을 몰랐고 따라서 지퍼를 피할 수있었습니다.
bli

16

비동기식, 병렬 식 또는 분산 형 코드를 작성하려는 경우 map대부분의 비동기식, 병렬 식 또는 분산 형 패키지가 mappython에 과부하를 가하는 기능을 제공하므로 목록 이해를 선호 할 것입니다 map. 그런 다음 map나머지 코드에 적절한 기능을 전달하면 원래 직렬 코드를 수정하여 병렬로 실행하지 않아도 될 수 있습니다.



1
파이썬의 멀티 프로세싱 모듈이이를 수행합니다 : docs.python.org/2/library/multiprocessing.html
Robert L.

9

따라서 Python 3 map()은 반복자이므로 필요한 것이 무엇인지 명심해야합니다. 반복자 또는list 객체와 합니다.

@AlexMartelli 이미으로 언급 , map()당신이 사용하지 않는 경우에만 빠른 지능형리스트보다lambda 기능을.

시간 비교를하겠습니다.

파이썬 3.5.2 및 CPython과
내가 사용했던 목성 노트북을 , 특히 %timeit내장 마법 명령
측정 : S == 1000 MS == 1000 * 1000 μS가 = 1000 * 1000 * 1000 ns의

설정:

x_list = [(i, i+1, i+2, i*2, i-9) for i in range(1000)]
i_list = list(range(1000))

내장 기능 :

%timeit map(sum, x_list)  # creating iterator object
# Output: The slowest run took 9.91 times longer than the fastest. 
# This could mean that an intermediate result is being cached.
# 1000000 loops, best of 3: 277 ns per loop

%timeit list(map(sum, x_list))  # creating list with map
# Output: 1000 loops, best of 3: 214 µs per loop

%timeit [sum(x) for x in x_list]  # creating list with list comprehension
# Output: 1000 loops, best of 3: 290 µs per loop

lambda 함수:

%timeit map(lambda i: i+1, i_list)
# Output: The slowest run took 8.64 times longer than the fastest. 
# This could mean that an intermediate result is being cached.
# 1000000 loops, best of 3: 325 ns per loop

%timeit list(map(lambda i: i+1, i_list))
# Output: 1000 loops, best of 3: 183 µs per loop

%timeit [i+1 for i in i_list]
# Output: 10000 loops, best of 3: 84.2 µs per loop

생성기 표현식과 같은 것도 있습니다 ( PEP-0289 참조) . 그래서 비교에 추가하는 것이 도움이 될 것이라고 생각했습니다.

%timeit (sum(i) for i in x_list)
# Output: The slowest run took 6.66 times longer than the fastest. 
# This could mean that an intermediate result is being cached.
# 1000000 loops, best of 3: 495 ns per loop

%timeit list((sum(x) for x in x_list))
# Output: 1000 loops, best of 3: 319 µs per loop

%timeit (i+1 for i in i_list)
# Output: The slowest run took 6.83 times longer than the fastest. 
# This could mean that an intermediate result is being cached.
# 1000000 loops, best of 3: 506 ns per loop

%timeit list((i+1 for i in i_list))
# Output: 10000 loops, best of 3: 125 µs per loop

당신은 list객체 가 필요 합니다 :

사용자 정의 함수 인 경우 목록 이해를 사용 list(map())하고 기본 제공 함수가있는 경우 사용

list객체 가 필요하지 않고 반복 가능한 객체 만 필요합니다.

항상 사용하십시오 map()!


1

객체의 메소드를 호출하기 위해 세 가지 메소드를 비교하는 빠른 테스트를 실행했습니다. 이 경우 시차는 무시할 만하고 문제의 기능의 문제입니다 (@Alex Martelli의 응답 참조 ). 여기에서는 다음 방법을 살펴 보았습니다.

# map_lambda
list(map(lambda x: x.add(), vals))

# map_operator
from operator import methodcaller
list(map(methodcaller("add"), vals))

# map_comprehension
[x.add() for x in vals]

목록 크기를 늘리기 위해 vals정수 (Python int) 및 부동 소수점 숫자 (Python float)의 목록 (변수에 저장 됨)을 살펴 보았습니다 . 다음과 같은 더미 클래스 DummyNum가 고려됩니다.

class DummyNum(object):
    """Dummy class"""
    __slots__ = 'n',

    def __init__(self, n):
        self.n = n

    def add(self):
        self.n += 5

구체적으로, add방법. 이 __slots__속성은 클래스에서 필요한 총 메모리 (속성)를 정의하여 메모리 크기를 줄이는 Python의 간단한 최적화입니다. 결과 플롯은 다음과 같습니다.

파이썬 객체 메소드 매핑 성능

앞에서 언급 한 바와 같이, 사용 된 기술은 최소한의 차이를 만들어 내기 때문에 가장 읽기 쉬운 방식으로 또는 특정 상황에서 코딩해야합니다. 이 경우 목록 이해 (map_comprehension 기술)는 객체의 두 가지 유형의 추가, 특히 짧은 목록에서 가장 빠릅니다.

플롯과 데이터를 생성하는 데 사용되는 소스를 보려면 이 페이스트 빈 을 방문하십시오 .


1
다른 답변에서 이미 설명했듯이 map함수가 정확히 같은 방식으로 호출되는 경우 (예 : [*map(f, vals)]vs. [f(x) for x in vals]) 더 빠릅니다 . 그래서 list(map(methodcaller("add"), vals))보다 더 빨리이다 [methodcaller("add")(x) for x in vals]. map루핑 대응 일부 오버 헤드를 피할 수있는 다른 호출 방법을 사용하는 경우 빠르게하지 않을 수 (예를 x.add()피할 methodcaller또는 람다 식 오버). 이 특정 테스트 케이스의 경우, [*map(DummyNum.add, vals)]빠른 (때문에 것 DummyNum.add(x)x.add()기본적으로 동일한 성능을 가지고).
GZ0

1
그런데 명시 적 list()호출은 목록 이해보다 약간 느립니다. 공정한 비교를 위해서는 글을 써야 [*map(...)]합니다.
GZ0

@ GZ0 큰 피드백에 감사드립니다! 모든 것이 의미가 있으며 list()호출이 오버 헤드를 증가 시킨다는 것을 알지 못했습니다 . 답변을 읽는 데 더 많은 시간을 보냈습니다. 공정한 비교를 위해 이러한 테스트를 다시 실행하지만 차이점은 무시할 수 있습니다.
craymichael

0

가장 파이썬적인 방법은 mapand 대신 목록 이해를 사용하는 것입니다 filter. 그 이유는 목록 이해력이 mapand 보다 명확하기 때문 filter입니다.

In [1]: odd_cubes = [x ** 3 for x in range(10) if x % 2 == 1] # using a list comprehension

In [2]: odd_cubes_alt = list(map(lambda x: x ** 3, filter(lambda x: x % 2 == 1, range(10)))) # using map and filter

In [3]: odd_cubes == odd_cubes_alt
Out[3]: True

아시다시피, 이해력은 필요에 따라 추가 lambda표현이 map필요 하지 않습니다 . 또한, 이해도 필터링을 쉽게 할 수 있지만 필터링을 허용 map해야 filter합니다.


0

@ alex-martelli의 코드를 시도했지만 약간의 불일치를 발견했습니다.

python -mtimeit -s "xs=range(123456)" "map(hex, xs)"
1000000 loops, best of 5: 218 nsec per loop
python -mtimeit -s "xs=range(123456)" "[hex(x) for x in xs]"
10 loops, best of 5: 19.4 msec per loop

지도는 매우 넓은 범위에서도 동일한 시간이 걸리며 목록 이해를 사용하면 코드에서 알 수 있듯이 많은 시간이 걸립니다. 따라서 "비 pythonic"으로 간주되는 것 외에도지도 사용과 관련된 성능 문제에 직면하지 않았습니다.


3
이것은 매우 오래된 질문이며, 당신이 말하는 대답 map은 목록을 반환하는 Python 2와 관련하여 작성되었을 가능성이 큽니다 . 파이썬 3에서는 map게으르게 평가되므로 단순히 호출 map하면 새로운 목록 요소가 계산되지 않으므로 짧은 시간을 얻는 이유는 무엇입니까?
kaya3

필자는 Python 3.x를 사용하고 있다고 생각합니다.이 질문을했을 때 Python 3은 최근에 릴리스되었으며 Python 2.x는 매우 표준이었습니다.
TimothyAWiseman
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.