(study) 파이썬 입문 (12주차) 5월23일
클래스 공부 4단계, 5단계
-
(1/4) motivating example
-
(2/4) __str__
, 파이썬의 비밀2
-
(3/4) __repr__
, 파이썬의 비밀3
-
(4/4) 주피터노트북의 비밀 (_repr_html_
), __repr__
와 __str__
의 우선적용 순위
import numpy as np
-
가위바위보
class RPC:
def __init__(self,candidate=['가위','바위','보']):
self.candidate = candidate
def throw(self):
print(np.random.choice(self.candidate))
a=RPC() # __init__는 암묵적으로 실행
a.throw()
-
생각해보니까 throw는 choose + show 의 결합인것 같다.
class RPC: ## 시점1
def __init__(self,candidate=['가위','바위','보']):
self.candidate = candidate
def choose(self):
self.actions = np.random.choice(self.candidate)
def show(self):
print(self.actions)
a=RPC() ## 시점2
a.actions ## 시점3
a.choose() ## 시점4
a.actions ## 시점5
a.show() ## 시점6
보충학습: 위와 같은코드
class _RPC: ## 시점1
pass # <- 이렇게하면 아무기능이 없는 비어있는 클래스가 정의된다
_a = _RPC() ## 시점2
def _init(_a,candidate=['가위','바위','보']):
_a.candidate = candidate
_init(_a)
_a.actions ## 시점3
def _choose(_a): ## 시점4
_a.actions = np.random.choice(_a.candidate)
_choose(_a)
_a.actions ## 시점5
def _show(_a): ## 시점6
print(_a.actions)
_show(_a)
-
또 다른 인스턴스 b를 만들자. b는 가위만 낼 수 있다.
b=RPC(['가위'])
b.candidate
b.choose()
b.show()
-
a,b의 선택들을 모아서 기록을 하고 싶다.
class RPC:
def __init__(self,candidate=['가위','바위','보']):
self.candidate = candidate
self.actions = list()
def choose(self):
self.actions.append(np.random.choice(self.candidate))
def show(self):
print(self.actions[-1])
a=RPC()
b=RPC(['가위'])
for i in range(5):
a.choose()
a.show()
a.actions
for i in range(5):
b.choose()
b.show()
b.actions
-
info라는 함수를 만들어서 a,b 오브젝트가 가지고 있는 정보를 모두 보도록 하자.
(예비학습) 문자열 \n
이 포함된다면?
'asdf\n1234'
print('asdf\n1234')
예비학습 끝
class RPC:
def __init__(self,candidate=['가위','바위','보']):
self.candidate = candidate
self.actions = list()
def choose(self):
self.actions.append(np.random.choice(self.candidate))
def show(self):
print(self.actions[-1])
def info(self):
print("낼 수 있는 패: {}\n기록: {}".format(self.candidate,self.actions))
a=RPC()
b=RPC(['가위'])
for i in range(5):
a.choose()
a.show()
for i in range(5):
b.choose()
b.show()
a.info()
b.info()
-
만들고보니까 info와 print의 기능이 거의 비슷함 $\to$ print(a)를 하면 a.info()와 동일한 효과를 내도록 만들 수 있을까?
-
말도 안되는 소리같다. 왜?
- 이유1: print는 파이썬 내장기능, 내장기능을 우리가 맘대로 커스터마이징해서 쓰기는 어려울 것 같다.
- 이유2: 이유1이 해결된다고 쳐도 문제다. 그럼 지금까지 우리가 사용했던 수 많은 print()의 결과는 어떻게 되는가?
-
그런데 a의 자료형(RPC자료형)에 해당하는 오브젝트들에 한정하여 print를 수정하는 방법이 가능하다면? (그럼 다른 오브젝트들은 수정된 print에 영향을 받지 않음)
-
관찰1: 현재 print(a)의 결과는 아래와 같다.
print(a)
- a는 RPC클래스에서 만든 오브젝트이며 a가 저장된 메모리 주소는 0x7fef32fef8b0라는 의미
-
관찰2: a에는 __str__
이 있다.
set(dir(a)) & {'__str__'}
이것을 함수처럼 사용하니까 아래와 같이 된다.
a.__str__()
?? print(a)를 해서 나오는 문자열이 리턴된다..
print(a.__str__()) # 이거 print(a)를 실행한 결과와 같다?
-
생각: 만약에 내가 a.__str__()
라는 함수를 재정의하여 리턴값을 'guebin hahaha'로 바꾸게 되면 print(a)해서 나오는 결과는 어떻게 될까? (약간 해커같죠)
(예비학습) 함수덮어씌우기
def f():
print('asdf')
f()
def f():
print('guebin hahaha')
f()
이런식으로 함수가 이미 정의되어 있더라도, 내가 나중에 덮어씌우면 그 함수의 기능을 다시 정의한다.
(해킹시작)
class RPC:
def __init__(self,candidate=['가위','바위','보']):
self.candidate = candidate
self.actions = list()
def choose(self):
self.actions.append(np.random.choice(self.candidate))
def show(self):
print(self.actions[-1])
def __str__(self):
return 'guebin hahaha'
def info(self):
print("낼 수 있는 패: {}\n기록: {}".format(self.candidate,self.actions))
a=RPC()
print(a)
print(a.__str__())
-
__str__
의 리턴값을 info에서 타이핑했던 문자열로 재정의한다면?
class RPC:
def __init__(self,candidate=['가위','바위','보']):
self.candidate = candidate
self.actions = list()
def choose(self):
self.actions.append(np.random.choice(self.candidate))
def show(self):
print(self.actions[-1])
def __str__(self):
return "낼 수 있는 패: {}\n기록: {}".format(self.candidate,self.actions)
a=RPC()
print(a)
a.choose()
a.show()
print(a)
-
print(a)
와 print(a.__str__())
는 같은 문법이다.
-
참고로 a.__str__()
와 str(a)
도 같은 문법이다.
a.__str__()
str(a)
-
지금까지 우리가 썼던 기능들 확인!
(예제1)
a=[1,2,3]
print(a)
a.__str__()
str(a)
(예제2)
a={1,2,3}
print(a)
a.__str__()
str(a)
(예제3)
a=np.array(1)
a.shape
print(a.shape)
a.shape.__str__()
str(a.shape)
(예제4)
a=range(10)
print(a)
a.__str__()
str(a)
(예제5)
a = np.arange(100).reshape(10,10)
print(a)
a.__str__()
str(a)
-
생각해보니까 print를 해서 우리가 원하는 정보를 확인하는건 아니었음
a=[1,2,3]
a
print(a)
-
a + 엔터
는 print(a) + 엔터
와 같은효과인가?
(반례)
a=np.array([1,2,3,4]).reshape(2,2)
a
print(a)
-
a + 엔터
는 print(a) + 엔터
가 다른 경우도 있다. $\to$ 추측: 서로 다른 숨겨진 기능이 있다! $\to$ 결론: 추측이 맞다. 그 기능은 __repr__
에 저장되어있음.
class RPC:
def __init__(self,candidate=['가위','바위','보']):
self.candidate = candidate
self.actions = list()
def choose(self):
self.actions.append(np.random.choice(self.candidate))
def show(self):
print(self.actions[-1])
def __repr__(self):
return "낼 수 있는 패: {}\n기록: {}".format(self.candidate,self.actions)
a=RPC()
a # print(a.__repr__())
-
그럼 우리가 지금까지 했던것은?
a = np.array([1,2,3])
a
print(a)
a.__repr__()
a.__str__()
-
대화형콘솔에서 오브젝트이름+엔터
를 쳐서 나오는 출력은 __repr__
의 결과와 연관있다.
a = np.array(range(10000)).reshape(100,100)
a
print(a)
a.__repr__()
-
참고로 a.__repr__()
은 repr(a)
와 같다.
representation
repr(a)
-
요즘에는 IDE의 발전에 따라서 오브젝트이름+엔터
칠떄 나오는 출력의 형태도 다양해지고 있음
통합 개발 환경(Integrated Development Environment, IDE)이란 공통된 개발자 툴을 하나의 그래픽 사용자 인터페이스(Graphical User Interface, GUI)로 결합하는 애플리케이션을 구축하기 위한 소프트웨어입니다
import pandas as pd
df = pd.DataFrame({'a':[1,2,3],'b':[2,3,4]})
df
- 예쁘게나온다.
-
위의결과는 print(df.__repr__())
의 결과와 조금 다르게 나온다?
print(df.__repr__())
-
print(df.__repr__())
는 예전 검은화면에서 코딩할때가 나오는 출력임
Python 3.10.2 | packaged by conda-forge | (main, Feb 1 2022, 19:28:35) [GCC 9.4.0] on linux
Type "help", "copyright", "credits" or "license" for more information.
> >> import pandas as pd
>>> df = pd.DataFrame({'a':[1,2,3],'b':[2,3,4]})>>> df
a b
0 1 2
1 2 3
2 3 4
>>>
-
주피터에서는? "오브젝트이름+엔터"치면 HTML(df._repr_html_())
이 실행되고, _repr_html_()
이 정의되어 있지 않으면 print(df.__repr__())
이 실행된다.
df._repr_html_()
- html코드!
from IPython.core.display import HTML
HTML(df._repr_html_())
set(dir(df)) & {'_repr_html_'}
-
물론 df._repr_html_()
함수가 내부적으로 있어도 html이 지원되지 않는 환경이라면 print(df.__repr__())
이 내부적으로 수행된다.
(예제1)
-
아래의 예제를 관찰하자.
class RPC:
def __init__(self,candidate=['가위','바위','보']):
self.candidate = candidate
self.actions = list()
def choose(self):
self.actions.append(np.random.choice(self.candidate))
def show(self):
print(self.actions[-1])
def __repr__(self):
return "낼 수 있는 패: {}\n기록: {}".format(self.candidate,self.actions)
a=RPC()
a
a.__repr__()
repr(a)
-
여기까지는 상식수준의 결과임. 이제 아래를 관찰하라.
print(a) # print(a.__str__())
a.__str__()
str(a)
-
__str__()
은 건드린적이 없는데? $\to$ 건드린적은 없는데 기능이 바뀌어있음
a.__str__??
a.__repr__??
(예제2)
-
아래의 예제를 관찰하자.
class RPC:
def __init__(self,candidate=['가위','바위','보']):
self.candidate = candidate
self.actions = list()
def choose(self):
self.actions.append(np.random.choice(self.candidate))
def show(self):
print(self.actions[-1])
def __str__(self):
return "낼 수 있는 패: {}\n기록: {}".format(self.candidate,self.actions)
a=RPC()
print(a)
a
a.__str__()
a.__repr__()
a.__str__??
a.__repr__??
(예제3)
class RPC:
def __init__(self,candidate=['가위','바위','보']):
self.candidate = candidate
self.actions = list()
def choose(self):
self.actions.append(np.random.choice(self.candidate))
def show(self):
print(self.actions[-1])
def __repr__(self):
return "guebin hahaha"
def __str__(self):
return "낼 수 있는 패: {}\n기록: {}".format(self.candidate,self.actions)
a=RPC()
a
print(a)
-
__str__
와 __repr__
을 건드리지 않고 출력결과를 바꾸고 싶다면?
class RPC:
def __init__(self,candidate=['가위','바위','보']):
self.candidate = candidate
self.actions = list()
def choose(self):
self.actions.append(np.random.choice(self.candidate))
def show(self):
print(self.actions[-1])
def _repr_html_(self):
html_str = """
낼 수 있는 패: {} <br/>
기록: {}
"""
return html_str.format(self.candidate,self.actions)
a=RPC()
str(a)
repr(a)
a
for i in range(5):
a.choose()
a.show()
a
아래의 클래스를 수정하여
class RPC:
def __init__(self,candidate=['가위','바위','보']):
self.candidate = candidate
self.actions = list()
def choose(self):
self.actions.append(np.random.choice(self.candidate))
def show(self):
print(self.actions[-1])
def _repr_html_(self):
html_str = """
낼 수 있는 패: {} <br/>
기록: {}
"""
return html_str.format(self.candidate,self.actions)
클래스에서 생성된 인스턴스의 출력결과가 아래와 같도록 하라.
학번: 202143052
낼 수 있는 패: ['가위', '바위', '보']
기록: ['가위', '가위', '보', '보', '바위']
class RPC:
def __init__(self,candidate=['가위','바위','보']):
self.candidate = candidate
self.actions = list()
def choose(self):
self.actions.append(np.random.choice(self.candidate))
def show(self):
print(self.actions[-1])
def _repr_html_(self):
html_str = """
학번: 202150754 <br/>
낼 수 있는 패: {} <br/>
기록: {}
"""
return html_str.format(self.candidate,self.actions)
a=RPC()
for i in range(5):
a.choose()
a.show()
a
-
(1/4) __add__
(1)
-
(2/4) __add__
(2)
-
(3/4) __add__
(3)
-
(4/4) __mul__
import numpy as np
-
지난시간까지 배운것: RPC자료형에 한정해서 print()등의 기능을 조작할 수 있었다. (재정의 할 수 있었다)
-
이번시간에 배울것: 특정자료형에 한정하여 print 이외의 파이썬 내부기능을 조작하여 보자. (재정의하여 보자)
-
아래의 연산구조를 관찰하자.
a=1
b=2
a+b
- a라는 인스턴스와 b라는 인스턴스를 +라는 기호가 연결하고 있다.
-
이번에는 아래의 연산구조를 관찰하자.
a=[1,2]
b=[3,4]
a+b
- a라는 인스턴스와 b라는 인스턴스를 +라는 기호가 연결하고 있다.
-
동작이 다른 이유?
- 클래스를 배우기 이전: int자료형의
+
는 "정수의 덧셈"을 의미하고 list자료형의+
는 "자료의 추가"를 의미한다. - 클래스를 배운 이후: 아마 클래스는
+
라는 연산을 정의하는 숨겨진 메소드가 있을것이다. (print가 그랬듯이) 그리고 int클래스에서는 그 메소드를 "정수의 덧셈"이 되도록 정의하였고 list클래스에서는 그 메소드를 "자료의 추가"를 의미하도록 정의하였다.
-
아래의 결과를 관찰
a=1
b=2
a.__add__(b)
b.__add__(a)
a=[1,2]
b=[3,4]
a.__add__(b)
b.__add__(a)
-
a+b는 사실 내부적으로 a.__add__(b)
의 축약구문이다. 따라서 만약 a.__add__(b)
의 기능을 바꾸면 (재정의하면) a+b의 기능도 바뀔 것이다.
-
학생예제
class Student:
def __init__(self,age=20.0, semester=0):
self.age = age
self.semester = semester
print("입학을 축하합니다. 당신의 나이는 {}이고 현재 학기는 {}학기 입니다.".format(self.age,self.semester))
def __add__(self,val):
# val == 0: 휴학
# val == 1: 등록
if val==0:
self.age=self.age+0.5
elif val==1:
self.age=self.age+0.5
self.semester= self.semester+1
def _repr_html_(self):
html_str = """
나이: {} <br/>
학기: {} <br/>
"""
return html_str.format(self.age,self.semester)
iu= Student()
iu
iu + 1 ## 1학년 1학기 등록
iu
iu + 0 ## 휴학함
iu
-
연산을 연속으로 하고 싶다.
iu + 1 + 0 + 0 + 0 + 0
-
에러의 이유?
(되는코드)
(1+1)+1 # 1+1+1은 이렇게 볼 수 있다
_a = (1+1)
type(_a)
_a+1 # 이 연산은 int인스턴스 + int인스턴스
(안되는코드)
iu+1+1
_a=iu+1
type(_a)
_a+1
-
에러를 해결하는 방법: iu+1의 결과로 Student클래스의 인스턴스가 리턴되면 된다.
class Student:
def __init__(self,age=20.0, semester=0):
self.age = age
self.semester = semester
print("입학을 축하합니다. 당신의 나이는 {}이고 현재 학기는 {}학기 입니다.".format(self.age,self.semester))
def __add__(self,val):
# val == 0: 휴학
# val == 1: 등록
if val==0:
self.age=self.age+0.5
elif val==1:
self.age=self.age+0.5
self.semester= self.semester+1
return self
def _repr_html_(self):
html_str = """
나이: {} <br/>
학기: {} <br/>
"""
return html_str.format(self.age,self.semester)
iu = Student()
iu+1 # __add__의 return에 Student클래스의 인스턴스가 리턴되면서 자동으로 _repr_html_() 실행
iu+1 +0+0+0+0
class RPC:
def __init__(self,candidate=['가위','바위','보']):
self.candidate = candidate
self.actions = list()
self.results = list()
def __mul__(self,other):
self.choose()
other.choose()
if self.actions[-1]=='가위' and other.actions[-1]=='가위':
self.results.append(0)
other.results.append(0)
if self.actions[-1]=='가위' and other.actions[-1]=='바위':
self.results.append(-1)
other.results.append(1)
if self.actions[-1]=='가위' and other.actions[-1]=='보':
self.results.append(1)
other.results.append(-1)
if self.actions[-1]=='바위' and other.actions[-1]=='가위':
self.results.append(1)
other.results.append(-1)
if self.actions[-1]=='바위' and other.actions[-1]=='바위':
self.results.append(0)
other.results.append(0)
if self.actions[-1]=='바위' and other.actions[-1]=='보':
self.results.append(-1)
other.results.append(1)
if self.actions[-1]=='보' and other.actions[-1]=='가위':
self.results.append(-1)
other.results.append(1)
if self.actions[-1]=='보' and other.actions[-1]=='바위':
self.results.append(1)
other.results.append(-1)
if self.actions[-1]=='보' and other.actions[-1]=='보':
self.results.append(0)
other.results.append(0)
def choose(self):
self.actions.append(np.random.choice(self.candidate))
def _repr_html_(self):
html_str = """
낼 수 있는 패: {} <br/>
액션: {} <br/>
승패: {}
"""
return html_str.format(self.candidate,self.actions,self.results)
a=RPC()
b=RPC()
a
b
a*b
a
b
for i in range(5):
a*b
a
b
for i in range(50000):
a*b
sum(a.results)/len(a.results)
sum(b.results)/len(a.results)
RPC클래스에서 player a와 player b를 만들어라. Player a는 ['가위','보'] 중에 하나를 낼 수 있다. 그리고 Player b는 ['가위','바위'] 중에 하나를 낼 수 있다. 두 player는 가지고 있는 패를 (같은확률로) 랜덤으로 낸다. (즉 player a가 가위만 내거나 보만 내는 경우는 없다.)
(1) 누가 더 유리한가? 이유를 스스로 생각해보라. (이유를 정리하여 숙제로 제출할 필요 없음)
(2) 50000번의 시뮬레이션을 해보고 결과를 분석해보라.
a=RPC(candidate=['가위','보'])
b=RPC(candidate=['가위','바위'])
for i in range(100):
a*b
a
b
sum(a.results)
sum(b.results)
b가 더 많이 이김