파이썬 (0525) 13주차

클래스
Author

김보람

Published

May 25, 2022

import numpy as np

클래스 공부 5단계

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

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

motive

a=1
b=2
type(a)
int
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__
<method-wrapper '__add__' of int object at 0x70c560>
dir(a)
['__abs__',
 '__add__',
 '__and__',
 '__bool__',
 '__ceil__',
 '__class__',
 '__delattr__',
 '__dir__',
 '__divmod__',
 '__doc__',
 '__eq__',
 '__float__',
 '__floor__',
 '__floordiv__',
 '__format__',
 '__ge__',
 '__getattribute__',
 '__getnewargs__',
 '__gt__',
 '__hash__',
 '__index__',
 '__init__',
 '__init_subclass__',
 '__int__',
 '__invert__',
 '__le__',
 '__lshift__',
 '__lt__',
 '__mod__',
 '__mul__',
 '__ne__',
 '__neg__',
 '__new__',
 '__or__',
 '__pos__',
 '__pow__',
 '__radd__',
 '__rand__',
 '__rdivmod__',
 '__reduce__',
 '__reduce_ex__',
 '__repr__',
 '__rfloordiv__',
 '__rlshift__',
 '__rmod__',
 '__rmul__',
 '__ror__',
 '__round__',
 '__rpow__',
 '__rrshift__',
 '__rshift__',
 '__rsub__',
 '__rtruediv__',
 '__rxor__',
 '__setattr__',
 '__sizeof__',
 '__str__',
 '__sub__',
 '__subclasshook__',
 '__truediv__',
 '__trunc__',
 '__xor__',
 'bit_length',
 'conjugate',
 'denominator',
 'from_bytes',
 'imag',
 'numerator',
 'real',
 'to_bytes']
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=1):
        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이고 현재 1학기 입니다.
iu.semester
1
iu  # 클래스가 저장되어있는 주소를 _repr_html_ 통해서 바꿔즘
나이: 20.0
학기: 1
iu + 1 #1학년 2학기 등록
iu
나이: 20.5
학기: 2
iu + 0 # 휴학
iu
나이: 21.0
학기: 2

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

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

- 에러의 이유?

1+1+1 #이거는 되는데?
3
(1+1)+1
3
_a = (1+1)
type(_a)
int
_a + 1    # 이 연산은 int인스턴스 + int인스턴스 
3

(안되는거)

iu+1+1
TypeError: unsupported operand type(s) for +: 'NoneType' and 'int'
_a=iu+1
type(_a)
NoneType
_a+1
TypeError: unsupported operand type(s) for +: 'NoneType' and 'int'

- 에러를 해결하는 방법: iu+1의 결과로 Student 클래스의 인스턴스가 리턴되면 된다.

class Student:
    def __init__(self, age=20.0, semester=1):
        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이고 현재 1학기 입니다.
iu + 1   # __add__의 return에 Student클래스의 인스턴스가 리턴되면서 자동으로 __repr_html_()실행
나이: 23.0
학기: 4
iu + 1 + 0 + 0 + 0 
나이: 25.0
학기: 5

__mul__

a=1
b=1
a*b
1
a.__mul__
<method-wrapper '__mul__' of int object at 0x70c560>
class RPS: 
    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=RPS()
b=RPS()
a
낼 수 있는 패: ['가위', '바위', '보']
기록: []
b
낼 수 있는 패: ['가위', '바위', '보']
기록: []
  • a*b 해서 승패를 확인하기 위한 클래스를 만들자
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
낼 수 있는 패: ['가위', '바위', '보']
액션: []
승패: []
for i in range(5):
    a*b
a
낼 수 있는 패: ['가위', '바위', '보']
액션: ['보', '바위', '가위', '가위', '바위']
승패: [1, 0, -1, 0, 1]

b
낼 수 있는 패: ['가위', '바위', '보']
액션: ['바위', '바위', '바위', '가위', '가위']
승패: [-1, 0, 1, 0, -1]

숙제

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

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

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

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)
    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(['가위','바위'])
for i in range(50000):
    a*b
sum(a.results)
-12358
sum(b.results)
12358