시퀀스, 이터러블 & 이터레이터
내용
시퀀스(Sequence)와 이터러블(iterable)
인덱스를 가진 요소들로 구성된 객체를 시퀀스(Sequence)라고 하며 파이썬은 리스트, 바이트 배열, 문자열, 튜플, range, byte와 같은 내장 시퀀스 형(type)들을 제공합니다.
시퀀스는 불변형과 가변형으로 구분할 수 있으며 한 객체내의 모든 요소의 자료형이 같은 동종형(homogeneous type)과 그렇지 않은 이종형(heterogeneous type)으로 분류할 수 있습니다. 예로서 문자열은 모든 요소가 문자로 이루어지므로 동종형입니다. 리스트는 수, 문자, 다른 리스트나 객체 등을 요소로 가질 수 있으므로 동종형 뿐만 아니라 이종형이 됩니다. 당연히 저장이나 연산에서 동종형이 이종형보다 효율적입니다.
이터러블(iterable)은 요소들를 하나씩 호출할 수 있는 객체를 의미합니다. 그러므로 문자형, 리스트를 포함하는 모든 시퀀스는 이터러블입니다. 그러나 모든 이터러블이 시퀀스가 아닙니다. 예로서 사전(dictionary)형 객체의 요소들은 인덱스를 가지지 않지만 반복문 등을 사용하여 각각의 요소를 호출할 수 있으므로 이터러블입니다. 다음 객체 dic는 사전형 객체로 반복문 중의 하나인 for 문
을 사용하여 요소들 각각을 호출할 수 있습니다. 이 경우 사전의 키(key)만이 반환됩니다.
사전(dictionary) 형은 키와 값으로 구성되며 각각을 메소드로 호출할 수 있습니다. 그러나 다음 코드와 같이 사전 객체 자체에서 요소를 호출할 경우 인덱스 역할을 하는 키만이 호출됩니다. 값이나 키와 값을 모두 호출하기 위해서는 .values()와 .items() 메소드를 사용합니다.
dic={'book':1, 'computer':2} for i in dic: print(i)
book computer
for i in dic.values(): print(i)
1 2
for i in dic.items(): print(i)
('book', 1) ('computer', 2)
위 결과에 의하면 사전형은 시퀀스는 아니지만 이터러블 객체입니다.
- 시퀀스(sequence)
- 인덱스에 의해 객체의 각 요소에 순서가 정해져 있는 객체
- 리스트, 튜플, 문자열, range
- 이터러블(iterable)
- 객체의 각 요소를 개별적으로 호출할 수 있는 객체
- 지연된 평가가 이루어지는 이터레이터(iterator) 역시 이터러블
set 형은 내부의 요소들에 인덱스를 포함하지 않습니다. 즉, 각 요소에 순서를 지정할 수 없으므로 시퀀스 객체는 아닙니다. 그러나 for 문을 사용하여 요소를 하나씩 참조할 수 있습니다. 그러므로 set 형은 이터러블이지만 시퀀스는 아닙니다.
x=set([3, 1, 5, 7]); x
{1, 3, 5, 7}
for i in x: print(i)
1 3 5 7
x[0]
TypeError: 'set' object is not subscriptable
모든 객체는 기본적으로 숫자(number), 문자열(string), 리스트(list), 사전(dictionary), 집합(set) 등의 유형에 포함되며 각 자료형은 그에 대응하는 고유한 클래스(class)와 연결됩니다. 그러므로 사용자가 생성하는 객체는 이 자료형들 중 하나의 클래스와 연결되는 객체가 됩니다. 클래스는 자체적인 속성과 메서드(함수)를 가지며, 특정한 클래스와 연결된 객체는 그 클래스의 속성과 메소드를 사용할 수 있습니다. 이터러블 중 인덱스 없이 요소를 호출할 수 있는 것은 클래스에 이러한 동작을 일으키는 메소드를 가진다는 것을 의미합니다. 파이썬은 클래스를 생성할 경우 자동적으로 부여되는 내장 속성과 메소드를 제공하는데 그 중의 하나인 속성 __iter__
가 이터러블을 생성합니다.
내장 메소드 또는 속성은 이름 앞뒤로 이중 밑줄(under score)를 함유하고 있으며 파이썬에서 객체를 생성할 경우 자동적으로 부여되는 메소드입니다. 메소드(method)는 작동 영역이 한정된 함수로서 "클래스" 편에서 자세히 소개합니다.
객체의 클래스가 포함하는 속성이나 메서드는 dir(객체)
함수로 확인할 수 있습니다. 위의 set 형 객체인 x의 모든 속성과 메서드들은 다음과 같습니다.
print(dir(x))
['__and__', '__class__', '__class_getitem__', ..., 'symmetric_difference_update', 'union', 'update']
이터러블(iterable) 객체는 내장 속성인 __iter__
를 포함합니다. 연산자 in
을 사용하여 이 속성의 포함 여부를 알아봅니다.
'__iter__' in dir(x)
True
이터러블이 시퀀스보다 광의적인 개념입니다.
이터레이터(iterator)
다음 코드는 내장함수 range()
이 할당된 객체 x로서 range 형입니다. 이 타입은 파이썬의 기본 자료형(수, 문자형, 리스트, 튜플, 집합, 사전 등)에 포함되지 않은 상태이므로 평가가 이루어지지 않습니다. 즉, 객체의 구체적인 내용을 반환하지 못하는 상태로 단지 객체의 생성만을 나타냅니다.
x=range(9);x
range(0, 9)
type(x)
range
다음 결과는 객체 x의 각 요소에 인덱스가 부여 됨을 의미합니다. 또한 for 반복문
에 의해 각각의 요소를 호출할 수 있습니다. 즉, 이 객체는 스퀀스이며 이터러블입니다.
x[2]
2
for i in x: print(i, end=" ")
0 1 2 3 4 5 6 7 8
위의 인덱스와 for 문 사용외에 이 객체를 평가하기 위해서는 객체의 자료형을 평가 가능한 기본형으로 변환해야 합니다. 다음은 리스트형으로 변환하기 위해 list()
함수를 적용한 것입니다.
list(x)
[0, 1, 2, 3, 4, 5, 6, 7, 8]
객체 x와 같이 객체 생성 후 평가를 위한 부가적인 작업이 필요한 평가 방식을 지연된 평가(lazy evaluation)라 하며 이러한 방식으로 평가되는 객체들은 위와 같이 요소들을 하나씩 호출할 수 있습니다. 즉, 이터러블입니다. 이 지연된 평가와 이터러블의 두 특성을 모두 가진 객체를 이터레이터(iterator)라고 합니다. 이터레이터의 이 지연된 평가 방식은 변환 등의 부가적인 실행이 이루어질 때까지는 실행되지 않기 때문에 메모리 관리에 효율적입니다.
이터레이터(Iterator)
이터레이터는 이터러블(iterable)이면서 지연된 평가(lazy evaluation)에 의해 실행되는 객체입니다.
이터레이터 객체를 생성하는 클래스는 내장 메소드__iter__
와 __next__
를 포함합니다. 메소드 __iter__
는 이 객체가 이터러블임을 나타내고 __next__
는 지연된 방식으로 평가됨을 의미합니다. 즉, 이터레이터는 위에서 소개한 for 문이나 list() 등의 부가적인 적용 이전에 스스로 평가할 수 있는 장치인 __next__
메소드를 가지고 있습니다.
- __iter__()
- 이터러블 객체에 포함되는 내장 메소드(built-in method)
- 이 메소드 실행으로
__next__
메서드를 생성, 즉 이터레이터로 전환- 이터러블 객체.__iter__() ⇒ 이터레이터 객체 생성
- 내장 함수
iter(이터러블)
를 사용하여 이터레이터 객체를 생성할 수 있음
- __next__()
- 이터러블이면서 지연된 평가로 이루어지는 객체는 __iter__와 함께 포함되는 내장 메소드
- 객체의 요소를 하나씩 반환하는 메서드
- 이 메소드에 의해 반환된 요소는 메모리에서 삭제 됩니다.
- 모든 항목이 반환된 경우(평가된 경우) 파이썬 자체적으로 제공하는 에러(예외)의 종류인 StopIteration가 발생
- 내장 메소드 __iter__(), __next__()를 포함하는 객체인 경우 내장함수
next()
함수를 적용할 수 있음
다음 객체 a는 리스트이므로 이터러블이지만 지연된 평가방식을 따르지 않습니다. 그러므로 이 객체와 연결된 리스트 클래스는 내장 메소드 __iter__()
는 포함하지만 __next__()
는 가지고 있지 않습니다. 내장함수 dir()
로 확인할 수 있습니다.
a=[1,2,3]; a
[1, 2, 3]
'__iter__' in dir(a)
True
'__next__' in dir(a)
False
즉, 위 객체 a는 이터러블이지만 이터레이터는 아닙니다. 그러나 __iter__()
메소드를 포함하므로 다음과 같이 객체내에 이 메소드를 활성시키는 것으로 __next__()
와 연결할 수 있습니다. 결과적으로 객체 a를 이터레이터로 변환할 수 있습니다.
a1=a.__iter__() a1
<list_iterator at 0x7f2ce4b655e0>
'__next__' in dir(a1)
True
print(next(a1)) print(next(a1)) print(next(a1)) print(next(a1))
1 2 3 --------------------------------------------------------------------------- StopIteration Traceback (most recent call last) Cell In[35], line 4 2 print(next(a1)) 3 print(next(a1)) ----< 4 print(next(a1)) StopIteration:
내장 메소드 __iter__()를 포함하는 객체 즉, 이터러블 객체의 경우 내장함수 iter()
과 next()
를 사용하여 위 코드와 동일하게 이터레이터를 생성할 수 있습니다.
b=["a", 1, "b", 3] print("__iter__" in dir(b)) print("__next__" in dir(b))
True False
위 결과에 의하면 객체 b는 이터러블이지만 이터레이터는 아닙니다. iter()
함수를 사용하여 이터레이터로 전환할 수 있습니다.
b1=iter(b) print("__iter__" in dir(b1)) print("__next__" in dir(b1))
True True
객체 b1은 이터레이터입니다. 그러므로 next()
함수를 사용하여 요소를 하나씩 호출할 수 있습니다.
print(next(b1)) print(next(b1)) print(next(b1)) print(next(b1)) print(next(b1))
a 1 b 3 --------------------------------------------------------------------------- StopIteration Traceback (most recent call last) Cell In[41], line 5 3 print(next(b1)) 4 print(next(b1)) ----< 5 print(next(b1)) StopIteration:
시퀀스의 메소드
표 1은 시퀀스 객체에 적용할 수 있는 메소드를 나타냅니다.
메서드 | 내용 |
---|---|
len(seq) | 시퀀스의 크기(요소의 수)를 반환 |
element in seq | element가 seq에 포함 여부를 판단 |
seq.index(element, start, end) | seq내에 element의 인덱스를 반환 (element가 여러개 존재할 경우 첫번째 인덱스를 반환) 검색을 시작할 위치(start)와 종료위치(end)를 지정할 경우 대상의 인덱스를 반환, end는 생략 가능 |
min(seq), max(seq) | seq의 최소값, 최대값을 반환 |
seq1 + seq2 | seq1과 seq2의 결합 |
seq * n | seq를 n번 반복 |
seq1=[97, 98, 99] seq2='good morning' len(seq1)
3
len(seq2)
12
99 in seq1
True
'p' in seq2
False
seq2.index('o')
1
seq2.index('o', 3)
6
min(seq1), max(seq2)
(97, 'x')
seq1+seq2
[97, 98, 99, 'a', 'b', 'x', 'c', 'x']
seq1 *3
[97, 98, 99, 97, 98, 99, 97, 98, 99]
댓글
댓글 쓰기