import numpy as np
import pandas as pd14wk-2: 클래스공부 4단계 – 파이썬의 비밀 (3)
강의영상
youtube: https://youtube.com/playlist?list=PLQqh36zP38-yZWViSW4CktNpOM7Km4BAe
imports
클래스공부 4단계: 파이썬의 비밀 (3)
비밀12: __iter__ (\(\star\star\star\star\star\))
for문의 복습
- 아래와 같은 예제들을 관찰하여 for문을 복습하자.
(예제1) [1,2,3,4]
for i in [1,2,33,4]:
print(i)1
2
33
4
(예제2) (1,2,3,4)
for i in (1,2,33,4):
print(i)1
2
33
4
(예제3) ‘1234’
for i in '123asdf':
print(i)1
2
3
a
s
d
f
(예제4) 5
for i in 5:
print(i)TypeError: 'int' object is not iterable
- 의문1:
for i in ???:
print(i)에서 ??? 자리에 올수 있는 것이 무엇일까?
(예제5) [[1,2,3,4],[3,4,5,6]], pd.DataFrame([[1,2,3,4],[3,4,5,6]])
lst = [[1,2,3,4],[3,4,5,6]]
lst[[1, 2, 3, 4], [3, 4, 5, 6]]
for l in lst:
print(l)[1, 2, 3, 4]
[3, 4, 5, 6]
for l in np.array(lst):
print(l)[1 2 3 4]
[3 4 5 6]
for l in pd.DataFrame(lst):
print(l)0
1
2
3
- 데이터프레임인 경우는 colname이 반복
df = pd.DataFrame({'x':[1,2,3],'y':[2,3,4]})
df| x | y | |
|---|---|---|
| 0 | 1 | 2 |
| 1 | 2 | 3 |
| 2 | 3 | 4 |
for d in df:
print(d)x
y
- 의문2: for의 출력결과는 어떻게 예측할 수 있을까?
for문의 동작원리
- 의문1의 해결: 아래의 ??? 자리에 올 수 있는 것은 dir()하여 __iter__가 있는 object이다.
for i in ???:
print(i)이러한 오브젝트를 iterable object라고 한다.
- 예제1~4 확인
lst = [1,2,3]
set(dir(lst)) & {'__iter__'}{'__iter__'}
tpl = 1,2,3
set(dir(tpl)) & {'__iter__'}{'__iter__'}
string = '123'
set(dir(string)) & {'__iter__'}{'__iter__'}
a = 5
set(dir(a)) & {'__iter__'}set()
- __iter__의 역할: iterable object를 iterator로 만들 수 있다!
lst = [1,22,-33]lst_iterator = lst.__iter__()
# lst_iterator = iter(lst)- iterator가 되면 무엇이 좋은가? -> 숨겨진 기능 __next__가 열린다.
set(dir(lst_iterator)) & {'__next__'}{'__next__'}
- 그래서 __next__의 기능은? -> 원소를 차례대로 꺼내준다 + 더 이상 꺼낼 원소가 없으면 StopIteration Error를 발생시킨다.
next(lst_iterator)
# lst_iterator.__next__() # 같은코드1
next(lst_iterator)
# lst_iterator.__next__() # 같은코드22
next(lst_iterator)
# lst_iterator.__next__() # 같은코드-33
next(lst_iterator)
# lst_iterator.__next__() # 같은코드StopIteration:
- for문의 동작원리
for i in iterable:
...이터레이터생성:
.__iter__()혹은iter()을 이용하여 iterable을 iterator로 만든다.\(i\)생성 및 반복: 1에서 만들어진 iterator에서
.__next__()함수를 호출하고 결과를 \(i\)에 저장한뒤 for문 블락안의 내용 (들여쓰기 된 내용) 을 실행한다. \(\to\) 반복한다.정지:
.__next__()함수를 호출할때 StopIteration Error가 나오면 for문을 멈춘다.
flowchart LR
A[iterable] --> |"__iter__()"| B(iterator)
B --> |"__next__()"| C{stop?}
C --> |NO| D[i] --> E[...] --> B
C --> |YES| F[end]
- 아래의 구조도 잘 돌아갈까?
for i in iterator:
print(i)iterator의 iter가 자기자신을 리턴하도록 하는 트릭을 쓰면 “1.이터레이터생성 2.\(i\)생성 및 반복 3.정지”의 동작원리를 수정하지 않고 for문을 안전하게 돌릴 수 있다.
flowchart LR
A(iterator) --> |"__iter__()"| B(iterator)
B --> |"__next__()"| C{stop?}
C --> |NO| D[i] --> E[...] --> B
C --> |YES| F[end]
- 요약
- iterable object는 숨겨진 기능으로
__iter__를 가진다. - iterator는 숨겨진 기능으로
__iter__와__next__를 가진다. 따라서 정의상 iterator는 그 자체로 iterable object가 된다! - iterator의
__iter__는 자기자신을 리턴한다.
- 의문2의 해결: for의 출력결과는 어떻게 예측할 수 있을까? iterator를 만들어서 .__next__()의 출력값을 확인하면 알 수 있다.
_df_itertor = iter(df)next(_df_itertor)'x'
next(_df_itertor)'y'
next(_df_itertor)StopIteration:
사용자정의 이터레이터
- 내가 이터레이터를 만들어보자.
- 찌를 내는 순간 for문이 멈추도록 하는 이터레이터를 만들자.
class RPS_ITERATOR: # 찌를 내는순간 for문이 멈추도록 하는 이터레이터를 만들자
def __init__(self):
self.candidate = ["묵","찌","빠"]
def __iter__(self):
return self
def __next__(self):
action = np.random.choice(self.candidate)
if action == "찌":
print("찌가 나와서 for문을 멈춥니다")
raise StopIteration
else:
return actiona = RPS_ITERATOR()a.__next__()'빠'
a.__next__()'묵'
a.__next__()'묵'
a.__next__()찌가 나와서 for문을 멈춥니다
StopIteration:
for i in a:
print(i)빠
묵
묵
빠
빠
찌가 나와서 for문을 멈춥니다
range()
- 파이썬에서 for문을 처음 배울 때: range(5)를 써라!
for i in range(5):
print(i)0
1
2
3
4
- range(5)가 도데체 무엇이길래?
- range(5)의 정체는 그냥 iterable object이다.
set(dir(range(5))) & {'__iter__','__next__'}{'__iter__'}
- 그래서 언제든지 iterator로 바꿀 수 있다.
rtor= iter(range(5))
rtor<range_iterator at 0x7f9a8e84f5d0>
set(dir(rtor)) & {'__iter__','__next__'}{'__iter__', '__next__'}
- for문에서 range(5)가 행동하는 방법?
rtor = iter(range(5))rtor.__next__()0
rtor.__next__()1
rtor.__next__()2
rtor.__next__()3
rtor.__next__()4
rtor.__next__()StopIteration:
zip
- 이터레이터의 개념을 알면 for문에 대한 이해도가 대폭 상승한다.
for i,j in zip([1,2,3],'abc'):
print(i,j)1 a
2 b
3 c
- zip은 뭐지?
zip([1,2,3],'abc')<zip at 0x7f9a8e7dd5c0>
- 어차피 for i in ????: 의 ???? 자리는 iterable object의 자리이다.
set(dir(zip([1,2,3],'abc'))) & {'__iter__','__next__'}{'__iter__', '__next__'}
__next__()함수가 있음 \(\to\)zip([1,2,3],'abc')은 그자체로 iterator 였다!
z= zip([1,2,3],'abc')z.__next__()(1, 'a')
z.__next__()(2, 'b')
z.__next__()(3, 'c')
z.__next__()StopIteration:
또다른 이해: 그러고보니까 zip([1,2,3],'abc')은 뭐하는 문법이지?
zip?Init signature: zip(self, /, *args, **kwargs) Docstring: zip(*iterables) --> A zip object yielding tuples until an input is exhausted. >>> list(zip('abcdefg', range(3), range(4))) [('a', 0, 0), ('b', 1, 1), ('c', 2, 2)] The zip object yields n-length tuples, where n is the number of iterables passed as positional arguments to zip(). The i-th element in every tuple comes from the i-th iterable argument to zip(). This continues until the shortest argument is exhausted. Type: type Subclasses:
- 너 클래스였어?
iterator_from_zip = zip.__call__([1,2,3],'abc')for i,j in iterator_from_zip:
print(i,j)1 a
2 b
3 c
파이썬의 작은 비밀: zip은 iterator를 찍어내는 클래스이다.
enumerate
- zip의 짝궁으로 enumerate가 있음
for i,s in enumerate('abc'):
print(i,s)0 a
1 b
2 c
- enumerate('abc')도 문법상 iterable object 아니면 iterator 임.
set(dir(enumerate('abc'))) & {'__iter__', '__next__'}{'__iter__', '__next__'}
- iterator 였군
iterator = enumerate('abc')next(iterator)(0, 'a')
next(iterator)(1, 'b')
next(iterator)(2, 'c')
next(iterator)StopIteration:
- 참고: enumerate 는 클래스임
enumerate?Init signature: enumerate(iterable, start=0) Docstring: Return an enumerate object. iterable an object supporting iteration The enumerate object yields pairs containing a count (from start, which defaults to zero) and a value yielded by the iterable argument. enumerate is useful for obtaining an indexed list: (0, seq[0]), (1, seq[1]), (2, seq[2]), ... Type: type Subclasses:
파이썬의 작은 비밀: enumerate 역시 iterator를 찍어내는 클래스다.
파이썬의 비밀12: iterator나 iterable object만 for문과 함께 사용할 수 있다.
비밀을 알아서 좋은점
파이썬의 에러메시지 이해
TypeError: 타입이 맞지 않는 연산을 수행하려고 할 때 발생. 예를 들어, 숫자와 문자열을 더하려고 할 때 발생.
result = 10 + "20" # TypeError: unsupported operand type(s) for +: 'int' and 'str'TypeError: unsupported operand type(s) for +: 'int' and 'str'
SyntaxError: 코드의 구문이 잘못되었을 때 발생. 주로 오타, 괄호 불일치, 콜론(:) 빠뜨림 등의 문제로 인해 발생.
if x == 5
print("x is equal to 5") # SyntaxError: invalid syntaxSyntaxError: invalid syntax (<ipython-input-23-5259b3c47c58>, line 1)
NameError: 정의되지 않은 변수나 함수를 사용하려고 할 때 발생.
print(unknown_variable) # NameError: name 'unknown_variable' is not definedNameError: name 'unknown_variable' is not defined
ValueError: 함수나 메서드에 전달되는 인자의 값이 올바르지 않을 때 발생.
int_value = int("abc") # ValueError: invalid literal for int() with base 10: 'abc'ValueError: invalid literal for int() with base 10: 'abc'
AttributeError: 객체가 속성(attribute)이나 메서드(method)를 가지고 있지 않을 때 발생
my_list = [1, 2, 3]
my_list.append(4)
my_list.upper() # AttributeError: 'list' object has no attribute 'upper'AttributeError: 'list' object has no attribute 'upper'
ImportError: 모듈을 임포트하는 과정에서 문제가 발생할 때 발생. 임포트하려는 모듈이 존재하지 않거나, 임포트 경로 설정이 잘못된 경우에 해당.
import non_existent_module # ImportError: No module named 'non_existent_module'ModuleNotFoundError: No module named 'non_existent_module'
IOError: 파일 입출력 작업 중에 발생하는 에러. 파일이 존재하지 않거나, 파일에 쓰기 권한이 없는 경우 등에 해당.
file = open('file.txt', 'r') # IOError: [Errno 2] No such file or directory: 'file.txt'FileNotFoundError: [Errno 2] No such file or directory: 'file.txt'
KeyboardInterrupt: 사용자가 프로그램의 실행을 중단할 때 발생.
while True:
pass # 무한 루프
# KeyboardInterrupt: KeyboardInterrupt:
IndexError: 유효하지 않은 인덱스를 사용하여 시퀀스(리스트, 튜플, 문자열 등)의 요소에 접근하려고 할 때 발생.
my_list = [1, 2, 3]
print(my_list[5]) # IndexError: list index out of rangeIndexError: list index out of range
KeyError: 딕셔너리에서 존재하지 않는 키를 사용하여 요소에 접근하려고 할 때 발생.
my_dict = {'name': 'John', 'age': 25}
print(my_dict['address']) # KeyError: 'address'KeyError: 'address'
FileNotFoundError: 존재하지 않는 파일을 열려고 할 때 발생.
file = open('nonexistent_file.txt') # FileNotFoundError: [Errno 2] No such file or directory: 'nonexistent_file.txt'FileNotFoundError: [Errno 2] No such file or directory: 'nonexistent_file.txt'
ZeroDivisionError: 0으로 나누기 연산을 수행하려고 할 때 발생.
result = 10 / 0 # ZeroDivisionError: division by zeroZeroDivisionError: division by zero
TypeError에 대한 심층분석
- .__add__ 가 올바르게 정의되지 않아서 생기는 경우
---------------------------------------------------------------------------
TypeError Traceback (most recent call last)
Cell In[37], line 1
----> 1 boram + '등록'+ '휴학' + '등록' + '휴학'
TypeError: unsupported operand type(s) for +: 'NoneType' and 'str'
- .__getitem__이 정의되지 않은 오브젝트에서 인덱싱을 시도할때 생기는 오류
---------------------------------------------------------------------------
TypeError Traceback (most recent call last)
Cell In[57], line 1
----> 1 a[0], a[1]
TypeError: 'RPS' object is not subscriptable
- .__setitem__이 정의되지 않은 오브젝트에서 할당을 시도할때 생기는 오류
---------------------------------------------------------------------------
TypeError Traceback (most recent call last)
Cell In[77], line 1
----> 1 a[0] = '보'
TypeError: 'RPS_Ver2' object does not support item assignment
- .__len__ 이 정의되지 않은 오브젝트에서 len을 이용할때 생기는 오류
---------------------------------------------------------------------------
TypeError Traceback (most recent call last)
Cell In[122], line 1
----> 1 len(a)
TypeError: object of type 'RPS_Ver3' has no len()
- .__call__이 정의되지 않은 오브젝트를 함수처럼 이용하려 할때 생기는 오류
---------------------------------------------------------------------------
TypeError Traceback (most recent call last)
Cell In[402], line 1
----> 1 a()
TypeError: 'Klass' object is not callable
- .__iter__가 정의되지 않은 오브젝트를 iterable 오브젝트처럼 사용하려 할때 생기는 오류
---------------------------------------------------------------------------
TypeError Traceback (most recent call last)
Cell In[422], line 2
1 a=5
----> 2 for i in a:
3 print(i)
TypeError: 'int' object is not iterable
숙제
(1) 아래의 코드를 관찰하라.
iterator = enumerate('abcf',1)next(iterator)(1, 'a')
next(iterator)(2, 'b')
next(iterator)(3, 'c')
next(iterator)(4, 'f')
next(iterator)StopIteration:
이를 바탕으로 enumerate('abcf',1)의 기능을 유추해볼 것.
(2) enumrate를 이용하여 아래의 코드를 개선하여라.
lst = ['아이언맨','토르','헐크','블랙위도우','로키']
for i in range(len(lst)):
print('{}: {}'.format(i+1,lst[i]))1: 아이언맨
2: 토르
3: 헐크
4: 블랙위도우
5: 로키
(3) 앞면과 뒷면이 나올 확률이 각각 1/2인 동전을 생각하자. 하니와 규빈은 이 동전을 연속으로 던져서 아래와 같은 룰을 정하여 내기를 하였다.
- 동전을 연속으로 반복하여 던진다. 최근 2회의 결과가 (앞면,앞면) 이 나오면 하니의 승리
- 동전을 연속으로 반복하여 던진다. 최근 2회의 결과가 (뒷면,뒷면) 이 나오면 규빈의 승리
예를들어 동전을 반복하여 던져 아래와 같이 나온다면 하니의 승리이다.
- (앞면, 뒷면, 앞면, 뒷면, 앞면, 앞면)
- (앞면, 뒷면, 앞면, 앞면)
동전을 반복하여 던져 아래와 같이 나온다면 규빈의 승리이다.
- (앞면, 뒷면, 앞면, 뒷면, 뒷면)
- (뒷면, 앞면, 뒷면, 뒷면)
이 내기는 하니가 유리한가? 규빈이 유리한가? 시뮬레이션을 통해 검증하라.
hint: 당연히 5:5겠죠?
(4) 앞면과 뒷면이 나올 확률이 각각 1/2인 동전을 생각하자. 하니와 규빈은 이 동전을 연속으로 던져서 아래와 같은 룰을 정하여 내기를 하였다.
- 동전을 연속으로 반복하여 던진다. 최근 2회의 결과가 (뒷면,앞면) 이 나오면 하니의 승리
- 동전을 연속으로 반복하여 던진다. 최근 2회의 결과가 (뒷면,뒷면) 이 나오면 규빈의 승리
이 내기는 하니가 유리한가? 규빈이 유리한가? 시뮬레이션을 통해 검증하라.
hint: 이 경우도 똑같이 유리합니다.
(5) 앞면과 뒷면이 나올 확률이 각각 1/2인 동전을 생각하자. 하니와 규빈은 이 동전을 연속으로 던져서 아래와 같은 룰을 정하여 내기를 하였다.
- 동전을 연속으로 반복하여 던진다. 최근 2회의 결과가 (앞면,뒷면) 이 나오면 하니의 승리
- 동전을 연속으로 반복하여 던진다. 최근 2회의 결과가 (뒷면,뒷면) 이 나오면 규빈의 승리
이 내기는 하니가 유리한가? 규빈이 유리한가? 시뮬레이션을 통해 검증하라.
hint: 이 내기는 하니가 유리합니다. 저는 1000회 시뮬레이션 결과
{'하니': 761, '규빈': 239} ## 하니가 761번 승리
와 같이 결과가 나왔습니다. 왜 하니가 유리한지 스스로 고민해보시기 바랍니다. (질문해도 되고요)