19. 클래스와 객체




1 파이썬 클래스와 이름 공간


  • 파이썬 클래스는 새로운 이름 공간을 지원하는 또 다른 단위
  • 클래스 정의 구문

    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)
1

2

['__doc__', '__module__', 'a', 'b']
['__doc__', '__module__', 'a']
  • 파이썬에서는 동적으로 인스턴스 외부에서 인스턴스 멤버를 추가할 수 있음
    • 파이썬에서는 클래스와 독립적으로 각 인스턴스를 하나의 이름 공간으로 취급함
In [2]:
x = S1() # x는 S1의 클래스 인스턴스
print x.a

x.a = 10 # 클래스 인스턴스 x의 이름 공간에 이름 생성
print x.a

print S1.a # 클래스 이름 공간과 클래스 인스턴스의 이름공간은 다르다
1
10
1
In [3]:
y = S1() # S1 클래스의 또 다른 인스턴스 생성

y.a = 300 # 클래스 인스턴스 y의 이름 공간에 이름 생성

print y.a 
print x.a # x 인스턴스 공간의 이름 a 확인
print S1.a # 클래스 이름 공간의 a 확인
300
10
1
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을 정의한 적이 없다.
[1, 2, 3]
3
2

[1]
---------------------------------------------------------------------------
AttributeError                            Traceback (most recent call last)
<ipython-input-5-bf2593ac4e2c> in <module>()
      9 print
     10 print s1.stack # 최종 s1.stack값
---> 11 print s2.stack # s2에는 stack을 정의한 적이 없다.

AttributeError: Simple instance has no attribute 'stack'

inheritance

In [6]:
del s1.stack  # s1에서 stack삭제

2 메쏘드의 정의와 호출


2-1 일반 메쏘드의 정의와 호출

  • 클래스 내부에 메소드 선언 - def 키워드 사용
    • 일반 함수와 다른 점은 첫번째 인수로 self 사용 (self라는 이름은 관례적)
      • self: 인스턴스 객체 자신의 레퍼런스를 지니고 있음
      • 각 인스턴스들은 self를 이용하여 자신의 이름 공간에 접근
In [7]:
class MyClass:
    def set(self, v):
        self.value = v
    def get(self):
        return self.value
  • 인스턴스 객체를 통하여 메소드를 호출할 때 self 인자는 없다고 생각
In [8]:
c = MyClass() # 인스턴스 생성 
c.set('egg')  # 메소드 set 호출
print c.get() # 메소드 get 호출
print c.value # 인스턴스 변수에 직접 접근
egg
egg
  • 위 코드는 실제로 아래 코드와 동일함
In [9]:
c = MyClass() # 인스턴스 생성 
MyClass.set(c, 'egg')
print MyClass.get(c)
print c.value
egg
egg
In [10]:
class Simple:
    pass

c = MyClass()
s1 = Simple()
MyClass.set(s1, 'egg')  # 다른 클래스의 인스턴스를 넣어주면 에러 발생
---------------------------------------------------------------------------
TypeError                                 Traceback (most recent call last)
<ipython-input-10-0660940f4507> in <module>()
      4 c = MyClass()
      5 s1 = Simple()
----> 6 MyClass.set(s1, 'egg')  # 다른 클래스의 인스턴스를 넣어주면 에러 발생

TypeError: unbound method set() must be called with MyClass instance as first argument (got Simple instance instead)
  • 메소드 호출 종류
    • Unbound method call: 클래스 객체를 이용한 메소드 호출
      • 예: MyClass.set(c, 'egg')
    • Bound method call: 인스턴스 객체를 통한 메소드 호출 (self 인자는 호출받은 객체가 자동으로 할당)
      • 예: c.set('egg')

2-2 클래스 내부에서의 메쏘드 호출

In [11]:
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()
1

2
  • 만약 위 코드에서 self.set(self.value + 1)를 set(self.value + 1)으로 바꾸면 set 함수를 클래스 외부에서 찾는다.
In [21]:
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()
1

set function outside function -  2
1

2-3 정적 메소드(static method)

  • 정적 메소드: 인스턴스 객체와 무관하게 클래스 이름 공간에 존재하는 메소드로서 클래스 이름을 이용하여 직접 호출할 수 있는 메소드
    • [주의] 해당 클래스의 인스턴스를 통해서도 호출 가능
  • 장식자(Decorator) @staticmethod 활용
In [13]:
class D:
    @staticmethod
    def spam(x, y):            # self가 없다.
        print 'static method', x, y
        
D.spam(1,2) # 인스턴스 객체 없이 클래스에서 직접 호출

print
d = D()
d.spam(1,2) # 인스턴스 객체를 통해서도 호출 가능
static method 1 2

static method 1 2

2-4 클래스 메소드(class method)

  • 클래스 메소드: 인스턴스 객체와 무관하게 클래스 이름 공간에 존재하는 메소드로서 클래스 이름을 이용하여 호출하며 첫 인수로 클래스 객체를 자동으로 받는 메소드
    • [주의] 해당 클래스의 인스턴스를 통해서도 호출 가능
  • 장식자(Decorator) @classmethod 활용
In [31]:
class C:
    @classmethod
    def spam(cls, y):
        print cls, '->', y
    
print C

C.spam(5) # 첫번째 인수로 C가 잠재적으로 전달된다.

c = C()
c.spam(5) # 인스턴스 객체를 통해서도 호출 가능.
__main__.C
__main__.C -> 5
__main__.C -> 5
  • 상속받은 서브 클래스를 통해 호출하면, 첫 인수에는 서브 클래스 객체가 자동으로 할당됨
In [33]:
class D(C):
    pass

print D.spam(3)
print

d = D()
print d.spam(3)
print

print C.spam(3)
__main__.D -> 3
None

__main__.D -> 3
None

__main__.C -> 3
None

3 클래스 멤버와 인스턴스 멤버


  • 클래스 멤버 vs. 인스턴스 멤버
    • 클래스 멤버
      • 클래스 이름 공간에 생성됨
      • 모든 인스턴스들에 의해 공유됨
    • 인스턴스 멤버
      • 인스턴스 이름 공간에 생성됨
      • 각각의 인스턴스 마다 독립성이 보장됨
In [16]:
class Var:
    c_mem = 100 # 클래스 멤버 정의
    def f(self): 
        self.i_mem = 200 # 인스턴스 멤버 정의
    def g(self):
        print self.i_mem
        print self.c_mem        
In [17]:
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.i_mem   # 인스턴스 v2에는 아직 f() 호출이 안되어서 i_mem 멤버 없음 ==> 생성자의 필요성
100
100
200

---------------------------------------------------------------------------
AttributeError                            Traceback (most recent call last)
<ipython-input-17-1950cf0552a9> in <module>()
      8 print
      9 v2 = Var()       # 인스턴스 v2 생성
---> 10 print v2.i_mem   # 인스턴스 v2에는 아직 f() 호출이 안되어서 i_mem 멤버 없음 ==> 생성자의 필요성

AttributeError: Var instance has no attribute 'i_mem'

inheritance

  • "인스턴스 이름.멤버 이름"으로 멤버를 참조할 때 멤버의 검색 순서
    • 1) 인스턴스 멤버
    • 2) 인스턴스 멤버가 없다면 클래스 멤버
In [18]:
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  # 클래스 멤버참조
100
100

50
100
100

inheritance


4 생성자와 소멸자


  • __init__: 생성자 메소드
    • 객체가 생성될 때 자동으로 불리어지는 메소드
    • self 인자가 정의되어야 함
  • __del__: 소멸자 메소드
    • 객체가 소멸 (메모리에서 해제)될 때 자동으로 불리어지는 메소드
    • self 인자가 정의되어야 함
    • 개발자가 특별히 작성하지 않아도 될 메소드
      • 이유: 파이썬에서는 메모리나 기타 자원들의 해제가 자동으로 되기 때문에
  • [참고] __ (연속된 두 개의 언더라인)의 의미: 예약된 이름
  • 다음 코드에 대한 설명
    • mylife = Life() 로서 인스턴스 mylife가 생성되는 순간 __init__ 생성자 메소드 호출
    • sleep(3)에 의해 3초간 sleep 상태
    • 3초 이후 함수가 리턴됨 --> 로컬 변수가 메모리에서 해제됨 --> __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)상태에 있음 (CPU 점유 못함)

test()
Birthday Wed Nov 18 22:51:54 2015
Sleeping for 3 sec
Deathday Wed Nov 18 22:51:57 2015
  • 인자를 받는 생성자 호출 가능
  • [참고] __str__: print 예약어나 str() 내장함수 호출에 대응되는 메소드
In [38]:
class Integer:
    def __init__(self, i):
        self.i = i
    def __str__(self):
        return str(self.i)

i = Integer(10)
print i
print str(i)
10
10

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