일 | 월 | 화 | 수 | 목 | 금 | 토 |
---|---|---|---|---|---|---|
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 |
- 파이썬 강좌
- 스칼라 동시성
- Hyperledger fabric gossip protocol
- Golang
- 주키퍼
- hyperledger fabric
- 블록체인
- 스칼라
- 파이썬 머신러닝
- Akka
- play 강좌
- 플레이프레임워크
- 파이썬 데이터분석
- akka 강좌
- 하이퍼레저 패브릭
- Actor
- 파이썬 동시성
- 엔터프라이즈 블록체인
- play2 강좌
- 스칼라 강좌
- 그라파나
- 안드로이드 웹뷰
- 하이브리드앱
- CORDA
- Adapter 패턴
- Play2 로 웹 개발
- 스위프트
- 이더리움
- 파이썬
- Play2
- Today
- Total
HAMA 블로그
파이썬의 함정 - 1 (클래스변수 와 객체 변수) 본문
파이썬의 함정 - 1 (부제: 나의 삽질기)
클래스 변수와 객체 변수에 대한 함정
자바나 C++ 베이스에서 파이썬으로 옮겨 왔을때 가장 실수하기 쉬운 부분에 대해서 살펴보겠습니다.
먼저 아래 코드를 보시죠.
class Test :
num = 0
def show(self):
print 'num :' + str(num)
t = Test()
t.show()
어떻게 될까요?
에러입니다.
어디서?
print 'num :' + str(num)
네 여기에서 num 를 못찾아서 에러가 납니다. 클래스 변수 num 을 찾지 못하네요.
이걸 해결하려면
print 'num :' + str(self.num)
이렇게 self 를 붙여 주어야 하는데요.
self 는 JAVA나 C++에서 this 와 같으며 현재 객체를 말합니다.
print 'num :' + str(Test.num)
이렇게 클래스 변수로 나타낼 수도 있습니다.
그럼 아래는 어떻게 될까요?
class Test :
num = 0
def show(self):
print 'num :' + str(self.num)
t = Test()
Test.num = 10
t.show()
Test.num = 10 이렇게 클래스 변수에 직접 접근하여 10을 대입해 주었습니다. 클래스 공통 10 인거죠
self.num 은 객체 스스로라고 했으니깐 0 일꺼 같지만
결과는 "num :10" 이렇게 됩니다. 아직 클래스변수와 객체변수로 나누어지지 않았네요.
그럼 아래는 어떻게 될까요?
class Test :
num = 100
def __init__(self):
self.num = 0
def show(self):
print 'num :' + str(self.num)
t = Test()
Test.num = 10
t.show()
__init__ 라는 생성자를 이용하여 self.num 을 초기화 하였습니다. 이때 결과는 "num :0" 이렇게 됩니다.
즉 __init__ 생성자에서 초기화를 해 줘서 객체별 변수로 바뀝니다.
그럼 아래는 어떨까요?
class Test:
num = 100
def show(self):
print 'num :' + str(self.num)
def inc(self):
self.num = self.num + 1
t1 = Test()
t2 = Test()
t1.inc()
t2.inc()
t1.show()
t2.show()
객체를 2개 만들었습니다. 그리고 각각 inc() 함수를 호출하여 1씩 증가 시켰는데요.
show() 호출하면 둘다 101 을 나타냅니다.
__init__ 생성자에서 초기화 하지도 않았는데 self.num 에 값을 대입하면서 객체변수가 된거 같습니다.
(기존의 클래스 변수의 값을 가지고 객체변수로 초기화)
마지막으로 하나 보시죠. 이게 히트입니다. (동시에 아주 큰 실수입니다. 본문 끝까지 읽으셔야해요. 제발~)
class Test:
num = 0
ar = []
def show(self):
print 'num :' + str(self.num)
print 'list :' + str(self.ar)
def inc(self, n):
self.num = self.num + n
def inc_list(self, n):
self.ar.append(n)
t1 = Test()
t2 = Test()
t1.inc(2)
t2.inc(3)
t1.inc_list(1)
t2.inc_list(5)
t1.show()
t2.show()
이거 show() 하면 t1 객체의 num 는 2 , t2 객체의 num 는 3이 됩니다. 즉 값이 객체별로 분리되지만
리스트의 경우는 분리되지 않네요..
num :2
list :[1, 5]
num :3
list :[1, 5]
자바와 C++ 에서는 상상도 못할..
class Test:
num = 0
ar = []
def __init__(self):
self.ar = []
def show(self):
print 'num :' + str(self.num)
print 'list :' + str(self.ar)
def inc(self):
self.num = self.num + 1
def inc_list(self, n):
self.ar.append(n)
t1 = Test()
t2 = Test()
t1.inc()
t2.inc()
t1.inc_list(1)
t2.inc_list(5)
t1.show()
t2.show()
해보니 잘 됩니다.
__init__ 에서 초기화 해주어야 객체 별 변수가 된다면 모두 그게 적용되야 하는데
파이썬에서는 일반타입하고 배열타입하고 다르게 작동하는거 같습니다.
즉 일반타입은 자동으로 __init__ 초기화가 이루어지는 모냥.. ?? 땡~~
여기까지 저의 착각이었습니다. 아래 읽으세요.
앗 큰 실수!!
위에 작동방식이 너무 상식밖이라.. 파이썬 창조자 머리에 꽃 맞은것도 아니고
굳이 저렇게 헤깔리게 할까 싶어서 다시 살펴본결과
def inc(self):
self.num = self.num + 1
def inc_list(self, n):
self.ar.append(n)
이거 두개 사이에 차이가 있음을 발견
위에 num 은 초기화 해주고 있으나, ar 는 초기화가 아니네요.
self.ar.append(1) 이것을 sefl.num = 1 같은 초기화라고 착각한것 입니다.
self.ar = [] 이렇게 초기화를 해줘야 했었는데 말이죠.
즉 __init__ 뿐 아니라 다른 어느 곳 에서 든지 ar 도 초기화를 한다면 동일하게 객체변수화 합니다.
예를들어 아래 처럼 아무데서나 초기화 해주면 됩니다.
def listInit(self):
self.ar = []
아래처럼 외부에서 초기화 해줘도 되구요.
t1.ar = []
t2.ar = []
결론
애초에 공유목적 ( 즉 정적변수 ) 로 작동할때만 class 아래에 클래스 변수로 놓고 , 객체별로 사용될 변수들은 클래스 변수 자리가 아니라 __init__ 에 넣는게 파이썬 스타일/이디엄 인듯. 이렇게 되면 저런 실수는 하지 않게 되겠지요. 그래도 실수를 통해 하나 진득히 배우게 된거 같습니다. ^^
'Python' 카테고리의 다른 글
파이썬으로 클라우드 (병렬,분산) 하고 싶어요 (0) | 2016.09.14 |
---|---|
파이썬의 함정 - 2 (@classmethod 와 @staticmethod 의 차이) (1) | 2016.09.13 |
파이썬 최고 성능을 위한 프로젝트 (0) | 2016.08.31 |
Python, Numpy, SciPy, Matplotlib 설치 (0) | 2016.02.05 |
Scala 과 Python 를 동시에 배워보자. (0) | 2015.05.04 |