생물정보학/Python Study

5. 파이썬 클래스 - 파이썬(Python) 배우기

케이든 2014. 10. 17. 08:44

 

5. 파이썬 클래스 - 파이썬(Python) 배우기

 

클래스는 데이터(속성, attribute)와 그에 대한 처리를 하나로 정의한 것으로 파이썬 오브젝트들은 특정 클래스의 인스턴스이다.
클래스의 인스턴스인 오브젝트는 아래와 같은 생애주기를 갖는다.

  • 정의 : class 문을 통해 클래스를 정의한다.(시스템 클래스도 마찬가지)
  • 생성 : 클래스 정의에 따라 오브젝트를 생성하고 __init__를 호출하여 속성을 초기화한다.
  • 작업 : 메쏘드 또는 메쏘드 함수를 통하여 작업을 수행한다.
  • 정리(Garbage Collection) : 더이상 사용하지 않는 오브젝트를 정리한다

 

* 클래스 정의

클래스 정의는 아래와 같은 문형을 갖는다.

class 클래스명(부모 클래스):
코드 블럭


클래스명은 변수명 규칙을 따르지만 첫문자를 대문자로 해서 혼란을 방지한다. 메쏘드나 속성은 소문자로 작성.
부모 클래스는 상속 받을 클래스를 기술하는데, 단순하게 object를 기술해도 되고 괄호를 통째로 생략할 수도 있다.
코드 블럭에는 함수 정의에서 언급했던 Docstring이("""....""" 방식으로 문서화 정보 기술) 올수 있고,
함수정의와 대입문을 통한 속성 정의가 올 수 있다. 속성 정의에서 변수명 앞에 __를 붙이면 클래스 외부에서 해당 속성을 접근할 수 없도록 숨겨진다(실제로 내부적으로는 이름을 변경해서 저장한다) 그렇지만 이것은 단순한 이름 변경이고 다른 OOP언어에서의 private과 같은 개념을 파이썬은 지원하지 않는다.
클래스내 함수는 메쏘드 또는 메쏘드 함수라 불리우며 반드시 첫번째 파라미터로 self가 와야 한다. 아래는 bdb 모듈의 내장 클래스 Bdb 일부이다.

class Bdb:

    """Generic Python debugger base class.

    This class takes care of details of the trace facility;
    a derived class should implement user interaction.
    The standard debugger class (pdb.Pdb) is an example.
    """

    def __init__(self, skip=None):
        self.skip = set(skip) if skip else None
        self.breaks = {}
        self.fncache = {}

    def canonic(self, filename):
        if filename == "<" + filename[1:-1] + ">":
            return filename


 

 * 클래스 인스턴스 정의와 사용

"a = MyClass()" 처럼 클래스 이름에 함수 호출처럼 기술하며 클래스의 인스턴스를 생성하여 리턴한다.
메쏘드를 사용할 때는 생성한 오브젝트명.메쏘드의 형식으로 사용한다. 속성은 클래스 레벨로 정의된 클래스 속성(예제에서 attr1)과 인스턴스 레벨로 사용할 수 있는 인스턴스 속성이(예제에서 attr2) 있는데 인스턴스 속성은 동일 클래스로 부터 생성되었다 하더라도 서로 다른 인스턴스 간에는 상호 접근할 수 없다. 메쏘드 내에서 self.속성은 인스턴스 속성을 지칭한다.  클래스 속성은 오브젝트명.속성과 클래스명.속성으로 모두 사용할 수 있는데 오브젝트명.속성으로 기술한 경우에동일한 이름의 인스턴스 속성이 있으면 인스턴스 속성이 클래스 속성에 우선하여 접근한다.
dir(인스턴스명)으로 인스턴스를 통해 사용할 수 있는 모든 속성을 받을 수 있다.

>>> class MyClass:
attr1 = 100
def mf1(self, n) :
self.attr2 = n+2
return n+2
def mf2(self, n) :
self.attr2 = n*2
return n*2
>>> MyClass.attr1
100
>>> a = MyClass()
>>> a.attr1
100
>>> a.mf1(10)
12
>>> a.attr2
12


 
 

* 특별한 메쏘드

아래의 메쏘드들은 시스템에서 특별한 용도로 사용하는 메쏘드 이름으로 클래스 내부에 해당 이름의 메쏘드를 정의해 두면 해당 클래스에 대한 작업을 자연스럽게 적용할 수 있게 된다.

  • __init__ 클래스 인스턴스 생성후 초기화 작업을 위해 자동 호출된다. __init__함수의 파라미터는 클래스 인스턴스 생성시 전달해야한 파라미터와 일치된다.

>>> class MyClass:

attr1 = 100
def __init__(self, at):
MyClass.attr1 = at
def mf1(self, n) :
self.attr2 = n+2
.....
>>> a = MyClass(30)
>>> a.attr1
30

 

  • __del__ 인스턴스가 소멸될 때 호출되는 것으로. GC에서 자동정리하므로 많이 사용하지는 않는다.
  • __str__ print문에 해당 클래스가 인수로 등장했을때 호출된다. str(오브젝트) 형변환일 때도 호출된다.
  • __int__ 형변환으로 __long__, __float__, __oct__, __hex__, __complex__등도 있다.
  • __repr__ 인스턴스의 자세한 값을 보고 싶을때 사용하는 repr(오브젝트)을 사용할 때 호출된다.
  • __cmp__ 리스트내 정렬, 오브젝트간 비교등의 작업시에 호출되며 메쏘드를 작성하는 경우 1, 0 ,-1 중 하나를 리턴한다.
  • __add__ 오브젝트에 + 연산자를 사용했을때 호출된다. + 연산자 좌측에 있을때 호출됨 우측에 있을때는 __radd__, +=는 __iadd__
  • __sub__ - 연산자, __rsub__, __isub__
  • __mul__ * 연산자, __rmul__, __imul__
  • __div__ / 연산자, __rdiv__, __idiv__
  • __mod__ % 연산자, __rmod__, __imod__
  • __pow__ ** 연산자, __rpow__, __ipow__
  • __coerce__ 클래스에 대한 이항연산 수행전에 호출되어 사전 작업이 가능토록 한다. __coerce__ 메쏘드를 정의할떄 리턴에는 두개의 파라미터를 넘겨야 한다.
  • __len__ len() 수행시 호출됨
  • __nonzero__ bool(객체)등의 경우처럼 객체가 참인지 거짓인지를 판별할때 호출된다. True 또는 False 리턴
  • __contains__ in  수행시 호출됨
  • __getitem__ 오브젝트[키] 식으로 참조하는 경우 호출된다. for  in 문장, list(), tuple() 변환시에도 호출된다.
  • __setitem__ 오브젝트[키]=값 식으로 값을 설정하는 경우
  • __delitem__ del 오브젝트[키] 식으로 삭제하는 경우
  • __getattr__ 존재하지 않는 인스턴스 속성을 참조할때 호출된다. 자신의 클래스에 속하지 않는 함수나 속성을 참조할때 발생하므로 이때 적절한 코드를 수행하여 위임(Delegation)을 구현할 수 있다.
  • __setattribute__ 인스턴스 속성에 값을 대입할 때 호출된다.
  • __delattr__ 인스턴스 속성을 삭제할때 호출된다.
  • property(참조함수, 설정함수, 삭제함수, Docstring) 속성에 대한 처리를 수행하는 함수들을 인수로 하여 속성 타입을 리턴한다. 클래스 정의시 속성 타입의 속성을 정의할때는 참조 및 설정 메쏘드를 먼저 정의하고 property 타입은 속성의 참조 설정등을 처리할 메쏘드만을 관리하므로 별도의 인스턴스 속성을 관리하는 등의 처리과정이 있어야 한다.  단, 클래스는 object로 부터 상속을 받아야 한다.
  • __call__ 클래스 인스턴스명 자체로 함수처럼 사용할 때 호출 된다. __call__ 메쏘드를 정의한 클래스의 인스턴스는 callable(인스턴스)에 True 리턴한다.
  • __slots__ 클래스의 인스턴스에는 동적으로 인스턴스를 속성을 정의해 사용할 수 있고 이들은 인스턴스명.__dict__로 확인할 수 있다. 그런데 미리 정해진 속성만 사용하도록 강제할 수도 있는데 클래스 정의시 "__slots__ = ['속성명',....]" 방식으로 기술해 놓으면 된다. 단, 클래스는 object로 부터 상속을 받아야 한다. __slots__를 사용하면 __dict__는 사용하지 않는다.
  • 정적메쏘드 일반적으로 메쏘드는 오브젝트에 대해서만 사용할 수 있는데 메쏘드를 정의할 때  첫 인수로 self를 사용하지 않은 상태로 기술한 다음 "메쏘드명 = staticmethod(메쏘드명)"의 형식으로 지정하면 해당 메쏘드를 인스턴스 없이 클래스명.메쏘드로 직접 사용할 수 있다.
  • 클래스메쏘드 첫인수가 self인 일반 메쏘드들은 메쏘드를 호출하면 해당 인스턴스를 넘겨받는데, 인스턴스 대신 클래스를 전달받을 수 있고 인스턴스 없이 메쏘드 호출이 가능하다. self대신 클래스를 받을 인수를 정의하고 "메쏘드명 = classmethod(메쏘드명)"의 형식으로 지정해주면 된다.
  • 장식자(decorator) 정적메쏘드나 클래스메쏘드를 정의하는 방법 대신 메쏘드 상단에 "@장식자"의 형식으로 기술하는 방법으로 Aspect-oriented programming의 개념이 적용된 것이다. 파이썬에서 장식자는 일종의 함수로 메쏘드 앞에 적용되어 기존 함수를 다른 함수로 전환시켜주는 역할을 한다. 시스템에서 기본 제공하는 장식자는 앞서 사용한 @staticmethod, @classmethod이 있고 사용자가 직접 정의해서 타입 검사나 함수 디버깅 등에 다양한 용도로 사용할 수 있다. 아래는 장식자를 정의하는 방법이다.

def 장식자이름( 함수인수 ):
   def 내부함수명( *args, **keywords ):
함수인수_개선코드...  
    함수인수(args, keywords) # 기존 함수 호출
함수인수_개선코드...
   내부함수명.__name__= 함수인수.__name__
   내부함수명.__doc__= 함수인수.__doc__
   return 내부함수명

 

위에서 정의한 장식자를 메쏘드위에 "@장식자이름" 으로 지정하면 함수인수로 해당 메쏘드가 전달되고 메쏘드의 인수들은 args, keywords로 전달되어 개선 코드와 함께 원래 메쏘드가 호출되는 방식이다. 아래는 함수 호출 로깅을 위한 좋은 장식자 예제이다.(http://www.linuxtopia.org/online_books/programming_books/python_programming/python_ch26s03.html
참조)

 

def trace( aFunc ):
    """Trace entry, exit and exceptions."""
    def loggedFunc( *args, **kw ):
        print "enter", aFunc.__name__
        try:
            result= aFunc( *args, **kw )
        except Exception, e:
            print "exception", aFunc.__name__, e
            raise
        print "exit", aFunc.__name__
        return result
    loggedFunc.__name__= aFunc.__name__
    loggedFunc.__doc__= aFunc.__doc__
    return loggedFunc

 

장식자에 파라미터를 넘겨줄 경우에는 위에서 정의한 장식자 정의를 포함하고 인수는 장식자로 넘겨지는 파라미터를 받고 내포하는 장식자 함수 값을 리턴하는 형태로 정의하면 된다. 아래는 디버깅 옵션을 인수로 전달받는 장식자 정의 예제이다.

 

def debug( theSetting ):
    def concreteDescriptor( aFunc ):
        if theSetting:
            def debugFunc( *args, **kw ):
                print "enter", aFunc.__name__
                return aFunc( *args, **kw )
            debugFunc.__name__= aFunc.__name__
            debugFunc.__doc__= aFunc.__doc__
            return debugFunc
        else:
            return aFunc
    return concreteDescriptor


 

* 상속(Inheritance)

상속은 클래스 정의 핵심 개념중 하나로 범용의 슈퍼 클래스를 두고 슈퍼 클래스의 모든 속성을 상속 받으면서 전문적인 부가정보를 갖는 서브 클래스를 정의하는 방식이다.

  • 한개 이상의 슈퍼 클래스로부터 상속을 받을 수 있고, 시스템 내장 클래스도 상속 받아 기능을 확장할 수 있다.
  • 생성자(__init__)는 서브 클래스 부터 슈퍼 클래스로 계층적으로 이동하면서 한번만 호출된다. 즉 서브 클래스에 __init__가 정의되어 있으면 서브 클래스의 생성자만 호출되고 서브 클래스에 없고 슈퍼 클래스에 있으면 슈퍼 클래스 생성자가 호출된다.
  • 따라서, 서브 및 슈퍼 클래스에 모두 생성자가 있는 경우에는 서브 클래스 __init__에서 슈퍼 클래스 생성자를 명시적으로 호출해 주어야 한다. "슈퍼클래스명.__init__(self)"의 형식이다.
  • 서브 클래스에 슈퍼클래스의 메쏘드와 동일한 이름의 메쏘드를 정의하면(Override) 참조 우선순위는 서브 클래스에 있기 때문에 메쏘드를 대치하는 효과를 낸다. 슈퍼 클래스의 메쏘드가 동일 클래스내의 다른 메쏘드를 호출할 때 서브 클래스에서 해당 메쏘드를 Override했다면 슈퍼 클래스의 메쏘드 대신 서브 클래스의 메쏘드가 호출된다.
  • 대부분의 파이썬 기본 타입은 클래스 이름이다.
  • isinstance(오브젝트, 타입또는클래스) 오브젝트가 지정한 타입 또는 클래스의 인스턴스면 True 리턴
  • issubclass(클래스, 베이스클래스) 클래스가 베이스클래스의 서브 클래스면 True 리턴
  • __mro__ 클래스 상속이 복잡한 경우 참조 순서도 복잡해 질 수 있는데 파이썬에서는 슈퍼 클래스에서 object를 상속받은 경우 각 클래스 마다 __mro__속성에 메쏘드 처리순서(MRO, Method Resolution Order) 정보를 기록하고 클래스명.mro()로 조회할 수 있다.
  • super(클래스, self).메쏘드() MRO순서 상의 슈퍼클래스에 속하는 메쏘드를 호출한다.
  • __bases__ 상속 받은 모든 슈퍼 클래스를 튜플로 리턴
  • min(클래스배열, key=메쏘드) key값을 리턴하는 메쏘드를 통해서 클래스 배열에 대한 min()을 구할 수 있게 한다. max()도 마찬가지 형태로 사용할 수 있고, 클래스 내에 해당 메쏘드가 정의되어 있어야 한다.
  • 클래스가 object를 상속받은 경우에는 클래스명 자체가 타입으로 사용할 수 있다.


 

* 서술자(Descriptor)

클래스 내에서 특정 오브젝트의 값을 참조, 설정, 삭제 처리를 처리해 주는 클래스이다. 상위 클래스의 속성 정의 형태로 서술자를 기술하고 각 서술자 클래스는 미리 정의된 아래와 같은 메쏘드를 정의해야 한다.

  • __get__(self, 인스턴스, 소유자) 서술자 오브젝트를 참조할 때 호출된다.
  • __set__(self, 인스턴스, 값) 서술자 오브젝트에 값을 설정할 때 호출된다.
  • __delete__(self, 인스턴스) 서술자 오브젝트가 삭제 될때 호출된다.


property 객체로 속성을 정의하는 것과 유사하게 동작한다고 할 수 있다. 아래는 화씨와 섭씨를 동시에 가지고 있는 클래스를 표현한 것이다.

class Celsius( object ):
    def __init__( self, value=0.0 ):
        self.value= float(value)
    def __get__( self, instance, owner ):
        return self.value
    def __set__( self, instance, value ):
        self.value= float(value)
       
class Farenheit( object ):
    def __get__( self, instance, owner ):
        return instance.celsius * 9 / 5 + 32
    def __set__( self, instance, value ):
        instance.celsius= (float(value)-32) * 5 / 9

class Temperature( object ):
    celsius= Celsius()
    farenheit= Farenheit()


 
 

* with 문

시작-처리-종료의 과정을 밟는 파일 처리, 데이터베이스처리, 네트워크 처리를 다루는 클래스를 위해서 파이썬은 with 문을 제공하고 있다. 문법은 아래와 같다.

with 클래스 생성자 as 오브젝트:
코드블럭


파일을 예로 든다면

f = file('test.txt','r')
for line in f:
print line
f.close()


은 with 문으로 전환하면 아래와 같이 전환시킬 수 있다.

with file('test.txt','r') as f:
for line in f:
print line


with문은 시작 시점에 해당 클래스의 __enter__(self) 메쏘드를 호출해주고, with문 끝에 __exit__(type, value, traceback) 메쏘드를 호출해 주므로 해당 시점에 적절한 코드를 클래스 정의에 포함시키면 with문에 따른 효과적인 처리가 가능해진다.

 

[출처] - |주|동운시스템 http://www.dongwun.com/tc/142