Container

 컨테이너(Container)는 원소들을 가지고 있는 데이터 구조이며 멤버쉽 테스트를 지원한다. (멤버쉽 테스트는 아래에 나온다) 이는 메모리에 상주하는 데이터 구조로, 보통 모든 원소값을 메모리에 가지고 있다.

※ 파이썬에서 잘 알려진 컨테이너는 다음과 같다.

  • list, deque ...
  • set, frozenset ...
  • dict, defaultdict, OrderedDict, Counter ...
  • tuple, namedtuple ...
  • str

 기술적으로, 어떤 객체가 특정한 원소를 포함하고 있는지 아닌지를 판단할 수 있으면 컨테이너라고 한다. 다음과 같이 리스트, 셋 또는 튜플에 대해 멤버쉽 테스트를 할 수 있다.

>>> assert 1 in [1, 2, 3]     # lists
>>> assert 4 not in [1, 2, 3]
>>> assert 1 in {1, 2, 3}     # sets
>>> assert 4 not in {1, 2, 3}
>>> assert 1 in (1, 2, 3)     # tuples
>>> assert 4 not in (1, 2, 3)
# 딕셔너리 멤버쉽은 키 값을 체크한다:

>>> d = {1: 'foo', 2: 'bar', 3: 'qux'}
>>> assert 1 in d
>>> assert 4 not in d
>>> assert 'foo' not in d  # 'foo'는 딕셔너리의 키값이 아니다
# 마지막으로 문자열에는 부분문자열이 “포함"되어 있는지를 체크할 수 있다:

>>> s = 'foobar'
>>> assert 'b' in s
>>> assert 'x' not in s
>>> assert 'foo' in s  # 문자열은 부분문자열을 모두 "포함"하고 있다
※ 참고
 대부분의 컨테이너가 자신이 포함하고 있는 모든 원소들을 생성하는 방법을 제공하지만, 이 기능은 이를 컨테이너로 만드는게 아니라 이터레이블로 만듭니다. (잠시 후에 살펴본다)
 모든 컨테이너가 이터레이블할 필요는 없다. 이의 한 예는 Bloom filter이다. 이와 같은 확률적 데이터 구조는 특정 원소를 포함하고 있는지는 판단할 수 있지만, 각각의 개별 원소를 반환하지는 못한다.

 

Iterable(이터레이블)

 Iterable은 순회할 수 있는 모든 객체를 가리킨다. 다른 말로 하면 파이썬에서 for 문의 in 키워드 뒤에 올 수 있는 모든 값은 Iterable이다. 그러면 list, tuple, set, dict는 말할 것도 없고 문자열, 파일 등도 Iterable이라고 할 수 있다.

 대부분의 컨테이너는 또한 이터레이블(iterable)하다. 그러나 더 많은 것들 또한 이터레이블하다. 일례로 파일 열기, 소켓 열기등이 있다. 컨테이너가 일반적으로 유한할경우, 이터레이블은 무한한 데이터 소스를 나타낼 수도 있다.

 **이터레이블(iterable)**은 반드시 데이터 구조일 필요는 없으며 이터레이터(iterator)(모든 원소를 반환할 목적으로)를 반환할 수 있는 모든 객체가 가능하다. 이는 조금 어색하게 들릴 수 있지만, 이터레이블과 이터레이터 사이에는 중요한 차이점이 있다. 다음 예시를 보자.

>>> x = [1, 2, 3]
>>> y = iter(x)
>>> z = iter(x)
>>> next(y)
1
>>> next(y)
2
>>> next(z)
1
>>> type(x)
<class 'list'>
>>> type(y)
<class 'list_iterator'>

 여기서, y z는 각각 이터레이블 x로부터 값을 생성해내는 이터레이터의 인스턴스이고 x는 이터레이블이다. y z는 예시에서 볼 수 있듯이 상태를 가진다. 이 예시에서, x는 데이터 구조(리스트)지만, 이는 필수 요건은 아니다. 마지막으로 다음과 같이 작성했을 때 아래에 일어나는 일이 사진으로 설명되어 있다.

x = [1, 2, 3]
for elem in x:
    ...

Iterator

 요소가 복수인 컨테이너(리스트, 퓨플, 셋, 사전, 문자열)에서 각 요소를 하나씩 꺼내 어떤 처리를 수행할 수 있도록 하는 간편한 방법을 제공하는 객체이다.  

  ‘Iterable은 for 문에 넣을 수 있는 모든 값’으로 정의하면 되지만 Iterator는 조금 더 까다롭다. Iterator는 상태를 유지하며 반환할 수 있는 마지막 값까지 원소를 필요할 때마다 하나씩 반환하는 것이라고 생각하면 된다.  ‘반환할 수 있는 마지막 값까지 원소를 하나씩 반환한다’의 의미는 가령 list에서 값을 하나씩 반환하는 것과 동작이 같다.

# 무한 시퀀스로부터 유한 시퀀스를 생성하는 Iterator 예제
>>> from itertools import islice
>>> colors = cycle(['red', 'white', 'blue'])  # 무한
>>> limited = islice(colors, 0, 4)            # 유한
>>> for x in limited:						# 따라서 for 루프에 사용하기에 안전하다
... 	print(x)
red
white
blue
red
# 피보나치수를 생성하는 이터레이터 예제
>>> class fib:
...     def __init__(self):
...         self.prev = 0
...         self.curr = 1
...
...     def __iter__(self):
...         return self
...
...     def __next__(self):
...         value = self.curr
...         self.curr += self.prev
...         self.prev = value
...         return value
>>> f = fib()
>>> list(islice(f, 0, 10))
[1, 1, 2, 3, 5, 8, 13, 21, 34, 55]

참고로 이 클래스는 이터레이블(iter() 메서드를 사용하므로)이자 자체 이터레이터(next() 메서드를 가지므로)이다.

이터레이터 내의 상태는 prev curr 인스턴스값으로 유지되고 있으며, 이터레이터를 호출하는 서브 시퀀스에 사용된다. **next()**를 호출할때마다 두 가지 중요한 작업이 수행된다:

  1. 다음 next() 호출을 위해 상태를 변경한다
  2. 현재 호출에 대한 결과값을 생성한다

 

Generator

iterator의 한 종류로, 하나의 요소를 꺼내려고 할 때마다 요소 generator를 수행하는 타입으로, Python에서는 yield문을 통해 구현하는 것이 보통이다.

 Generator는 나만의 Iterable, Iterator 기능을 만들되, 생성 문법을 기존보다 단순화한 개념 또는 클래스라고 할 수 있다.리스트나 Set과 같은 컬렉션에 대한 iterator는 해당 컬렉션이 이미 모든 값을 가지고 있는 경우이나, Generator는 모든 데이타를 갖지 않은 상태에서 yield에 의해 하나씩만 데이타를 만들어 가져온다는 차이점이 있다. 이러한 Generator는 데이타가 무제한이어서 모든 데이타를 리턴할 수 없는 경우나, 데이타가 대량이어서 일부씩 처리하는 것이 필요한 경우, 혹은 모든 데이타를 미리 계산하면 속도가 느려서 그때 그때 On Demand로 처리하는 것이 좋은 경우 등에 종종 사용된다.

1) Generator 생성방법 1. yield

Generator 함수가 처음 호출되면, 그 함수 실행 중 처음으로 만나는 yield 에서 값을 리턴한다. Generator 함수가 다시 호출되면, 직전에 실행되었던 yield 문 다음부터 다음 yield 문을 만날 때까지 문장들을 실행하게 된다. 이러한 Generator 함수를 변수에 할당하면 그 변수는 generator 클래스 객체가 된다.

from random import randint

def random_number_generator(n):
	count = 0
    while count < n:
    	yield randint(1, 100)
        count += 1

 ‘yield’ 문을 활용한 기초적인 Generator 활용 예제다. 사용해보기 전에, Iterator를 직접 구현했을 때와 대비되는 Generator의 특징을 살펴보면 다음과 같다.

  • 클래스가 아닌 함수로 정의한다.
  • 프로토콜처럼 Iterable와 Iterator의 두 요소를 분리하지 않고 한 요소에 담을 수 있다.
  • 호출될 때마다 한 번씩 반환할 값을 반환하는 키워드가 ‘yield’이며, 이는 함수가 아니다. 즉 return 문처럼 ()를 사용하지 않는다.
# 랜덤 정수 5개를 반환하는 Generator 예제

g = random_number_generator(5)

print(g)

<generator object random_number_generator at 0x7f0801e15e08>

함수를 호출해 generator 객체(object)를 만들었다. 이때 객체라는 것은 함수의 반환값이 상태값을 유지하는 Generator 클래스의 인스턴스(instance)라는 것을 암시한다. Generator는 Iterable, Iterator 생성 문법을 간략화한 개념 및 구현이기 때문에 그 내부과정이 직관적으로 보이지는 않지만(추상화되었기 때문에), 함수의 호출 결과가 결국은 Iterator protocol을 준수하는 개체를 반환한다고 이해하면 무난하다. 생성된 Generator를 사용해보자.

print(next(g)) 
print(next(g)) 
print(next(g)) 
print(next(g)) 
print(next(g)) 
print(next(g)) 

72
90
3
48 
34 
----> 3 print(next(g)) 

StopIteration: 

# 총 5번의 호출 이후 StopIteration이 반환됐다.

 

 

2) Generator 생성방법 2. Generator comprehension

 ()를 사용해서 comprehension을 만들면 tuple이 생성되지 않고 다음과 같은 결과가 나온다.

print((n for n in range(3))) 

<generator object <genexpr> at 0x7f0801c6f620>

 []를 사용해 만든 comprehension은 list를 만들었다. 그러면 ()를 사용하면 tuple이 반환되리라 무난하게 예상할 수 있다. 그런데 예상과는 정반대로 ()를 사용하면 tuple이 아닌 ‘generator object’가 생성됐다. 즉, list comprehension의 문법을 사용하되 식을 닫는 괄호를 ()를 사용하는 방법이 generator를 생성하는 두 번째 방법이며, 이를 Generator comprehension(또는 generator expression)이라고 한다. 하지만 Generator Expression은 List Comprehension과 달리 실제 리스트 컬렉션 데이타 전체를 리턴하지 않고, 그 표현식만을 갖는 Generator 객체만 리턴한다. 즉 실제 실행은 하지 않고, 표현식만 가지며 위의 yield 방식으로 Lazy Operation을 수행하는 것이다. 위에서 사용한 예제를  generator comprehension을 사용해서 다시 만들어보자. 

for n in g :
	print(n)
    
96
45
14
56
89

 그러면 다음과 같은 질문을 던질 수 있다. Iterable, Iterator, Generator에서 Generator만 쓰면 되는가? 또 Generator를 사용한다고 할 때, yield를 사용하는 대신 Generator expression을 사용하는 방법이 무조건 옳은 것인가? 보다 복잡한 프로그램일수록 Iterator protocol을 준수한 Iterable, Iterator를 만들어 쓰는 것이 좋은 선택일 수 있고, 프로그램이 매우 단순하다면 Generator expression을 사용하는 방법이 더 바람직할 수 있다. 


 

출처 : https://mingrammer.com/translation-iterators-vs-generators/

출처 : https://engineer-mole.tistory.com/64

 

'Python 프로그래밍' 카테고리의 다른 글

[Python] enumerate 함수  (0) 2022.04.28
[Python] 익명함수 lambda  (0) 2022.04.26
[Python] List & Dictionary & Tuple & Set  (0) 2022.04.24
[Python] 문자열 출력 Formatting  (0) 2022.04.21
[Python] 가상환경  (0) 2022.04.18

+ Recent posts