오늘은 지난번에 살펴봤던 Container와 함께 iterable object의 일종인 iterator에 대해서 살펴보겠습니다.
iterator는 순차적으로 처리하는 object이지만 한번 item을 사용하면 소진된다는 점. 또 indexing이 불가하다는 점에서 container와 차이가 있습니다. (그렇기 때문에 tuple(), list(), set(), dict()등의 명령어를 이용해 iterator를 container로 변환해서 사용하는 경우도 많고, iterator 상태에서 item들을 꺼내기 위해서 upack(*, **)을 이용하기도 합니다. 대표적인 iterator로는 map(), zip(), enumerate() 객체들이 있습니다.)
잘 이해가 안가신다구요? 그럼 예시를 통해 알아보시죠. iterator중에서도 map을 중심으로 한번 살펴보겠습니다.
예시1)
x = map(int, '34567')
# 첫 번째 처리: all()을 사용하여 모든 값이 참인지 확인
print(all(x)) # True 출력 (모든 숫자가 0이 아니므로 True)
# 두 번째 처리: any()를 사용하여 값이 참인 것이 있는지 확인
print(any(x)) # False 출력 (Iterator가 이미 소진되었기 때문에 False)
# 세 번째 처리: 소진된 상태에서 값을 출력하려고 시도
print(list(x)) # 빈 리스트 출력 []
위의 예시에서 '34567'이라는 문자열을 map()을 활용해서 정수형(int)값이 들어있는 iterator x를 만들었습니다. 그런데, 그 다음 줄에서 all(x)이라는 명령어를 주었습니다. x의 모든 원소가 0이 아니면 True를 반환하라는 연산자이므로 x의 모든 원소들이 0이 아닌지 iteration돌면서 확인합니다. 그런데 x는 iterator이기 때문에 이렇게 한번 item을 꺼내서 확인하고 나면 소진되어 버립니다. 다음줄에서 확인할 수 있는데요, 이번에는 any(x)라는 명령어를 주네요. x의 원소 중 하나라도 0이 아닌 것이 있으면 True를 반환하는 연산자이지만... False가 출력됩니다. 이제 x에는 아무것도 없거든요. 더 확실하게 그 다음줄에서 list로 x를 변환해서 출력해보면 빈 리스트 []만 덩그러니 출력됩니다. 그래서 실제 사용 시에 이 특성을 간과하고 코드를 작성하지는 않았는지 주의하는 것이 필요합니다.
예시2)
a = map(int, input().split())
위와 같이 코드를 작성해주면 문자열을 keyboard로 입력해주면(예: '2, 6, 8, 9' )이것을 정수형으로 변환한 뒤 iterator로 만들어 a라는 이름으로 저장하라는 코드입니다. 보통 코딩테스트 시에 직접 인풋을 받아야할 경우에 이렇게 입력받아서 저장을 시킵니다. 이렇게 저장된 iterator a는 sum()등 iterable object를 인자로 받는 함수에 넣을 수 있습니다. iterator 역시 container와 같은 iterable object이기 때문입니다.
print(sum(a))
그런데, 만약에 이렇게 만든 iterator가 담고있는 item들을 출력하고 싶으면 어떻게 하면될까요? 그냥 한 번 프린트를 해볼까요?
print(a)
이렇게 하면 원하는 결과가 나오지 않습니다. <map object at 0x0000016C552EB940> map이라는 iterator object가 0x0000016C552EB940라는 메모리 주소에 저장되어 있는 것만 출력하고 있네요. 우리가 원하는 iterator a의 값들을 출력하려면
방법1) list 등의 container로 변환한 이후 출력한다.
print(list(a))
방법2) unpack 명령어를 이용하여 출력한다.
print(*a)
방법2)가 좀 더 쉽긴 한데, 만약 item을 꺼내어 쓰는 작업을 여러번 한다면 방법1)처럼 container로 만들어 준 뒤 작업하는게 더 편하겠죠? (왜냐하면 말씀드린것처럼 iterator는 한번 꺼내면 소진되기 때문이지요)
비슷하게 iterator의 2번째 item을 보고 싶다고 list처럼 a[1]로 호출하면 어떻게 나올까요?
TypeError: 'map' object is not subscriptable map 객체는 subscriptable하지 않다고 나옵니다. container와 iterator의 차이가 이제 좀 감이 잡히시나요?
iterator와 lambda 함수를 결합해서 쓰면 많은 상황에서 편리하게 사용할 수 있습니다.
list(map(lambda x: str(x) if x % 3 == 0 else x, a))
# if를 쓸 경우 반드시 else가 필요
list(map(lambda x, y: x * y, a, b))
list(filter(lambda x: x > 5 and x < 10, a))
reduce(lambda x, y: x + y, a) #: 누적합 / functools 로 옮겨짐
첫번째 줄의 코드는 a list의 원소들에 대해서 3으로 나누어 떨어질 때는 str타입으로 변환하고 그렇지않으면 그대로 두라는 코드입니다.
두번째 줄의 코드는 a, b 두 list의 같은 위치에 있는 원소들끼리 곱하여 새로운 list를 생성하는 코드입니다.
세번쨰 줄의 코드는 a list에서 5보다 크고 10보다 작은 원소만을 뽑아(filtering)내어 새로운 list를 생성하는 코드입니다.
마지막 줄의 코드는 누적합(사실 comprehension이나 lambda함수로도 쉽지 않은게 누적합인데 이것을 reduce를 활용해서 쉽게 작성했습니다)을 구하는 코드입니다.
어떠신가요? 위 코드들을 for loop 같은걸 이용해서 작성한다고 하면 코드도 길어지고 속도도 느립니다. (애초에 python은 그런식으로 작업하기 위한 목적으로 만들어진 언어가 아닙니다) 파이썬을 파이썬답게 pythonic하게 만들어 주어야 효율적인 코딩이 가능합니다.
'프로그래밍 > Python 관련 정보' 카테고리의 다른 글
[Python Programming 기초] Dictionary (3) | 2024.10.22 |
---|---|
[Python Programming 기초] Comprehension (2) | 2024.10.22 |
[Python Programming 기초] Container (0) | 2024.10.19 |
[Python Programming 기초] Mutable/Immutable (1) | 2024.10.19 |
[Python Programming 기초] 파이썬 연산자 우선순위 (4) | 2024.10.17 |