강의영상

- (1/4) motivating example

- (2/4) __str__, 파이썬의 비밀2

- (3/4) __repr__, 파이썬의 비밀3

- (4/4) 주피터노트북의 비밀 (_repr_html_), __repr____str__의 우선적용 순위

imports

import numpy as np

클래스공부 4단계

Motivating Example

- 가위바위보

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
---------------------------------------------------------------------------
AttributeError                            Traceback (most recent call last)
/tmp/ipykernel_577026/4082246120.py in <module>
----> 1 a.actions ## 시점3

AttributeError: 'RPC' object has no attribute 'actions'
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
---------------------------------------------------------------------------
AttributeError                            Traceback (most recent call last)
/tmp/ipykernel_577026/2546139601.py in <module>
----> 1 _a.actions ## 시점3

AttributeError: '_RPC' object has no attribute 'actions'
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'
'asdf\n1234'
print('asdf\n1234')
asdf
1234

예비학습 끝

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에 영향을 받지 않음)

__str__

- 관찰1: 현재 print(a)의 결과는 아래와 같다.

print(a)
<__main__.RPC object at 0x7fe0b1160d90>
  • a는 RPC클래스에서 만든 오브젝트이며 a가 저장된 메모리 주소는 0x7fef32fef8b0라는 의미

- 관찰2: a에는 __str__이 있다.

set(dir(a)) & {'__str__'}
{'__str__'}

이것을 함수처럼 사용하니까 아래와 같이 된다.

a.__str__()
'<__main__.RPC object at 0x7fe0b1160d90>'

?? print(a)를 해서 나오는 문자열이 리턴된다..

print(a.__str__()) # 이거 print(a)를 실행한 결과와 같다?
<__main__.RPC object at 0x7fe0b1160d90>

- 생각: 만약에 내가 a.__str__()라는 함수를 재정의하여 리턴값을 'guebin hahaha'로 바꾸게 되면 print(a)해서 나오는 결과는 어떻게 될까? (약간 해커같죠)

(예비학습) 함수덮어씌우기

def f():
    print('asdf')
f()
asdf
def f():
    print('guebin hahaha')
f()
guebin hahaha

이런식으로 함수가 이미 정의되어 있더라도, 내가 나중에 덮어씌우면 그 함수의 기능을 다시 정의한다.

(해킹시작)

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)
guebin hahaha
print(a.__str__())
guebin hahaha

- __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)
낼 수 있는 패: ['가위', '바위', '보']
기록: ['바위']

파이썬의 비밀2

- print(a)print(a.__str__())는 같은 문법이다.

- 참고로 a.__str__()str(a)도 같은 문법이다.

a.__str__()
"낼 수 있는 패: ['가위', '바위', '보']\n기록: ['바위']"
str(a)
"낼 수 있는 패: ['가위', '바위', '보']\n기록: ['바위']"

- 지금까지 우리가 썼던 기능들 확인!

(예제1)

a=[1,2,3]
print(a)
[1, 2, 3]
a.__str__()
'[1, 2, 3]'
str(a)
'[1, 2, 3]'

(예제2)

a={1,2,3}
print(a)
{1, 2, 3}
a.__str__()
'{1, 2, 3}'
str(a)
'{1, 2, 3}'

(예제3)

a=np.array(1)
a.shape
()
print(a.shape)
()
a.shape.__str__()
'()'
str(a.shape)
'()'

(예제4)

a=range(10) 
print(a)
range(0, 10)
a.__str__()
'range(0, 10)'
str(a)
'range(0, 10)'

(예제5)

a = np.arange(100).reshape(10,10)
print(a)
[[ 0  1  2  3  4  5  6  7  8  9]
 [10 11 12 13 14 15 16 17 18 19]
 [20 21 22 23 24 25 26 27 28 29]
 [30 31 32 33 34 35 36 37 38 39]
 [40 41 42 43 44 45 46 47 48 49]
 [50 51 52 53 54 55 56 57 58 59]
 [60 61 62 63 64 65 66 67 68 69]
 [70 71 72 73 74 75 76 77 78 79]
 [80 81 82 83 84 85 86 87 88 89]
 [90 91 92 93 94 95 96 97 98 99]]
a.__str__()
'[[ 0  1  2  3  4  5  6  7  8  9]\n [10 11 12 13 14 15 16 17 18 19]\n [20 21 22 23 24 25 26 27 28 29]\n [30 31 32 33 34 35 36 37 38 39]\n [40 41 42 43 44 45 46 47 48 49]\n [50 51 52 53 54 55 56 57 58 59]\n [60 61 62 63 64 65 66 67 68 69]\n [70 71 72 73 74 75 76 77 78 79]\n [80 81 82 83 84 85 86 87 88 89]\n [90 91 92 93 94 95 96 97 98 99]]'
str(a)
'[[ 0  1  2  3  4  5  6  7  8  9]\n [10 11 12 13 14 15 16 17 18 19]\n [20 21 22 23 24 25 26 27 28 29]\n [30 31 32 33 34 35 36 37 38 39]\n [40 41 42 43 44 45 46 47 48 49]\n [50 51 52 53 54 55 56 57 58 59]\n [60 61 62 63 64 65 66 67 68 69]\n [70 71 72 73 74 75 76 77 78 79]\n [80 81 82 83 84 85 86 87 88 89]\n [90 91 92 93 94 95 96 97 98 99]]'

__repr__

- 생각해보니까 print를 해서 우리가 원하는 정보를 확인하는건 아니었음

a=[1,2,3]
a
[1, 2, 3]
print(a) 
[1, 2, 3]

- a + 엔터print(a) + 엔터와 같은효과인가?

(반례)

a=np.array([1,2,3,4]).reshape(2,2)
a
array([[1, 2],
       [3, 4]])
print(a)
[[1 2]
 [3 4]]

- 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
array([1, 2, 3])
print(a)
[1 2 3]
a.__repr__()
'array([1, 2, 3])'
a.__str__()
'[1 2 3]'

파이썬의 비밀3

- 대화형콘솔에서 오브젝트이름+엔터를 쳐서 나오는 출력은 __repr__의 결과와 연관있다.

a = np.array(range(10000)).reshape(100,100)
a
array([[   0,    1,    2, ...,   97,   98,   99],
       [ 100,  101,  102, ...,  197,  198,  199],
       [ 200,  201,  202, ...,  297,  298,  299],
       ...,
       [9700, 9701, 9702, ..., 9797, 9798, 9799],
       [9800, 9801, 9802, ..., 9897, 9898, 9899],
       [9900, 9901, 9902, ..., 9997, 9998, 9999]])
print(a)
[[   0    1    2 ...   97   98   99]
 [ 100  101  102 ...  197  198  199]
 [ 200  201  202 ...  297  298  299]
 ...
 [9700 9701 9702 ... 9797 9798 9799]
 [9800 9801 9802 ... 9897 9898 9899]
 [9900 9901 9902 ... 9997 9998 9999]]
a.__repr__()
'array([[   0,    1,    2, ...,   97,   98,   99],\n       [ 100,  101,  102, ...,  197,  198,  199],\n       [ 200,  201,  202, ...,  297,  298,  299],\n       ...,\n       [9700, 9701, 9702, ..., 9797, 9798, 9799],\n       [9800, 9801, 9802, ..., 9897, 9898, 9899],\n       [9900, 9901, 9902, ..., 9997, 9998, 9999]])'

- 참고로 a.__repr__()repr(a)와 같다.

representation

repr(a)
'array([[   0,    1,    2, ...,   97,   98,   99],\n       [ 100,  101,  102, ...,  197,  198,  199],\n       [ 200,  201,  202, ...,  297,  298,  299],\n       ...,\n       [9700, 9701, 9702, ..., 9797, 9798, 9799],\n       [9800, 9801, 9802, ..., 9897, 9898, 9899],\n       [9900, 9901, 9902, ..., 9997, 9998, 9999]])'

주피터노트북의 비밀 (_repr_html_)

- 요즘에는 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
a b
0 1 2
1 2 3
2 3 4
  • 예쁘게나온다.

- 위의결과는 print(df.__repr__())의 결과와 조금 다르게 나온다?

print(df.__repr__())
   a  b
0  1  2
1  2  3
2  3  4

- 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_()
'<div>\n<style scoped>\n    .dataframe tbody tr th:only-of-type {\n        vertical-align: middle;\n    }\n\n    .dataframe tbody tr th {\n        vertical-align: top;\n    }\n\n    .dataframe thead th {\n        text-align: right;\n    }\n</style>\n<table border="1" class="dataframe">\n  <thead>\n    <tr style="text-align: right;">\n      <th></th>\n      <th>a</th>\n      <th>b</th>\n    </tr>\n  </thead>\n  <tbody>\n    <tr>\n      <th>0</th>\n      <td>1</td>\n      <td>2</td>\n    </tr>\n    <tr>\n      <th>1</th>\n      <td>2</td>\n      <td>3</td>\n    </tr>\n    <tr>\n      <th>2</th>\n      <td>3</td>\n      <td>4</td>\n    </tr>\n  </tbody>\n</table>\n</div>'
  • html코드!
from IPython.core.display import HTML
HTML(df._repr_html_())
a b
0 1 2
1 2 3
2 3 4
set(dir(df)) & {'_repr_html_'}
{'_repr_html_'}

- 물론 df._repr_html_()함수가 내부적으로 있어도 html이 지원되지 않는 환경이라면 print(df.__repr__())이 내부적으로 수행된다.

__repr____str__의 우선적용 순위

(예제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__()
"낼 수 있는 패: ['가위', '바위', '보']\n기록: []"
repr(a)
"낼 수 있는 패: ['가위', '바위', '보']\n기록: []"

- 여기까지는 상식수준의 결과임. 이제 아래를 관찰하라.

print(a) # print(a.__str__())
낼 수 있는 패: ['가위', '바위', '보']
기록: []
a.__str__()
"낼 수 있는 패: ['가위', '바위', '보']\n기록: []"
str(a)
"낼 수 있는 패: ['가위', '바위', '보']\n기록: []"
  • __str__()은 건드린적이 없는데? $\to$ 건드린적은 없는데 기능이 바뀌어있음
a.__str__??
Signature:      a.__str__()
Call signature: a.__str__(*args, **kwargs)
Type:           method-wrapper
String form:    <method-wrapper '__str__' of RPC object at 0x7fe091e34df0>
Docstring:      Return str(self).
a.__repr__??
Signature: a.__repr__()
Docstring: Return repr(self).
Source:   
    def __repr__(self):
        return "낼 수 있는 패: {}\n기록: {}".format(self.candidate,self.actions)
File:      /tmp/ipykernel_577026/1255092133.py
Type:      method

(예제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
<__main__.RPC at 0x7fe091deaeb0>
a.__str__()
"낼 수 있는 패: ['가위', '바위', '보']\n기록: []"
a.__repr__()
'<__main__.RPC object at 0x7fe091deaeb0>'
a.__str__??
Signature: a.__str__()
Docstring: Return str(self).
Source:   
    def __str__(self):
        return "낼 수 있는 패: {}\n기록: {}".format(self.candidate,self.actions)
File:      /tmp/ipykernel_577026/2068707480.py
Type:      method
a.__repr__??
Signature:      a.__repr__()
Call signature: a.__repr__(*args, **kwargs)
Type:           method-wrapper
String form:    <method-wrapper '__repr__' of RPC object at 0x7fe091deaeb0>
Docstring:      Return repr(self).

(예제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
guebin hahaha
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)
'<__main__.RPC object at 0x7fe091dea4f0>'
repr(a)
'<__main__.RPC object at 0x7fe091dea4f0>'
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
학번: 202150754
낼 수 있는 패: ['가위', '바위', '보']
기록: ['바위', '보', '바위', '바위', '보']

강의영상

- (1/4) __add__ (1)

- (2/4) __add__ (2)

- (3/4) __add__ (3)

- (4/4) __mul__

imports

import numpy as np

클래스공부 5단계

- 지난시간까지 배운것: RPC자료형에 한정해서 print()등의 기능을 조작할 수 있었다. (재정의 할 수 있었다)

- 이번시간에 배울것: 특정자료형에 한정하여 print 이외의 파이썬 내부기능을 조작하여 보자. (재정의하여 보자)

motive

- 아래의 연산구조를 관찰하자.

a=1 
b=2 
a+b 
3
  • a라는 인스턴스와 b라는 인스턴스를 +라는 기호가 연결하고 있다.

- 이번에는 아래의 연산구조를 관찰하자.

a=[1,2]
b=[3,4]
a+b
[1, 2, 3, 4]
  • a라는 인스턴스와 b라는 인스턴스를 +라는 기호가 연결하고 있다.

- 동작이 다른 이유?

  • 클래스를 배우기 이전: int자료형의 +는 "정수의 덧셈"을 의미하고 list자료형의 +는 "자료의 추가"를 의미한다.
  • 클래스를 배운 이후: 아마 클래스는 +라는 연산을 정의하는 숨겨진 메소드가 있을것이다. (print가 그랬듯이) 그리고 int클래스에서는 그 메소드를 "정수의 덧셈"이 되도록 정의하였고 list클래스에서는 그 메소드를 "자료의 추가"를 의미하도록 정의하였다.

- 아래의 결과를 관찰

a=1
b=2
a.__add__(b)
3
b.__add__(a)
3
a=[1,2]
b=[3,4]
a.__add__(b)
[1, 2, 3, 4]
b.__add__(a)
[3, 4, 1, 2]

- a+b는 사실 내부적으로 a.__add__(b)의 축약구문이다. 따라서 만약 a.__add__(b)의 기능을 바꾸면 (재정의하면) a+b의 기능도 바뀔 것이다.

__add__

- 학생예제

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()
입학을 축하합니다. 당신의 나이는 20.0이고 현재 학기는 0학기 입니다.
iu
나이: 20.0
학기: 0
iu + 1 ## 1학년 1학기 등록
iu
나이: 20.5
학기: 1
iu + 0 ## 휴학함
iu 
나이: 21.0
학기: 1

- 연산을 연속으로 하고 싶다.

iu + 1 + 0 + 0 + 0 + 0
---------------------------------------------------------------------------
TypeError                                 Traceback (most recent call last)
/tmp/ipykernel_577026/2201481932.py in <module>
----> 1 iu + 1 + 0 + 0 + 0 + 0

TypeError: unsupported operand type(s) for +: 'NoneType' and 'int'

- 에러의 이유?

(되는코드)

(1+1)+1 # 1+1+1은 이렇게 볼 수 있다
3
_a = (1+1) 
type(_a)
int
_a+1 # 이 연산은 int인스턴스 + int인스턴스 
3

(안되는코드)

iu+1+1
---------------------------------------------------------------------------
TypeError                                 Traceback (most recent call last)
/tmp/ipykernel_577026/1714451165.py in <module>
----> 1 iu+1+1

TypeError: unsupported operand type(s) for +: 'NoneType' and 'int'
_a=iu+1 
type(_a)
NoneType
_a+1 
---------------------------------------------------------------------------
TypeError                                 Traceback (most recent call last)
/tmp/ipykernel_577026/1102778112.py in <module>
----> 1 _a+1

TypeError: unsupported operand type(s) for +: 'NoneType' and 'int'

- 에러를 해결하는 방법: 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()
입학을 축하합니다. 당신의 나이는 20.0이고 현재 학기는 0학기 입니다.
iu+1 # __add__의 return에 Student클래스의 인스턴스가 리턴되면서 자동으로 _repr_html_() 실행
나이: 20.5
학기: 1
iu+1 +0+0+0+0
나이: 23.0
학기: 2

__mul__

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
낼 수 있는 패: ['가위', '바위', '보']
액션: ['보']
승패: [1]
b
낼 수 있는 패: ['가위', '바위', '보']
액션: ['바위']
승패: [-1]
for i in range(5):
    a*b
a
낼 수 있는 패: ['가위', '바위', '보']
액션: ['보', '가위', '보', '바위', '가위', '가위']
승패: [1, 0, 1, 0, 1, 1]
b
낼 수 있는 패: ['가위', '바위', '보']
액션: ['바위', '가위', '바위', '바위', '보', '보']
승패: [-1, 0, -1, 0, -1, -1]
for i in range(50000):
    a*b
sum(a.results)/len(a.results)
-0.002759668839739231
sum(b.results)/len(a.results)
0.002759668839739231

숙제

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
낼 수 있는 패: ['가위', '보']
액션: ['가위', '가위', '보', '보', '보', '가위', '가위', '보', '가위', '가위', '보', '보', '가위', '보', '보', '가위', '보', '가위', '가위', '보', '가위', '가위', '가위', '가위', '가위', '보', '가위', '보', '가위', '보', '가위', '보', '가위', '가위', '가위', '가위', '가위', '가위', '가위', '가위', '보', '가위', '보', '가위', '가위', '보', '보', '보', '보', '가위', '가위', '가위', '가위', '보', '가위', '보', '보', '가위', '보', '가위', '가위', '가위', '가위', '가위', '보', '가위', '가위', '보', '보', '보', '보', '가위', '보', '보', '가위', '가위', '가위', '가위', '가위', '가위', '가위', '가위', '보', '가위', '보', '보', '가위', '보', '가위', '보', '가위', '보', '보', '보', '보', '가위', '보', '가위', '가위', '가위']
승패: [0, 0, 1, -1, 1, 0, -1, -1, 0, 0, -1, 1, 0, -1, -1, -1, 1, -1, 0, 1, -1, 0, 0, -1, 0, 1, -1, 1, -1, -1, 0, -1, 0, -1, 0, -1, -1, 0, -1, 0, 1, -1, 1, 0, -1, -1, -1, 1, 1, 0, -1, 0, -1, 1, -1, 1, -1, -1, 1, -1, 0, -1, 0, -1, 1, -1, 0, 1, 1, 1, -1, 0, -1, -1, 0, -1, 0, 0, -1, -1, 0, 0, 1, 0, 1, 1, -1, 1, -1, -1, 0, -1, -1, -1, -1, 0, -1, -1, 0, -1]
b
낼 수 있는 패: ['가위', '바위']
액션: ['가위', '가위', '바위', '가위', '바위', '가위', '바위', '가위', '가위', '가위', '가위', '바위', '가위', '가위', '가위', '바위', '바위', '바위', '가위', '바위', '바위', '가위', '가위', '바위', '가위', '바위', '바위', '바위', '바위', '가위', '가위', '가위', '가위', '바위', '가위', '바위', '바위', '가위', '바위', '가위', '바위', '바위', '바위', '가위', '바위', '가위', '가위', '바위', '바위', '가위', '바위', '가위', '바위', '바위', '바위', '바위', '가위', '바위', '바위', '바위', '가위', '바위', '가위', '바위', '바위', '바위', '가위', '바위', '바위', '바위', '가위', '가위', '가위', '가위', '가위', '바위', '가위', '가위', '바위', '바위', '가위', '가위', '바위', '가위', '바위', '바위', '바위', '바위', '바위', '가위', '가위', '가위', '가위', '가위', '가위', '가위', '가위', '바위', '가위', '바위']
승패: [0, 0, -1, 1, -1, 0, 1, 1, 0, 0, 1, -1, 0, 1, 1, 1, -1, 1, 0, -1, 1, 0, 0, 1, 0, -1, 1, -1, 1, 1, 0, 1, 0, 1, 0, 1, 1, 0, 1, 0, -1, 1, -1, 0, 1, 1, 1, -1, -1, 0, 1, 0, 1, -1, 1, -1, 1, 1, -1, 1, 0, 1, 0, 1, -1, 1, 0, -1, -1, -1, 1, 0, 1, 1, 0, 1, 0, 0, 1, 1, 0, 0, -1, 0, -1, -1, 1, -1, 1, 1, 0, 1, 1, 1, 1, 0, 1, 1, 0, 1]
sum(a.results)
-25
sum(b.results)
25

b가 더 많이 이김