"뭐에요? 🤨" 시리즈 1편
평소에 헷갈렸던 것들이나 암기해두면 좋을 것들, 대강 알고 정확한 정의를 몰랐던 것들을 좀 깊게 들어가보면서 질문별, 문제상황별로 포스트를 작성해보겠다. 파이썬에 대해서는 기초적인 문법을 알고 있다고 생각하고 작성한다.
기본적으로 __iter__()
혹은 __getitem__()
스페셜 메서드가 있는 클래스 객체들은 다 이터러블이다. (그 말인즉슨 우리가 만든 커스텀 클래스에 저 스페셜 메서드 하나 이상을 구현해주면 이터러블이 된다는 거다.)
다음과 같은 애들이 파이썬에서 기본적으로 사용할 수 있는 이터러블이다. (for .. in [이_자리에_들어갈_수_있는_애들]
로 생각하면 외우기 쉽다)
list
(mutable sequence type)tuple
(immutable sequence type)str
(immutable, text sequence type)dict
(mutable, non-sequence, mapping type)set
(mutable, non-sequence type)file
iterable
이라고 쓴 자리에 이터러블을 넣어서 사용한다.
for <variable> in <iterable>
zip(*iterables, strict=False)
, map(function, iterable, ...)
, ... )iter(iterable)
→ 해당 객체의 이터레이터 리턴. 이 이터레이터는 값들을 쭉 한 번씩 거치는 동안 유효하다.for
문의 경우 루프를 도는 동안 이터레이터를 자동으로 생성해서(자동으로 __iter__()
를 호출해준다는 뜻) 이름 없는 변수에 잡아둔다.이터러블 오브젝트의 스페셜 메서드(__iter__()
)가 호출되면 해당 이터러블에 대한 이터레이터 객체가 생성되고 리턴된다.
이터레이터에는 또 __next__()
라는 스페셜 메서드가 있어서, 이 메서드를 호출하면
따라서 이터레이터 형은 두 개의 메서드를 사용해서 구현된다. (이터러블은 __iter__()
나 __getitem__()
이 정의되어 있다면) 이터러블 형은
__iter__()
__next__()
이 두 개의 메서드를 모두 지원해야 한다. (이터레이터 형에도 __iter__()
메서드가 있다는 것은 이터레이터에 대한 이터레이터도 만들 수 있다는 것이다. 또 이터러블의 조건을 만족하는 것이기도 하므로 이터레이터는 이터러블이다. 따라서 이터러블이 사용되는 곳에 당연히 이터레이터도 쓸 수 있다.)
아래처럼 리스트를 만들고 for 문에 이 리스트(이터러블)을 사용해보자. 그럼 아래와 같은 결과가 나타나는데, 어떻게 돌아가는 걸까?
for 가 하는 일을 풀어서 코드로 써보겠다. for 문이 이터러블을 받으면 이터러블의 __iter__()
를 호출한다. __iter__()
메서드는 이터레이터 객체를 리턴한다.
for 문의 각 반복마다 내부적으로 __next__()
메서드를 호출하고 이 메서드는 현재 가르키고 있는 인덱스에 해당하는 원소를 반환하고 (이 반환된 값은 i
에 할당된다), 이터레이터 포인터를 다음 원소로 옮긴다. 모든 원소를 다 접근하고 나면 StopIteration
exception이 나오고 for 문이 종료된다.
__iter__()
또는 __getitem__()
필요__iter__()
와 __next__()
필요__iter__()
) 이터레이터는 이터러블이다.__iter__()
메서드를 자동으로 호출해서 이터레이터를 생성, 리턴하고 루프를 도는 동안 이름 없는 변수에 잡아둔다.__next__()
를 호출해서 현재 이터레이터 포인터가 가르키고 있는 곳의 값을 리턴하고, 포인터가 다음 원소를 가르키도록 한다.이터레이터는 이터러블이라고 했다. 이터레이터도 __iter__()
가 있기 때문에 이터러블이 된다.
하지만 중요한 예외가 있는데, 리스트와 같은 컨테이너 객체를 iter() 함수에 전달하거나 for 루프에서 사용할 때마다 새 이터레이터를 만든다. 하지만 이터레이터는 이럴때마다 새 이터레이터를 만들지 않고, 지난 이터레이션에 사용된 이미 소진된 이터레이터를 돌려준다. 차이를 코드로 확인해보자.
리스트에 두 번 for 루프를 돌렸는데 똑같이 처음부터 원소가 잘 나온다. 새로운 이터레이터가 for 문마다 자동으로 생성됐기 때문이다.
id를 확인해봐도 계속 새로운 이터레이터가 생긴 것을 알 수 있다.
이터레이터에 for 문을 적용했더니 두번째 for 문에서는 아무것도 출력되지 않는다. 이미 앞서 만들어진 이터레이터에 대한 이터레이터를 리턴했기 때문이다. 계속 같은 이터레이터이기에 id도 같다.
정리하자면 이터레이터에 대한 이터레이터는 한 번 생긴 것을 계속 쓴다. 하지만 이터레이터가 아닌 이터러블에 대한 이터레이터는 계속 새로 만든다.