(study) 파이썬 입문 (12주차) 5월9일
클래스 공부 3단계
-
(1/6) 오브젝트의 개념
-
(2/6) 예제1
-
(3/6) 예제2,3,4
-
(4/6) 예제5
-
(5/6) 예제 6,7
-
(6/6) 숙제설명
-
이 단계에서는 클래스오브젝트에 소속된 변수와 인스턴스오브젝트에 소속된 변수를 설명한다.
-
파이썬은 모든 것이 오브젝트로 이루어져 있다. <- 우선은 그냥 명언처럼 외우세요
-
오브젝트는 메모리주소에 저장되는 모든것을 의미한다.
a=1
id(a) # 메모리주소를 보는 명령어
a='asdf'
id(a)
a=[1,2,3]
id(a)
-
클래스와 인스턴스도 오브젝트다
class A:
x=0
def f(self):
print(self.x)
id(A)
- A는 오브젝트
a=A()
id(a)
- a는 오브젝트
b=A()
id(b)
- b는 오브젝트
-
앞으로는 A를 클래스오브젝트, a,b를 인스턴스오브젝트라고 부르자.
-
시점0
class A:
x=0
y=0
def f(self):
self.x = self.x + 1
A.y = A.y + 1
print("현재 인스턴스에서 f가 {}번 실행".format(self.x))
print("A클래스에서 만들어진 모든 인스턴스들에서 f가 총 {}번 실행".format(self.y))
A.x, A.y
-
시점1
a = A()
b = A()
[A.x,A.y], [a.x,a.y], [b.x,b.y]
-
시점2
a.f()
[A.x,A.y], [a.x,a.y], [b.x,b.y]
-
시점3
b.f()
[A.x,A.y], [a.x,a.y], [b.x,b.y]
-
시점4
b.f()
[A.x,A.y], [a.x,a.y], [b.x,b.y]
-
시점5
a.f()
[A.x,A.y], [a.x,a.y], [b.x,b.y]
-
시점6
c=A()
[A.x,A.y], [a.x,a.y], [b.x,b.y], [c.x,c.y]
-
시점7
c.f()
[A.x,A.y], [a.x,a.y], [b.x,b.y], [c.x,c.y]
-
신기한점: 각 인스턴스에서 인스턴스이름.f()
를 실행한 횟수를 서로 공유하는 듯 하다. (A의 관리하는 것처럼 느껴진다)
-
x와 y는 약간 느낌이 다르다. x는 지점소속, y는 본사소속의 느낌?
이 예제에서 x는 인스턴스오브젝트에 소속된 변수, y는 클래스오브젝트에 소속된 변수처럼 느껴짐
(약속) 앞으로 인스턴스오브젝트에 소속된 변수를 인스턴스변수라고 하고, 클래스오브젝트에 소속된 변수를 클래스변수라고 하자.
-
인스턴스변수와 클래스변수를 구분하는 방법? 인스턴스이름.__dict__
를 쓰면 인스턴스변수만 출력된다
- 따라서 a.+ tab을 눌러서 나오는 변수중
a.__dict__
에 출력되지 않으면 클래스변수이다.
a.__dict__
b.__dict__
c.__dict__
c는 한 번 시행해서
-
이 예제에서 아래는 모두 클래스변수이다.
a.y, b.y, c.y
-
시점0
class A:
x=0
y=0
def f(self):
self.x = self.x + 1
A.y = A.y + 1
print("현재 인스턴스에서 f가 {}번 실행".format(self.x))
print("A클래스에서 만들어진 모든 인스턴스들에서 f가 총 {}번 실행".format(self.y))
a=A()
[A.x,A.y], [a.x,a.y]
-
시점1
a.f()
a.f()
a.f()
[A.x,A.y], [a.x,a.y]
-
시점2
a.x = 0 # f의 실행기록을 초기화하고 싶다
a.f()
[A.x,A.y], [a.x,a.y]
-
시점0
class A:
x=0
y=0
def f(self):
self.x = self.x + 1
A.y = A.y + 1
print("현재 인스턴스에서 f가 {}번 실행".format(self.x))
print("A클래스에서 만들어진 모든 인스턴스들에서 f가 총 {}번 실행".format(self.y))
a=A()
b=A()
[A.x,A.y], [a.x,a.y], [b.x,b.y]
-
시점1
a.f()
[A.x,A.y], [a.x,a.y], [b.x,b.y]
-
시점2
A.y = 100
[A.x,A.y], [a.x,a.y], [b.x,b.y]
a.f()
[A.x,A.y], [a.x,a.y], [b.x,b.y]
-
시점0
class A:
x=0
y=0
def f(self):
self.x = self.x + 1
A.y = A.y + 1
print("현재 인스턴스에서 f가 {}번 실행".format(self.x))
print("A클래스에서 만들어진 모든 인스턴스들에서 f가 총 {}번 실행".format(self.y))
a=A()
[A.x, A.y], [a.x,a.y]
-
시점1
a.f()
[A.x, A.y], [a.x,a.y]
-
시점2
A.x = 100 # 이렇게 되면 앞으로 만들어진 인스턴스는 기본적으로 현재 인스턴스에서 100번 f를 실행하였다는 정보를 가지고 태어나게 된다.
[A.x, A.y], [a.x,a.y]
-
시점3
b=A()
[A.x, A.y], [a.x,a.y], [b.x,b.y]
-
시점4
b.f()
-
시점5
a.f()
a.f()
b.f()
-
시점0
class B:
x=10 # 초기자본금
y=0
def f(self): # f()를 실행할때마다 돈을 쓴다.
self.x = self.x - 1
B.y = B.y + 1
print("현재 인스턴스에서 {}원 잔액남음".format(self.x))
print("A클래스에서 만들어진 모든 인스턴스들에서 총 {}원 사용".format(self.y))
a=B()
b=B()
[B.x,B.y], [a.x,a.y], [b.x,b.y]
-
시점1
a.f() # 돈을쓰는것
a.f()
b.f()
-
시점2
[B.x,B.y], [a.x,a.y], [b.x,b.y]
B.x = 999
[B.x,B.y], [a.x,a.y], [b.x,b.y]
-
시점3
c = B()
c.f()
-
시점4
a.f()
b.f()
c.f()
c.f()
c.f()
-
시점0
class A:
x=0
y=0
def f(self):
self.x = self.x + 1
A.y = A.y + 1
print("현재 인스턴스에서 f가 {}번 실행".format(self.x))
print("A클래스에서 만들어진 모든 인스턴스들에서 f가 총 {}번 실행".format(self.y))
a=A()
b=A()
[A.x, A.y], [a.x,a.y], [b.x,b.y]
-
시점1
a.f()
b.f()
a.f()
[A.x, A.y], [a.x,a.y], [b.x,b.y]
-
시점2
a.y ## 인스턴스a에 소속되어있지만 클래스변수
a.y = 999 ## 내가 하드코딩으로 a.y에 999를 입력 -> 이것이 A.y나 b.y에도 반영될까? (X)
[A.x, A.y], [a.x,a.y], [b.x,b.y]
-
시점3
b.f()
a.f()
b.f()
b.f()
a.f()
-
요약
- 인스턴스에서 클래스변수의 값을 변경하면? -> 클래스변수의 값이 변경되는 것이 아니라 인스턴스변수가 새롭게 만들어져서 할당된다.
- 이 예제에서 a.y는 이제 클래스변수에서 인스턴스변수로 재탄생되었다. 즉 999 오브젝트가 새롭게 만들어져서 a.y라는 이름을 얻은것임.
- 기존의 A.y나 b.y에는 아무런 변화가 없다.
a.y = 999 은 새로운 인스턴변수 y를 할당하는 역할을 한다. 클래스변수의 값을 변경하는 것이 아니다. (왜냐하면 애초에 a.y는 없는 값이었고, A.y를 빌리고 있었던 것임)
a.__dict__
b는 y의 값을 빌려 출력하고 있던 것이었다!
b.__dict__
-
시점0
class A:
x=0
y=0
def f(self):
self.x = self.x + 1
A.y = A.y + 1
print("현재 인스턴스에서 f가 {}번 실행 (인스턴스레벨)".format(self.x))
print("A클래스에서 만들어진 모든 인스턴스들에서 f가 총 {}번 실행 (클래스레벨)".format(A.y))
print("A클래스에서 만들어진 모든 인스턴스들에서 f가 총 {}번 실행 (인스턴스레벨)".format(self.y))
a=A()
a.f()
b=A()
b.f()
-
시점1
a.y = 999
a.f()
a.f()
a.f()
-
의문: 아래의 코드에서 x는 클래변수라고 봐야할까? 인스턴스 변수라고 봐야할까? --> 클래스변수
class SoWhaTV:
x=0 ### 이 시점에서 x는 클래스변수인가? 아니면 인스턴변수인가?
def f(self):
print(self.x)
-
시점0
class A:
x=0
y=0
def f(self):
self.x = self.x + 1
A.y = A.y + 1
print("현재 인스턴스에서 f가 {}번 실행 (인스턴스레벨)".format(self.x))
print("A클래스에서 만들어진 모든 인스턴스들에서 f가 총 {}번 실행 (클래스레벨)".format(A.y))
#print("A클래스에서 만들어진 모든 인스턴스들에서 f가 총 {}번 실행 (인스턴스레벨)".format(self.y))
a=A()
b=A()
a.x,a.y,b.x,b.y
a.__dict__, b.__dict__
- 지금 시점에서 a.x, a.y, b.x, b.y 는 모두 클래스변수임
-
시점1
a.f()
a.__dict__, b.__dict__
- 이 순간 a.x가 클래스변수에서 인스턴스변수로 변경되었다. 왜? f가 실행되면서 self.x = self.x + 1 이 실행되었으므로!
-
시점2
b.f()
a.__dict__, b.__dict__
-
아래처럼 코드를 바꾸면 어떻게 되는가?
class A:
def __init__(self):
self.x=0 # 인스턴스변수로 나중에 쓸꺼니까 명시함
A.y=0 # 클래스변수로 나중에 쓸꺼니까 명시함
def f(self):
self.x = self.x + 1
A.y = A.y + 1
print("현재 인스턴스에서 f가 {}번 실행 (인스턴스레벨)".format(self.x))
print("A클래스에서 만들어진 모든 인스턴스들에서 f가 총 {}번 실행 (클래스레벨)".format(A.y))
#print("A클래스에서 만들어진 모든 인스턴스들에서 f가 총 {}번 실행 (인스턴스레벨)".format(self.y))
-
사용해보자.
a=A()
b=A()
a.f()
b.f()
b.f()
b.f()
a.f()
-
잘 되는것 같다?
-
조금만 생각해보면 엉터리라는 것을 알 수 있다. 아래를 관찰해보자.
c=A() # 이 시점에서 __init__()이 실행된다!
a.f()
- 클래스레벨의 변수가 왜 초기화가 되었지?
-
오류의 이유? c=A()가 실행되는 시점에 __init__()
이 실행되면서 A.y=0
이 실행된다. 따라서 강제초기화가 진행되었음
아래의 조건에 맞는 클래스를 생성하라.
(1) ['가위','바위']
와 같은 리스트를 입력으로 받아 인스턴스를 생성한다.
(2) 위의 리스트에서 하나의 값을 뽑는 메소드 f
를 가지고 있다.
(3) f의 실행횟수를 기록하는 기능을 가진다. (각 인스턴스에서 실행한 횟수, 클래스에서 생성된 모든 인스턴스에서 실행한 횟수 모두 기록)
사용예시
a = Klass(['가위','바위'])
a.f() # 가위가 1/2 바위가 1/2의 확률로 출력
b = Klass(['가위','바위','보'])
b.f() # 가위, 바위, 보가 1/3의 확률로 출력
import numpy as np
class Klass:
def __init__(self,x):
self.x = x
Klass.y = 0
def f(self):
Klass.y = Klass.y + 1
print(np.random.choice(self.x))
print(Klass.y)
a = Klass(['가위','바위'])
b = Klass(['가위','바위','보'])
a.f() # 가위가 1/2 바위가 1/2의 확률로 출력
b.f() # 가위, 바위, 보가 1/3의 확률로 출력
a.f() # 가위가 1/2 바위가 1/2의 확률로 출력
b.f() # 가위, 바위, 보가 1/3의 확률로 출력