Save my data

Python : map()에 대한 공부 이것저것. 본문

개인공부/Python

Python : map()에 대한 공부 이것저것.

양을 좋아하는 문씨 2024. 4. 24. 13:36

알고리즘 문제를 풀다가 map을 쓸 일이 생겼다.

상황은 이러하다.

 

1. list1 에 있는 내부 요소들을 list2 로 옮겨야 된다.

2. 방법이야 여러 가지가 있겠지만, map을 써서 옮길 수 없을까 생각했다.

3. append 함수가 있으니, map을 활용해서 해볼 수 있겠다 생각함.

4. 여러 가지 버전의 코드들을 테스트 해봤다.

# 1
list1 = [1, 2, 3]

list2 = []

list2 = [*map(list2.append, list1)]

print(list2) # [None, None, None]

# 2
list1 = [1, 2, 3]

list2 = []

map(list2.append, list1)

print(list2) # []

# 3
list1 = [1, 2, 3]

list2 = []

[map(list2.append, list1)]

print(list2) # []

# 4
list1 = [1, 2, 3]

list2 = []

[*map(list2.append, list1)]

print(list2) # [1, 2, 3]

 

# 1 의 경우

append 함수는 추가하는 동작만 하고 None을 반환한다. 따라서 map이 생성한 이터러블 객체들은 모두 None이고 list2에 담긴다.

 

# 2 ~ 4 의 경우

아래 설명 참조.


list1 = [1, 2, 3]
list2 = []
map(list2.append, list1)
print(list2)

 

map함수의 결과값 뭉치, 즉 append가 개별로 동작하면서 그때그때 뱉어내는 반환값들이 필요한 게 아니라 append의 동작만 필요하기 때문에 map함수를 호출만 해줬다.

 

내 예상은 [1, 2, 3] 이었다.

 

그런데 결과는 빈 리스트 [] 였다.

 

왜 그렇지?

한참 구글링 해보고 답을 찾았다.

 

이유는, map 함수는 지연 평가(lazy evaluation)를 사용하는 함수이기 때문이다. (Promise의 동작 방식과 비슷한걸까?)

지연 평가란, 말 그대로 계산이 필요한 시점까지 계산을 미루는 것을 의미하는데, 당장 필요하지 않은 계산을 미룸으로써 성능적으로 이점을 가져갈 수 있게 된다.

 

map 함수는 호출은 되었는데, 실제 map함수의 개별 이터러블 객체를 사용하지는 않고 있다.

개별 이터러블 객체들이 사용되지 않고 있기 때문에, 지연 평가에 의해서 map 함수 내부는 동작되지 않고 있는 것이다.

 

map 함수가 반환하는 이터레이터는 map함수에 전달된 함수(여기서는 append)와 이터러블 객체(여기서는 list1)를 저장하고, 값을 호출하는 등의 동작이 있을 때마다 해당 함수(append)를 호출하여 값을 만들어낸다.


어쨌든 결론은 map 함수를 호출할 때에는 호출 즉시 내부 함수의 동작에 의한 값이 생성되는 것이 아니라, 값이 필요한 시점에 내부 함수가 동작되고 개별 값을 생성하게 된다. 이러한 특성으로 인해 메모리를 효율적으로 사용할 수 있고 필요한 만큼의 계산만 수행할 수 있게 된다.

 

다시 예시 # 2 ~ 4로 돌아와서 살펴보자.

 

# 2
list1 = [1, 2, 3]

list2 = []

map(list2.append, list1)

print(list2) # []

 

위의 경우는 map은 호출되었지만 실제로 내부 값을 사용한다는 어떤 코드도 없어서 지연 평가에 의해 계산되지 않았다.

 

# 3
list1 = [1, 2, 3]

list2 = []

[map(list2.append, list1)]

print(list2) # []

 

위의 경우는 map이 호출되고 그 반환값이 리스트에 담기고 있지만, 역시나 개별 객체의 값을 사용한다는 요청이 별도로 없기 때문에 지연 평가에 의해 계산되지 않았다.

 

# 4
list1 = [1, 2, 3]

list2 = []

[*map(list2.append, list1)]

print(list2) # [1, 2, 3]

 

위의 경우는 map이 호출되었고, map의 개별 객체들을 언팩해서 리스트에 각각 담겠다고 하였다.

map의 개별 객체들을 사용한다고 했기 때문에, list2.append가 개별 값들을 생성하기 위한 동작을 하게 되었다.

그런데 append의 동작만 필요할 뿐이지 append가 생성하는 값이 필요한 게 아니기 때문에 append가 반환하는 개별 값들(None)은 무시하면 된다(그것을 무시하지 않은 케이스가 # 1 의 케이스이다.)

 

이러한 동작 방식을 이해하면 아래와 같이 다양한 방식으로 map함수를 활성화 할 수 있다.

list1 = [1, 2, 3]

list2 = []

for _ in map(list2.append, list1):
    pass

print(list2)

 

Comments