ㅍㅍㅋㄷ

python super() 함수와 2.X 와 3.X 사용법 본문

Programming/Python

python super() 함수와 2.X 와 3.X 사용법

클쏭 2015. 6. 25. 18:27

Python Super() 함수


Python에서 다중 상속시 발생할 수 있는 문제점이 있다.

이 현상은 Python 뿐만 아니라 다중 상속이 가능한 어떤 언어에서나 발생할 수 있는 문제이다.


아래와 같은 상황을 보자.



D 클래스가 B와 C 클래스를 상속 받고,

B와 C 클래스는 같은 부모 클래스인 A 클래스를 상속 받는 형태이다.


이때, D 클래스를 호출하게 되면 어떤 현상이 일어나게 될까?


D는 B를 상속받았으니, B의 생성자가 한번 실행되며 

B는 A를 상속 받았으니, A의 생성자를 실행할 것이다. 

또한 D는 C도 상속받았으니, C의 생성자를 한번 실행하며,  

C는 또 A를 상속 받았으니, A의 생성자를 실행할 것이다.


즉, A의 생성자는 두번 호출 되는 꼴이다.


#!/usr/bin/python


class A:

   def __init__(self):

      print("Class A __init__()")


class B(A):

   def __init__(self):

      print("Class B __init__()")

      A.__init__(self)


class C(A):

   def __init__(self):

      print("Class C __init__()")

      A.__init__(self)



class D(B, C):

   def __init__(self):

      print("Class D __init__()")

      B.__init__(self)

      C.__init__(self)



d = D()



위 코드를 실행 시켜 보면 아래와 같은 결과를 볼 수 있다.


$ python test.py

Class D __init__()

Class B __init__()

Class A __init__()

Class C __init__()

Class A __init__()


생성자가 두번 호출 된다.

위와 같은 문제를 어떻게 해결할 수 있을까?


Python에서는 super()라는 함수로 이 문제를 해결할 수 있다.


#!/usr/bin/python


class A:

   def __init__(self):

      print("Class A __init__()")

      super(A, self).__init__()


class B(A):

   def __init__(self):

      print("Class B __init__()")

      super(B, self).__init__()


class C(A):

   def __init__(self):

      print("Class C __init__()")

      super(C, self).__init__()


class D(B, C):

   def __init__(self):

      print("Class D __init__()")

      super(D, self).__init__()


d = D()


실행해 보면 아래와 같이 최상단 부모 클래스는 한번 호출 된다.


Class D __init__()

Class B __init__()

Class C __init__()

Class A __init__()


즉, 클래스를 사용하는 입장에서 전체 클래스의 상속 구조를 이해하지 못하였어도,

문제 없이 상속을 사용할 수 있다. 


super() 함수는 슈퍼클래스의 method를 호출하라는 의미인데, 이때 다수의 슈퍼 클래스가 존재시 클래스 호출 순서의 결정은 __mro__ 를 통해 결정된다. 


mro ( Method Resolution Order ) 가 무엇인지는 아래 링크 참고.

( 참고 : https://docs.python.org/2/library/stdtypes.html#class.__mro__


위 예제에서의 mro 호출 결과는 아래와 같다.


(<class '__main__.D'>, <class '__main__.B'>, <class '__main__.C'>, <class '__main__.A'>, <type 'object'>)



위 MRO 순서에 맞게 생성자 실행 순서도 D->B->C->A 순으로 호출되었음을 알 수 있다.


Class D __init__()

Class B __init__()

Class C __init__()

Class A __init__()








Python 2.X 에서 super()함수 사용시 주의할 점



super() 함수를 사용할때 python 버전에 따라 주의해야 할 점이 있다.

위 예제 코드는 python 3.X에서는 문제 없이 동작할 것이다.

하지만 python 2.X 에서는 에러가 발생한다.

Traceback (most recent call last):

  File "test2.py", line 23, in <module>

    d = D()

  File "test2.py", line 21, in __init__

    super(D,self).__init__()

TypeError: must be type, not classobj



먼저 super()함수의 내용을 자세히 살펴보자.

(참고 : https://docs.python.org/2/library/functions.html#super )


super(type[object-or-type])


Return a proxy object that delegates method calls to a parent or sibling class of type. This is useful for accessing inherited methods that have been overridden in a class.



여기서 주의할 것은 첫째 argument는 반드시 type 형태의 new-style class 이어야 한다는 것이다.

(참고 : https://docs.python.org/2/glossary.html#term-new-style-class )


python 2.X 에서는 class를 아래와 같이 정의할 시,

class A:

...


기본적으로 new-style이 아닌 old-style class로 생성된다. 

old-style class는 형태가 type 이기 때문에, super 함수에서 old-style class 로 할 경우 type 에러를 발생한다.

이러한 문제를 피하기 위해서는 반드시 python 2.X 에서는 class를 정의할 시 아래와 같이 명시하여 주어야 한다.


class A(object):

....



[참고]

  • https://docs.python.org/2/library/stdtypes.html#class.__mro__
  • https://docs.python.org/2/library/functions.html#super
  • https://docs.python.org/2/glossary.html#term-new-style-class


Comments