Python 클래스의 상속 (inheritance)
Jan 7, 2021

텐서플로의 Model Subclassing 구현을 위해서는 python의 상속(inheritance) 개념을 필히 알고 있어야 합니다. 그래서, 이번 포스팅에서는 python의 클래스 그리고 상속에 대한 내용을 다뤄 보도록 하겠습니다.

다만, Python의 클래스 기능은 이번 포스팅에서 모두 다루지 않고, TensorFlow에서 Model Subclassing 구현을 위한 가장 기본적인 내용만 다루도록 하겠습니다. 본 내용만 확실히 익혀도 Model Subclassing에는 큰 문제가 없을겁니다.

Python의 Class와 상속(inheritance)의 개념

  • Python 문법에서의 상속(inheritance)란, 부모 클래스(Super Class)의 속성(property)과 함수(method)를 그대로 물려 받는 개념입니다.
  • Super Class의 내용을 자식 클래스(Sub Class)가 물려 받게 되면, Super Class의 속성과 함수를 자식 클래스에서 사용할 수 있습니다.
  • class 에 속한 함수 (method)는 첫 번째 인자로 self를 입력합니다. (사실 self가 아니어도 상관없지만, 일반적으로 self를 많이 사용합니다.)
  • 자식클래스의 함수를 재정의하게 되면, 재정의된 함수로 실행되게 됩니다.
class Person:
    """Super Class"""
    # 클래스 변수
    total_count = 0
    
    # 생성자 메서드(method)
    def __init__(self):
        self.name = '홍길동'
        self.age = 1
        Person.total_count+=1
    
    # class내 정의된 메서드(method)
    def introduce(self):
        print(f'제 이름은 {self.name} 이고, 나이는 {self.age}살 입니다.')

인스턴스(instance) 생성과 객체(object)

객체는 바로 밑의 예시에서 p1, p2, p3를 일컫습니다.

정의된 클래스(class)로부터 생성된 녀석을 인스턴스(instance) 혹은 객체(object)라고 합니다.

하지만, 용어의 온도차(?)는 존재합니다.

p1 = Person()
p2 = Person()
p3 = Person()
p1.introduce()
제 이름은 홍길동 이고, 나이는 1살 입니다.

객체와 인스턴스의 차이

점프투 파이썬의 좋은 설명을 인용하도록 하겠습니다.


클래스로 만든 객체를 인스턴스라고도 한다.

그렇다면 객체와 인스턴스의 차이는 무엇일까?

이렇게 생각해 보자. p1 = Person() 이렇게 만든 p1객체이다.

그리고 p1 객체는 Person의 인스턴스이다.

인스턴스라는 말은 특정 객체(p1)가 어떤 클래스(Person)의 객체인지를 관계 위주로 설명할 때 사용한다.

"p1는 인스턴스"보다는 "p1은 객체"라는 표현이 어울리며 "p1는 Person의 객체"보다는 "p1은 Person의 인스턴스"라는 표현이 훨씬 잘 어울린다.

클래스 변수의 출력

클래스 변수는 모든 클래스가 공유하게 됩니다. 클래스의 객체가 3번 만들어 졌기 때문에 3이 출력되는 것을 확인할 수 있습니다.

p1.__class__.total_count
3
class Person:
    """Super Class"""
    # 클래스 변수
    total_count = 0
    
    # 생성자 메서드
    def __init__(self, name, age):
        self.name = name
        self.age = age
        Person.total_count+=1
    
    # class내 정의된 함수(method)
    def introduce(self):
        print(f'제 이름은 {self.name} 이고, 나이는 {self.age}살 입니다.')

다음을 실행시 오류가 발생합니다.

생성자 메서드가 재정의 되었기 때문입니다.

p4 = Person()
---------------------------------------------------------------------------
TypeError                                 Traceback (most recent call last)
<ipython-input-6-1e96c1bf9b60> in <module>()
----> 1 p4 = Person()

TypeError: __init__() missing 2 required positional arguments: 'name' and 'age'
p5 = Person('김철수', 22)
p5.introduce()
제 이름은 김철수 이고, 나이는 22살 입니다.

클래스 상속 (inheritance) 받기

class Student(Person):
    """Sub Class"""

    def __init__(self):
        super().__init__()

    def print_name(self):
        print(f'제 이름은 {self.name} 입니다.')
        
    def print_age(self):
        print(f'제 나이는 {self.age} 입니다.')
s1 = Student()
---------------------------------------------------------------------------
TypeError                                 Traceback (most recent call last)
<ipython-input-9-906bfe3cbabc> in <module>()
----> 1 s1 = Student()

<ipython-input-8-ed9ff6a99777> in __init__(self)
      3 
      4     def __init__(self):
----> 5         super().__init__()
      6 
      7     def print_name(self):

TypeError: __init__() missing 2 required positional arguments: 'name' and 'age'

오류를 어떻게 해결할 수 있을까?

Sub Class 에서의 오류

Student 클래스는 Person 클래스를 상속받아 구현하게 되면서, 생성자 (init) 함수에서 name, age argument를 요구합니다. 이 Rule을 지켜주지 못하면 에러가 발생합니다.

첫 번째 해결책

super().__init__() 호출시 name과 age argument를 넘겨줍니다.

class Student(Person):
    """Sub Class"""

    def __init__(self):
        super().__init__('테디', 30)
student = Student()

두 번재 해결책

Student 클래스의 __init__().__init__(name, age) 인자를 받는 생성자 메서드로 재정의할 수 있습니다.

class Student(Person):
    """Sub Class"""

    def __init__(self, name, age):
        super().__init__(name, age)
student = Student('테디', 30)
student.introduce()
제 이름은 테디 이고, 나이는 30살 입니다.

메서드 오버라이딩

  • 메서드 오버라이딩은 부모로부터 물려받은(상속받은) 메서드를 재정의하여 사용할 때 사용합니다.
  • 부모로부터 물려받은 다른 기능은 그대로 사용하되, 몇몇 메서드만 수정하여 활용하고 싶을 때 사용합니다.
class Person:
    """Super Class"""
    
    # PErson의 생성자 메서드
    def __init__(self, name='홍길동', age=20):
        self.name = name
        self.age = age
    
    # Person의 메서드
    def introduce(self):
        print(f'난 Person이야. 내 이름은 {self.name} 이고, 나이는 {self.age}살이야.')

케이스 1. 클래스 메서드 오버라이딩을 안 하는 경우

class Student(Person):
    """Sub Class"""

    def __init__(self):
        super().__init__()

다음과 같이 부모클래스 (Super Class)의 introduce()가 실행됨을 확인할 수 있습니다.

student = Student()
student.introduce()
난 Person이야. 내 이름은 홍길동 이고, 나이는 20살이야.

케이스 2. 클래스 메서드 오버라이딩을 한 경우

class Student(Person):
    """Sub Class"""

    def __init__(self):
        super().__init__()

    # 메서드 오버라이딩
    def introduce(self):
        print(f'난 Student야. 내 이름은 {self.name}이고, 나이는 비밀이야.')

메서드 오버라이딩을 하게 되면, 자식클래스 (Sub Class)에서 재정의 한 메서드가 호출 되게 됩니다. (부모클래스의 메서드는 무시됩니다.)

student = Student()
student.introduce()
난 Student야. 내 이름은 홍길동이고, 나이는 비밀이야.

케이스 3. 클래스 메서드 오버라이딩을 하고 그 안에서 super()를 호출한 경우

class Student(Person):
    """Sub Class"""

    def __init__(self):
        super().__init__()

    # 메서드 오버라이딩
    def introduce(self):
        # 부모의 메서드 호출
        super().introduce()
        print(f'난 Student야. 내 이름은 {self.name}이고, 나이는 비밀이야.')

만약, 부모클래스의 메서드를 호출하고 싶다면, super().introduce() 형식으로 호출 할 수 있습니다.

student = Student()
student.introduce()
난 Person이야. 내 이름은 홍길동 이고, 나이는 20살이야.
난 Student야. 내 이름은 홍길동이고, 나이는 비밀이야.

상속 구조 확인

  • 상속의 구조는 클래스명.mro()로 확인할 수 있습니다.
  • 구조는 상속 받은 순서대로 표시됩니다.
  • 모든 class는 object를 상속받기 때문에 항상 object가 마지막에 표기 됩니다.
Student.mro()
[__main__.Student, __main__.Person, object]


관련 글 더보기

- #02-파이썬(Python) 리스트(list)와 튜플(tuple)

- 넘파이(Numpy) 튜토리얼

- #01-파이썬(Python) 기본 자료구조

- python의 built-in function인 lambda, map, filter, 그리고 reduce에 대한 쉬운 이해와 사용법

- 뉴스 기사 와 RSS 피드에서 손쉽게 크롤링 하여 정보 수집하기

데이터 분석, 머신러닝, 딥러닝의 대중화를 꿈 꿉니다.