강의영상

- (1/4) __add__ (1)

- (2/4) __add__ (2)

- (3/4) __add__ (3)

- (4/4) __mul__

imports

import numpy as np

클래스공부 5단계

- 지난시간까지 배운것: RPS자료형에 한정해서 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)
Input In [109], in <cell line: 1>()
----> 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)
Input In [113], in <cell line: 1>()
----> 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)
Input In [115], in <cell line: 1>()
----> 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 RPS: 
    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=RPS()
b=RPS()
a
낼 수 있는 패: ['가위', '바위', '보']
액션: []
승패: []
b
낼 수 있는 패: ['가위', '바위', '보']
액션: []
승패: []
a*b
a
낼 수 있는 패: ['가위', '바위', '보']
액션: ['보']
승패: [-1]
b
낼 수 있는 패: ['가위', '바위', '보']
액션: ['가위']
승패: [1]
for i in range(5):
    a*b
a
낼 수 있는 패: ['가위', '바위', '보']
액션: ['보', '가위', '바위', '바위', '가위', '보']
승패: [-1, -1, 0, 1, -1, 1]
b
낼 수 있는 패: ['가위', '바위', '보']
액션: ['가위', '바위', '바위', '가위', '바위', '바위']
승패: [1, 1, 0, -1, 1, -1]
for i in range(50000):
    a*b
sum(a.results)/len(a.results)
-0.005739311282646082
sum(b.results)/len(a.results)
0.005739311282646082

숙제

RPS클래스에서 player a와 player b를 만들어라. Player a는 ['가위','보'] 중에 하나를 낼 수 있다. 그리고 Player b는 ['가위','바위'] 중에 하나를 낼 수 있다. 두 player는 가지고 있는 패를 (같은확률로) 랜덤으로 낸다. (즉 player a가 가위만 내거나 보만 내는 경우는 없다.)

(1) 누가 더 유리한가? 이유를 스스로 생각해보라. (이유를 정리하여 숙제로 제출할 필요 없음)

(2) 50000번의 시뮬레이션을 해보고 결과를 분석해보라.