파이썬의 iterable과 iterator의 차이점과 활용법을 알라보고 iterable객체의 정의와 대표적으로 어떻게 사용되는지 iterator 변환 방법 및 실용적인 활용 예시를 통해서 파이썬으로데이터 처리를 어떻게해야하는지 알아보도록 하겠습니다.
파이썬 Iterable(이터러블)
파이썬 iterable은 멤버를 하나씩 차례로 반환 가능한 객체를 의미합니다. 파이썬에서 대표적인 interable객체로는 시퀀스 타입인 리스트(list), 문자열(str), 튜플(tuple)이 있습니다. interable 객체는 ‘for’ 루프와 같은 반복문에서 자주 사용되며, 각 요소를 순차적으로 가져올 수 있습니다.
for i in range(5):
print(i)
# 출력
0
1
2
3
4
이 코드는 ‘range()’ 함수가 생성한 리스트가 interable이기 때문에, ‘for’ 루프가 이리스트의 각 멤버를 차례로 가져와서 추력할 수있음을 보여줍니다.
또 시퀀스 타입이 아닌 객체도 interable일 수 있습니다. 예를 들어, 딕셔너리와 파일도 이터러블 객체입니다. 딕셔너리의 경우 ‘for’루프를 사용해서 키에 순차적으로접근할 수 있습니다.
x = {'a': 1, 'b': 2, 'c': 3}
for i in x:
print(i)
#출력
a
b
c
또한 ‘iter()’ 또한 ‘__getitem__()’ 메소드가 정의된 클래스도 모두 iterable이라고 할 수 있습니다. interable객체는 ‘for’루프 뿐만아니라 ‘zip()’, ‘map()’과 같은 시퀀스 특성이 필요한 작업에도 유용하게 사용됩니다. ‘zip()’이나 ‘map()’함수는 interable을 인수로 받도록 정의되어있습니다.
파이썬 iterator(이터레이터)
iterator는 ‘next()’ 메소드로 데이터를 순차적으로 호출 가능한 객체를 의미합니다. 만약 ‘next()’로 다음 데이터를 불러올 수 없을 경우(마지막 데이터에 도달했을 때) ‘StopIteration’ 예외를 발생시킵니다.
그러면 모든 interable 객체가 interator일까요? 결론만 말하자면 그렇지 않습니다. 아래의 예제를 봐보도록 하겠습니다.
x = [1, 2, 3]
next(x)
#출력
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
TypeError: 'list' object is not an iterator
리스트는 iterable이지만 ‘next()’ 메소드로 호출해도 동작하지 않습니다. 리스트는 iterator가 아니기 때문에 에러가 발생하는것입니다. 만약에 iterable을 iterator로 변환하고 싶다면 iter()라는 내장함수를 사용하면 문제가 해결됩니다.
x = [1, 2, 3]
type(x) # <class 'list'>
y = iter(x)
type(y) # <class 'list_iterator'>
#출력
next(y) # 1
next(y) # 2
next(y) # 3
next(y) # Traceback (most recent call last): StopIteration
이제 ‘y’는 이터레이터가 되었으므로 ‘next()’를 사용할 수 있습니다. ‘next()’를 이용해서 리스트의 정보를 하나씩 꺼낼 수 있고, 마지막 요소를 호출한 이후에 ‘next()’를 호출하면 ‘StopIteration’ 예외가 발생할겁니다.
하지만 우리가 리스트나 튜플같은 iterable 객체를 사용할 때 굳이 ‘iter()’ 함수를 사용하지 않아도 ‘for’ 루프를 이용하여서 순차적으로 접근이 가능합니다. 이는 ‘for’루프를사용할 때 파이썬 내부에서 자동으로 iterable객체를 iterator로 변환을 해주기 때문입니다.
파이썬 interable과 interator의 활용
Iterable의 다양한 활용
이터러블 객체는 파이썬의 다양한 함수와 함께 사용할 수 있어 매우 유용합니다. 예를 들어, map() 함수는 이터러블 객체를 인수로 받아서 각 요소에 함수를 적용한 결과를 반환합니다:
def square(x):
return x * x
numbers = [1, 2, 3, 4]
squared = map(square, numbers)
for num in squared:
print(num)
위 코드는 다음과 같은 출력을 생성합니다:
4
9
16
이터러블 객체는 또한 리스트 컴프리헨션이나 제너레이터 표현식과 함께 사용할 수도 있습니다. 예를 들어, 다음과 같은 리스트 컴프리헨션을 사용하여 각 요소를 제곱할 수 있습니다:
squared = [x * x for x in numbers]
print(squared) # [1, 4, 9, 16]
Iterator의 다양한 활용
iterator는 데이터를 한번에 하나씩 처리할 수 있습니다. 그래서 메모리를 효율적인 방식으로 큰 데이터를 처리할 때 아주 유용합니다. 예를 들어서 generator를사용해서 큰 데이터 스트림을 처리할 수 있습니다. 아래의 코드를 참고하세요.
def generate_numbers():
num = 0
while num < 10:
yield num
num += 1
gen = generate_numbers()
for num in gen:
print(num)
위 코드는 다음과 같은 출력을 생성합니다:
0
1
2
3
4
5
6
7
8
9
제너레이터는 필요할 때마다 값을 생성하기 때문에 메모리를 효율적으로 사용할 수 있습니다. 이는 특히 큰 데이터셋을 처리할 때 유용합니다.
파일 읽기
파일 객체는 이터러블이므로 for 루프를 사용하여 파일의 각 줄을 순차적으로 읽을 수 있습니다:
with open('example.txt', 'r') as file:
for line in file:
print(line.strip())
이 코드는 파일의 각 줄을 읽어와서 출력합니다. 파일 객체는 이터러블이므로 for 루프에서 직접 사용할 수 있습니다.
enumerate() 함수
enumerate() 함수는 이터러블 객체를 인덱스와 함께 반환하는 이터레이터를 생성합니다. 이 함수는 이터러블 객체의 각 요소에 대해 인덱스를 부여하여 반복할 수 있게 합니다:
fruits = ['apple', 'banana', 'cherry']
for index, fruit in enumerate(fruits):
print(f"{index}: {fruit}")
위 코드는 다음과 같은 출력을 생성합니다:
0: apple
1: banana
2: cherry
enumerate() 함수는 인덱스와 값을 동시에 처리해야 할 때 유용합니다.
Generator와 Iterator의 차이점
Generator의 대해서
Generator는 Iterator 한 종류로, 함수 내에서 yield
키워드를 사용하여 값을 하나씩 생성합니다. 제너레이터 함수는 호출될 때마다 하나의 값을 반환하고, 중단된 위치에서 실행을 재개합니다. 제너레이터는 큰 데이터셋을 처리하거나 무한 시퀀스를 생성할 때 유용합니다.
예를 들어, 다음과 같은 Generator 함수를 사용하여 피보나치 수열을 생성할 수 있습니다:
def fibonacci():
a, b = 0, 1
while True:
yield a
a, b = b, a + b
fib = fibonacci()
for _ in range(10):
print(next(fib))
위 코드는 다음과 같은 출력을 생성합니다:
0
1
1
2
3
5
8
13
21
34
제너레이터는 값을 필요할 때만 계산하여 반환하므로 메모리 효율적인 처리가 가능합니다.
Iterator와 Generator의차이점
이터레이터는 __iter__()와 __next__() 메소드를 구현한 객체입니다. 이터레이터는 next() 메소드를 호출하여 값을 순차적으로 가져올 수 있으며, 더 이상 값이 없으면 StopIteration 예외를 발생시킵니다.
반면, 제너레이터는 함수 내에서 yield를 사용하여 값을 생성하는 특수한 이터레이터입니다. 제너레이터 함수는 호출될 때마다 값을 하나씩 반환하고, 중단된 위치에서 다시 시작할 수 있습니다. 제너레이터는 일반적으로 더 간결한 문법과 메모리 효율성을 제공합니다.
다음은 제너레이터와 일반 Iterator의 차이점을 보여주는 예제입니다:
# 제너레이터 함수
def my_generator():
yield 1
yield 2
yield 3
gen = my_generator()
print(next(gen)) # 1
print(next(gen)) # 2
print(next(gen)) # 3
# 일반 이터레이터 클래스
class MyIterator:
def __init__(self):
self.data = [1, 2, 3]
self.index = 0
def __iter__(self):
return self
def __next__(self):
if self.index >= len(self.data):
raise StopIteration
result = self.data[self.index]
self.index += 1
return result
it = MyIterator()
print(next(it)) # 1
print(next(it)) # 2
print(next(it)) # 3
제너레이터는 간단한 문법으로 이터레이터를 생성할 수 있는 반면, 일반 이터레이터는 __iter__()와 __next__() 메소드를 직접 구현해야 합니다.