라롸 2020. 5. 8. 09:12

1. 클래스 선언

class Person:       #클래스 정의
   name = 'tom'     #멤버 변수
   def Print(self): #멤버 메소드
       print('my name is {}'.format(self.name))
       
p = Person()   #인스턴스 객체 생성
p.Print()      #my name is tom

 

  클래스를 정의하면 클래스 객체가 생성되고 독립적인 이름공간이 만들어진다. 이렇게 생성된 공간에 멤버 변수와 메소드가 존재하게 된다. 정의된 클래스를 사용하려면 일반적으로 인스턴스 객체를 만들어야 한다. 인스턴스 객체 생성은 클래스의 이름을 사용해 함수를 호출하는 형태이며, 클래스와 동일하게 인스턴스 객체가 생성되고 독립적인 이름공간이 생성된다. 기본적으로 인스턴스 객체가 변경되기 전까지는 클래스 객체와 동일한 데이터와 메소드를 가르킨다. 인스턴스 객체의 데이터가 변경되면 클래스 객체의 데이터와 구분하기 위해 인스턴스 객체 이름공간에 변경된 데이터를 저장한다. 반면 아직 변경되지 않은 데이터와 메소드는 여전히 클래스 객체와 공간을 보유하고 있다.

 

  멤버 메소드 정의를 보면 첫 인자로 self가 있다. 이것은 현재 인스턴스 객체를 가리키는 것으로 c++이나 java의 this 키워드와 동일하지만 파이썬에서는 특별히 예약어로 지정되어 있지는 않다. 이를 통해 인스턴스 객체의 이름공간에 접근하기 때문에 클래스 공간의 정적 메소드나 클래스 메소드를 제외하고 명시적으로 메소드의 첫 인자는 인스턴스 객체가 된다. 

 

[객체와 인스턴스의 차이]
클래스로 만든 객체를 인스턴스라고도 한다. 그렇다면 객체와 인스턴스의 차이는 무엇일까?
이렇게 생각해 보자. a = Cookie() 이렇게 만든 a는 객체이다. 그리고 a 객체는 Cookie의 인스턴스이다. 즉 인스턴스라는 말은 특정 객체(a)가 어떤 클래스(Cookie)의 객체인지를 관계 위주로 설명할 때 사용한다. "a는 인스턴스"보다는 "a는 객체"라는 표현이 어울리며 "a는 Cookie의 객체"보다는 "a는 Cookie의 인스턴스"라는 표현이 훨씬 잘 어울린다.

 

2. 클래스 객체와 인스턴스 객체의 이름공간

기본적으로 인스턴스 객체를 통해 변수나 함수의 이름을 찾는 경우 아래의 순서로 그 이름을 찾는다.

인스턴스 객체 영역 -> 클래스 객체 영역 -> 전역 영역

파이썬에서는 런타임에 각 클래스와 인스턴스 이름 공간에 멤버 변수를 추가하거나 삭제할 수 있다. 클래스와 인스턴스 이름 공간이 분리되었기 때문에 가능하다.

 

class Person:       #클래스 정의
    name = 'default'

p1 = Person()   #인스턴스 생성
p2 = Person()

p1.name = 'kim'   #p1 인스턴스의 name 속성 변경
print(p2.name)    #default

Person.title = 'New title'   #클래스 겍체에 새로운 멤버 변수 추가
print(p1.title, p2.title)    #두 인스턴스 객체에서 모두 접근 가능

p1.age = 20                 #p1 인스턴스 객체에만 age 멤버 변수 추가
print(p2.age, Person.age)   #p2객체와 상위 클래스 Person에서는 age를 찾을 수 없음

인스턴스 객체에 동적으로 멤버 변수를 추가하는 경우, 추가한 인스턴스 객체를 통해서만 접근 가능하다.

 

 

3. 클래스 내에서 self

str = 'Not Class Member'

class ggString:
    str = ''
    def setSt(self, msg):
        self.str = msg
    def print(self):
        print(str)
        
g = ggString()
g.setSt('test')
g.print()   #Not Class Member

  코드 작성 시 주로 발생하는 실수 중 하나가 클래스 메소드 내에서 self를 통하지 않고 변수에 접근하는 것이다. 위 코드에서 전역과 클래스 영역에 동일한 이름의 변수 str이 존재한다. 멤버 메소드인 print() 내부에서 self를 통하지 않고 str을 출력하는 경우 전역변수의 str을 출력하고 있다.

 

 

4. 정적 메소드, 클래스 메소드

  메소드의 확장 형태로 정적 메소드와 클래스 메소드가 있다. 정적 메소드는 인스턴스 객체를 통하지 않고 클래스를 통해 직접 호출할수 있는 메소드이다. 이 경우 메소드를 정의할 때 self를 선언하지 않는다. 클래스 메소드는 암묵적으로 첫 인자로 클래스 객체가 전달된다.

 

 - 기본 예제

class count:
    ins = 0
    def __init__(self):
        count.ins += 1
    def staticPrint():   #정적 메소드 정의
        print(count.ins)
    sPrint = staticmethod(staticPrint)  #정적 메소드 등록
    
    def classPrint(cls):   #클래스 메소드 정의
       print(cls.ins)
    cPrint = classmethod(classPrint)   #클래스 메소드 등록
    
a, b, c = count(), count(), count()
count.sPrint()
b.sPrint()
count.cPrint()
b.cPrint()

 

5. 생성자 소멸자 메소드 

 - 기본 구조

class Myclass:
    def __init__(self, value): #생성자 메소드
        self.value = value
        print('value is', value)
    
    def __del__(self):   #소멸자 메소드
        pass
        
def foo():
    d = Myclass(10)
    
foo() #value is 10

 

 

6. 클래스 상속

  상속을 이용하면 부모 클래스의 모든 속성(데이터, 메소드)를 자식 클래스로 물려줄 수 있다. 이렇게 함으로써 구현해야 하는 여러 클래스의 공통된 속성을 부모 클래스에 정의하고, 각 하위 클래스에서는 그에 맞는 메소드와 데이터를 정의할 수 있다. 그 결과 각 클래스마다 동일한 코드가 작성되는 것을 방지하고, 부모 클래스에 공통된 속성을 두어 코드를 유지보수하기가 쉬워진다.

 

 - 간단 예제

부모 클래스는 Person으로 name과 phone을 멤버로 가지고 printinfo(), printperson()을 메소드를 가진다.

자식 클래스는 추가적으로 subject, subjectID 멤버를 가진다.

class Person:
    def __init__(self, name, phone):
        self.name = name
        self.phone = phone
    
    def printinfo(self):
        pass
        
    def printperson(self):
        pass
class Student(Person):
    def __init__(self, name, phone, subject, id):
        Person.__init__(self, name, phone)
        self.subject = subject
        self.id = id
        
    def printinfo(self): #메소드 재정의
        print(self.name, self.phone, self.subject, self.id)

  자식 클래스는 부모 클래스의 생성자를 호출함으로써 멤버변수를 초기화 할 수 있다. 이때 명시적으로 부모 클래스 생성자를 호출해야하며 첫 인자인 self도 같이 전달해야 한다.

자식 클래스는 부모로 상속 받은 메소드를 그대로 사용할 수도 있지만 용도에 맞게 재정의(overriding) 해서 쓸 수도 있다. 이 때 재정의 하는 메소드는 부모 클래스 메소드의 이름은 동일해야 한다. 

 

 

 

 


참고 

1.  참고 서적 - 빠르게 활용하는 파이썬 3.6 프로그래밍