지도가 Python 3에서 목록 대신지도 객체를 반환하는 이유는 무엇입니까?


82

Python 3.x새로운 언어 디자인을 이해하는 데 관심이 있습니다 .

나는 Python 2.7에서 기능을 즐긴다 map.

Python 2.7.12
In[2]: map(lambda x: x+1, [1,2,3])
Out[2]: [2, 3, 4]

그러나 Python 3.x에서는 다음과 같이 변경되었습니다.

Python 3.5.1
In[2]: map(lambda x: x+1, [1,2,3])
Out[2]: <map at 0x4218390>

나는 방법을 이해하지만 이유에 대한 참조를 찾을 수 없습니다. 왜 언어 디자이너들이이 선택을했을까요? 제 생각에는 엄청난 고통을 안겨줍니다. 이것이 목록 이해력을 고수하는 팔 씨름 개발자였습니까?

IMO, 목록은 자연스럽게 Functor로 생각할 수 있습니다 . 그리고 나는 어떻게 든 이런 식으로 생각한다고 생각했습니다.

fmap :: (a -> b) -> f a -> f b

2
그 이유는 우리가 목록 이해력 대신 생성기를 사용하는 이유와 동일해야합니다. 게으른 평가를 사용하면 메모리에 많은 것을 보관할 필요가 없습니다. 여기 허용 대답을 확인합니다 stackoverflow.com/questions/1303347/...
모 베르그

8
이것이 당신에게 "큰 고통"을 가져다주는 이유를 설명해 주시겠습니까?
RemcoGerlich

3
수년간의 사용이 가장 일반적인 사용 map이 결과를 반복 한다는 것을 보여주기 때문이라고 생각합니다 . 필요하지 않을 때 목록을 작성하는 것은 비효율적이므로 개발자는 map게으 르기로 결정했습니다 . 성능을 위해 얻을 수있는 것이 많고 잃을 것이 많지 않습니다 (목록이 필요하면 하나만 요청하십시오 ... list(map(...))).
mgilson

3
좋아요, Functor 패턴을 유지하고 List의 게으른 버전을 제공하는 것보다, 그들이 매핑 될 때마다 목록의 게으른 평가를 강제하기로 결정한 것이 흥미 롭습니다. 저는 제너레이터->지도-> 생성기 또는 목록->지도-> 목록 (내가 결정)을
선택할 수있는 권한을 갖고 싶었

4
@NoIdeaHowToFixThis, 실제로 전체 목록을 필요로하는 경우, 그냥 쉽게 지옥 같은 목록에 변환, 당신에게 달려
Netwave

답변:


37

나는지도가 여전히 존재 이유를 생각 전혀 할 때 발전기 표현이 또한 존재, 그것은 모든 기능에 이상 루프 및 전달되는 여러 반복자 인수를 취할 수 있다는 것입니다 :

>>> list(map(min, [1,2,3,4], [0,10,0,10]))
[0,2,0,4]

zip을 사용하는 것보다 약간 쉽습니다.

>>> list(min(x, y) for x, y in zip([1,2,3,4], [0,10,0,10]))

그렇지 않으면 단순히 생성기 표현식에 아무것도 추가하지 않습니다.


1
목록 이해가 더 비단뱀적이고 언어 디자이너가 강조하고 싶어하는 욕구를 더하면 이것이 가장 현장의 대답이라고 생각합니다. @vishes_shell은 어떻게 든 언어 디자인에 충분히 집중하지 않습니다.
NoIdeaHowToFixThis

2
두 목록의 길이가 같지 않으면 Python 2와 3에서 다른 결과를 생성 합니다. 시도 c = list(map(max, [1,2,3,4], [0,10,0,10, 99]))파이썬 2와 파이썬 3에
cdarke

1
여기 python3에서 모두 제거 맵에 원래 계획에 대한 기준은 다음과 같습니다 artima.com/weblogs/viewpost.jsp?thread=98196
베른 하르트

흠 내가지도를 목록으로 감싸면 1 개의 요소 목록이 표시됩니다.
awiebe

24

반복자를 반환하기 때문에 전체 크기 목록을 메모리에 저장하지 않습니다. 그래서 당신은 기억에 어떤 고통도 일으키지 않고 미래에 그것을 쉽게 반복 할 수 있습니다. 아마도 전체 목록이 필요하지는 않지만 상태에 도달 할 때까지 목록의 일부가 필요합니다.

문서는 유용하며 반복자는 훌륭합니다.

데이터 스트림을 나타내는 개체입니다. 반복기의 __next__()메소드 에 대한 반복 호출 (또는 내장 함수에 전달 next())은 스트림에서 연속 항목을 리턴합니다. 더 이상 데이터를 사용할 수 없으면 StopIteration대신 예외가 발생합니다. 이 시점에서 반복기 객체가 소진되고 해당 __next__()메서드에 대한 추가 호출이 StopIteration다시 발생합니다. 반복기에는 __iter__()반복기 객체 자체를 반환 하는 메서드가 있어야 모든 반복기가 반복 가능하며 다른 반복기가 허용되는 대부분의 장소에서 사용될 수 있습니다. 한 가지 주목할만한 예외는 여러 번의 반복을 시도하는 코드입니다. 컨테이너 객체 (예 :)는 사용자가 객체에 list전달할 때마다 새로운 이터레이터를 생성합니다.iter()함수를 사용하거나 for 루프에서 사용하십시오. 반복기로이를 시도하면 이전 반복 단계에서 사용 된 동일한 소진 된 반복기 객체가 반환되어 빈 컨테이너처럼 보입니다.


14

Guido는 여기 에서이 질문에 대답 합니다 . " 목록을 만드는 것은 낭비 일 것입니다."

그는 또한 올바른 변환은 일반 for루프 를 사용하는 것이라고 말합니다 .

map()2에서 3으로 변환 하는 것은 그저 list( )주위에 a를 붙이는 단순한 경우가 아닐 수 있습니다 . Guido는 또한 말합니다 :

입력 시퀀스가 동일한 길이없는 경우 " map()시퀀스의 짧은 종료에 중지됩니다. 전체의 호환성을 위해 함께 map()파이썬 2.X에서,도에서 시퀀스 포장 itertools.zip_longest(), 예를

map(func, *sequences)

된다

list(map(func, itertools.zip_longest(*sequences)))

"


3
Guido 주석은 map()함수 로 사용하는 것이 아니라 함수의 부작용에 대해 호출됩니다 .
abukaj

4
변형 zip_longest이 잘못되었습니다. itertools.starmap동등 하게 사용하려면 list(starmap(func, zip_longest(*sequences))). zip_longest튜플을 생성 하기 때문입니다 . 따라서 는를 호출 할 때처럼 고유 한 인수 대신 func단일 n업플 인수를 받습니다 . nmap(func, *sequences)
Bakuriu

12

Python 3에서 많은 함수 (뿐만 map아니라 zip, range기타)는 전체 목록이 아닌 반복자를 반환합니다. 반복기를 원할 수도 있고 (예를 들어 전체 목록을 메모리에 보관하지 않으려는 경우) 목록을 원할 수도 있습니다 (예 : 색인을 생성 할 수 있어야 함).

그러나 Python 3에서 변경된 주요 이유 는 전체 목록이 이미 작성되어 메모리에 저장되어 있기 때문에 list(some_iterator)역 등가를 사용하여 반복기를 목록으로 변환하는 것이 사소한 일이지만 iter(some_list)원하는 결과를 얻지 못하기 때문이라고 생각합니다.

예를 들어 Python 3 list(range(n))에서는 range객체 를 빌드 한 다음 목록으로 변환하는 데 드는 비용이 거의 들지 않으므로 제대로 작동합니다. 그러나 Python 2 iter(range(n))에서는 range()반복기가 빌드되기 전에 전체 목록이 생성되기 때문에 메모리를 절약하지 않습니다 .

따라서 Python 2에서는 imapfor map(그것이 완전히 동일 하지는 않지만 ), xrangefor range, izipfor 와 같이 목록이 아닌 반복자를 생성하는 데 별도의 함수가 필요합니다 zip. 대조적으로 Python 3는 필요한 경우 list()호출이 전체 목록을 생성하므로 단일 함수 만 필요 합니다.


itertools반환 반복자의 Python 2.7 함수의 AFAIK 도 있습니다. 또한 목록을 여러 번 반복하고 무작위로 액세스 할 수 있기 때문에 반복기를 게으른 목록으로 보지 않습니다.
abukaj

확인 감사 @abukaj, 나는 명확하게하려고 내 대답을 편집 한
Chris_Rands

@IgorRivin 무슨 뜻이야? Python 3 map객체에는 next()메서드가 있습니다. 파이썬 3 range범위 객체는 엄격하게 이터레이터 있습니다 나는 알고있다
Chris_Rands

내 Anaconda 배포판 python 3.6.2의 @Chris_Rands foo = map(lambda x: x, [1, 2, 3])는 맵 객체를 반환합니다 foo. 이렇게하면 foo.next()오류가 다시 온다 :'map' object has no attribute 'next'
이고르 리빈

1
@IgorRivin :로 시작하고 끝나는 메서드 __는 Python에 예약되어 있습니다. 그 예약 없이는 next단순한 메서드 (실제로 반복자가 아님)와 반복자 인 것을 구별하는 데 문제 가 있습니다. 실제로는 메소드를 건너 뛰고 2.6부터 모든 Python 버전에서 제대로 작동 하는 next()함수 (예 :)를 사용해야합니다 next(foo). 잘 작동 len(foo)하더라도 사용하는 것과 동일한 방식입니다 foo.__len__(). dunder 메서드는 일반적으로 직접 호출 되지 않고 다른 작업의 일부로 암시 적으로 호출됩니다.
ShadowRanger
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.