#!/usr/bin/env python # coding: utf-8 # *** # *** # # 19. 클래스와 객체 # *** # *** # *** # ## 1 파이썬 클래스와 이름 공간 # *** # - 파이썬 클래스(Class)는 새로운 이름 공간을 지원하는 또 다른 단위 # - 객체 지향 프로그래밍에서 클래스(Class)의 역할 # - 사용자 정의 타입(Type) 생성 # - 클래스 정의 구문 # > class 클래스 이름: #헤더(Header)
# >         pass #몸체(Body) # - 인스턴스: 클래스로 부터 만들어낸 객체 # - 모듈 vs. 클래스 vs. 인스턴스 # - 모듈: 파일 단위로 이름 공간을 구성 # - 클래스: 클래스 영역 내에 이름 공간을 구성 # - 인스턴스: 인스턴스 영역 내에 이름 공간을 구성 # In[1]: class S1: a = 1 print(S1.a) print() S1.b = 2 # 클래스 이름 공간에 새로운 이름의 생성 print(S1.b) print() print(dir(S1)) # S1에 포함된 이름들을 리스트로 반환 del S1.b # 이름 공간 S1에서 b삭제 print(dir(S1)) # - 파이썬에서는 동적으로 인스턴스 외부에서 인스턴스 멤버를 추가할 수 있음 # - 클래스와 독립적으로 각 인스턴스를 하나의 이름 공간으로 취급함 # In[2]: x = S1() # x는 S1의 클래스 인스턴스 print(x.a) x.a = 10 # 클래스 인스턴스 x의 이름 공간에 이름 생성 print(x.a) print(S1.a) # 클래스 이름 공간과 클래스 인스턴스의 이름공간은 다르다 # In[3]: y = S1() # S1 클래스의 또 다른 인스턴스 생성 y.a = 300 # 클래스 인스턴스 y의 이름 공간에 이름 생성 print(y.a) print(x.a) # x 인스턴스 공간의 이름 a 확인 print(S1.a) # 클래스 이름 공간의 a 확인 # In[4]: class Simple: pass s1 = Simple() s2 = Simple() # In[5]: s1.stack = [] # 동적으로 클래스 인스턴스 이름 공간 안에 새로운 변수(이름) stack 생성 s1.stack.append(1) # 값 추가 s1.stack.append(2) s1.stack.append(3) print(s1.stack) print(s1.stack.pop()) print(s1.stack.pop()) print() print(s1.stack) # 최종 s1.stack값 print(s2.stack) # s2에는 stack을 정의한 적이 없다. #기존에러: #AttributeError: Simple instance has no attribute 'stack' # ![inheritance](../images/instance.png) # In[6]: del s1.stack # s1에서 stack삭제 # *** # ## 2 메쏘드의 정의와 호출 # *** # ### 2-1 멤버 메쏘드의 정의와 호출 # - 클래스 내부에 객체 멤버 메소드 선언 - def 키워드 사용 # - 일반 함수와 다른 점은 첫번째 인수로 self 사용 (self라는 이름은 관례적) # - self: 인스턴스 객체 자신의 레퍼런스를 지니고 있음 # - 각 인스턴스들은 self를 이용하여 자신의 이름 공간에 접근 # In[16]: class MyClass: def set(self, v): self.value = v def get(self): return self.value # - 인스턴스 객체를 통하여 멤버 메소드를 호출할 때 self 인자는 없다고 생각 # In[17]: c = MyClass() # 인스턴스 생성 c.set('egg') # 메소드 set 호출 print(c.get()) # 메소드 get 호출 print(c.value) # 인스턴스 변수에 직접 접근 # - 위 코드는 실제로 아래 코드와 동일함 # In[18]: c = MyClass() # 인스턴스 생성 MyClass.set(c, 'egg') print(MyClass.get(c)) print(c.value) # - 멤버 메소드 호출 종류 # - Unbound method call: 클래스 객체를 이용한 메소드 호출 # - 예: MyClass.set(c, 'egg') # - Bound method call: 인스턴스 객체를 통한 메소드 호출 (self 인자는 호출받은 객체가 자동으로 할당) # - 예: c.set('egg') # ### 2-2 객체 내부에서의 다른 맴버 메쏘드 호출 # In[3]: class MyClass: def set(self, v): self.value = v def incr(self): self.set(self.value + 1) # 내부 메소드 호출 def get(self): return self.value c = MyClass() c.set(1) print(c.get()) print() c.incr() print(c.get()) # - [NOTE!!!] 만약 위 코드에서 self.set(self.value + 1)set(self.value + 1)으로 바꾸면 set 함수를 클래스 외부에서 찾는다. # In[4]: def set(i): print("set function outside function - ", i) class MyClass: def set(self, v): self.value = v def incr(self): set(self.value + 1) # 클래스 외부에 존재하는 set 메소드 호출 def get(self): return self.value c = MyClass() c.set(1) print(c.get()) print() c.incr() print(c.get()) # ### 2-3 정적 메소드(static method) # - 정적 메소드: 인스턴스 객체와 무관하게 클래스 이름 공간에 존재하는 메소드로서 클래스 이름을 이용하여 직접 호출할 수 있는 메소드 # - self를 인자로 할당하지 않음 # - [주의] 해당 클래스의 인스턴스를 통해서도 호출 가능 # # - 장식자(Decorator) @staticmethod 활용 # In[22]: class D: @staticmethod def spam(x, y): # self가 없다. print('static method', x, y) D.spam(1, 2) # 인스턴스 객체 없이 클래스에서 직접 호출 print() d = D() d.spam(1, 2) # 인스턴스 객체를 통해서도 호출 가능 # ### 2-4 클래스 메소드(class method) # - 클래스 메소드: 인스턴스 객체와 무관하게 클래스 이름 공간에 존재하는 메소드로서 클래스 이름을 이용하여 호출하며 ***첫 인수로 클래스 객체를 자동으로 받는 메소드*** # - [주의] 해당 클래스의 인스턴스를 통해서도 호출 가능 # - 장식자(Decorator) @classmethod 활용 # In[24]: class C: @classmethod def spam(cls, y): print(cls, '->', y) print(C) C.spam(5) # 첫번째 인수로 C가 잠재적으로 전달된다. c = C() c.spam(5) # 인스턴스 객체를 통해서도 호출 가능. # - 상속받은 서브 클래스를 통해 호출하면, 첫 인수에는 서브 클래스 객체가 자동으로 할당됨 # In[25]: class D(C): pass print(D.spam(3)) print() d = D() print(d.spam(3)) print() print(C.spam(3)) # *** # ## 3 클래스 멤버와 인스턴스 멤버 # *** # - 클래스 멤버 vs. 인스턴스 멤버 # - 클래스 멤버 # - 클래스 이름 공간에 생성됨 # - 모든 인스턴스들에 의해 공유됨 # - 인스턴스 멤버 # - 인스턴스 이름 공간에 생성됨 # - 각각의 인스턴스 마다 독립성이 보장됨 # In[6]: class Var: c_mem = 100 # 클래스 멤버 정의 def f(self): self.i_mem = 200 # 인스턴스 멤버 정의 def g(self): print(self.i_mem) print(self.c_mem) # In[7]: print(Var.c_mem) # 클래스 객체를 통하여 클래스 멤버 접근 v1 = Var() # 인스턴스 v1 생성 print(v1.c_mem) # 인스턴스를 통하여 클래스 멤버 접근 v1.f() # 인스턴스 멤버 i_mem이 생성됨 print(v1.i_mem) # 인스턴스 v1을 통하여 인스턴스 멤버 접근 print("-------") v2 = Var() # 인스턴스 v2 생성 print(v2.c_mem) print(v2.i_mem) # 인스턴스 v2에는 아직 f() 호출이 안되어서 i_mem 멤버 없음 ==> 생성자의 필요성 # ![inheritance](../images/instance2.png) # - "인스턴스 이름.멤버 이름"으로 멤버를 참조할 때 멤버의 검색 순서 # - 1) 인스턴스 멤버 # - 2) 인스턴스 멤버가 없다면 클래스 멤버 # In[8]: print(v1.c_mem) # 인스턴스 v1을 통해 클래스 멤버 참조 print(v2.c_mem) # 인스턴스 v2를 통해 클래스 멤버 참조 print() v1.c_mem = 50 # 인스턴스 이름 공간에 c_mem생성 print(v1.c_mem) # 인스턴스 v1을 통해 인스턴스 멤버 참조 print(v2.c_mem) # 인스턴스 v2을 통해 클래스 멤버참조 (인스턴스 멤버가 없으므로, 클래스 멤버 참조) print(Var.c_mem) # 클래스 멤버참조 # ![inheritance](../images/instance3.png) # *** # ## 4 생성자와 소멸자 # *** # - \_\_init__: 생성자 메소드 # - 객체가 생성될 때 자동으로 불리어지는 메소드 # - 일반적으로 객체가 보유해야 할 변수나 자원들의 초기화하는 코드를 작성함 # - self 인자가 정의되어야 함 # # - \_\_del__: 소멸자 메소드 # - 객체가 소멸 (메모리에서 해제)될 때 자동으로 불리어지는 메소드 # - 일반적으로 객체가 점유하고 있는 메모리나 기타 자원들의 해제하는 코드를 작성함 # - self 인자가 정의되어야 함 # - 개발자가 특별히 작성하지 않아도 될 메소드 # - 이유: 파이썬에서는 객체가 점유하고 있는 메모리나 기타 자원들의 해제가 자동으로 되기 때문에 # - [참고] \_\_ (연속된 두 개의 언더라인)의 의미: 예약된 (특수) 이름 # - 다음 코드에 대한 설명 # - mylife = Life() 로서 로컬 변수에 할당되는 인스턴스 mylife가 생성되는 순간 \_\_init__ 생성자 메소드 호출 # - sleep(3)에 의해 3초간 sleep 상태 # - 3초 이후 함수가 리턴됨 --> 로컬 변수, 즉 mylife 객체가 메모리에서 해제됨 --> \_\_del__ 소멸자 메소드 호출 # In[34]: # _*_ coding:utf-8 _*_ from time import ctime, sleep class Life: def __init__(self): # 생성자 self.birth = ctime() # 현재시간에 대한 문자열을 얻는다. print('Birthday', self.birth) # 현재 시간 출력 def __del__(self): # 소멸자 print('Deathday', ctime()) # 소멸 시간 출력 def test(): mylife = Life() print('Sleeping for 3 sec') sleep(3) #3초간 sleep(block)상태에 있음 (즉, 3초간 CPU 점유 못함) test() # - 인자를 받는 생성자 호출 가능 # - [참고] \_\_str\_\_: print 예약어나 str() 내장함수 호출에 대응되는 메소드 # In[35]: class Integer: def __init__(self, i): self.i = i def __str__(self): return "self.i:" + str(self.i) i = Integer(10) print(i) print(str(i)) #

참고 문헌: 파이썬(열혈강의)(개정판 VER.2), 이강성, FreeLec, 2005년 8월 29일