TIL

TIL.33 클래스 상속(기반 클래스 및 파생클래스)

codermun 2020. 11. 10. 20:51
728x90
반응형

## 클래스 상속하기

## 클래스 상속(inheritance)

## 상속이란 무언가를 물려준다는 뜻으로 클래스 상속은 기반 클래스(base class)에서 상속을 받아

    새롭게 만드는 클래스를 파생 클래스(derived class)라 한다.

## 클래스 상속은 물려받은 기능을 유지한채로 다른 기능을 추가할 때 사용하는 기능이다.

 

## 보통 기반 클래스는 부모 클래스(parent class), 슈퍼 클래스(superclass)라고 부르고

## 파생 클래스는 자식 클래스(child class), 서브 클래스(subclass)라고도 부른다.

## 클래스 상속은 기반 클래스의 기능을 그대로 활용하면서 다른 기능을 추가할때 사용하는데,

# 그렇다면 왜 굳이 클래스를 새로 만들지 않고 클래스 상슥을 시켜 파생 클래스를 만드는걸까?

## 만약에 새로운 기능이 필요할때마다 클래스를 새로 만들면 중복되는 부분을 반복해서 만드는 경우가 생긴다.

# 예를들어

# 아래 그림에서 조류와 어류를 표현하고자 할때

# 생물이고, 동물이고, 척추동물이고 조류이다. 로 표현할때

# 생물이고, 동물이고, 척추동물이고 어류다. 처럼 불필요하게 반복을 만들어야하는데,

## 이럴때 상속을 이용하면 중복된 부분을 새로 만들지 않아 더 효율적이라 이해하자.

class Person:
    def greeting(self):
    print('안녕')

class Student (Person):
    def study (self):
    print('공부하기')


james = Student()
james.greeting() # 안녕 /
# Student 클래스에는 greeting메서드가 없지만 Person의 파생클래스로서 
# Person의 기능을 물려받는 상속을 해준 상태이므로
# Person에 있는 greeting메서드를 찾아가 정상 동작한다.

james.study() # 공부하기 
# Student 클래스에는 greeting 메서드가 없지만 
# Person 클래스를 상속받았으므로 greeting 메서드를 호출할 수 있다.

 

## 사람 클래스와로 학생 클래스 만들기

## 이처럼 클래스 상속은 기반 클래스의 기능을 유지하면서 새로운 기능을 추가 할 수 있다.

## 특히 클래스 상속은 연관되면서 동등한 기능일 때 사용한다.

## 학생은 사람이므로 연관된 개념이고, 학생은 사람에서 역할만 확장되었을뿐 동등한 개념이다.


## 클래스 상속 관계 확인하기.

# 클래스의 상속 관계를 확인하고 싶을 때는 issubclass 함수를 사용한다.

# 클래스가 기반 클래스의 파생 클래스인지 확인하여,

   기반 클래스의 파생 클래스가 맞으면 True, 아니면 False를 반환합니다.

# issubclass(파생클래스, 기반클래스)

class Person:
   pass

class Student(Person):
    pass

print(issubclass(Student, Person))
::
True

## 상속 관계와 포함 관계

## 상속 관계

class Person:
    def greeting(self):
    print('안녕하세요.')

class Student(Person):
    def study(self):
    print('공부하기')

# 앞에서 만든 Student 클래스는 Person 클래스를 상속받아 만들었다.

## 여기서, "학생은 사람이다"와 같이 학생 Student는 사람 Person이므로 같은 종류이다.

## 이처럼 상속은 명확하게 같은 종류이며 동등한 관계일 때 사용한다.

    즉, "학생은 사람이다."라고 했을 때 말이 되면 동등한 관계이다.

## 상속관계는 영어로 "is a" 관계라고 부른다. (Student is a Persion)

 

## 포함 관계

## 그렇다면 학생 클래스가 아닌 사람 목록을 관리하는 클래스를 만든다면 어떻게 해야 할까?

# 다음과 같이 리스트 속성에 Person 인스턴스를 넣어서 관리하면 된다.

class Person:
    def greeting(self):
    print('안녕하세요.')

class PersonList:
    def __init__(self):
    self.person_list = [] # 리스트 속성에 Person 인스턴스를 넣어서 관리

    def append_person(self, person): # 리스트 속성에 Person 인스턴스를 추가하는 함수
    self.person_list.append(person)

## 여기서는 상속을 사용하지 않고 속성에 인스턴스를 넣어서 관리하므로 PersonList가 Person을 포함하고 있다.

## 이러면 사람 목록 PersonList와 사람 Person은 동등한 관계가 아니라 포함 관계이다.

## 즉, "사람 목록은 사람을 가지고 있다."라고 말할 수 있으며, 포함 관계를 영어로 "has-a" 관계라고 부른다                  (PersonList has a Person).

 

### 정리하자면 같은 종류에 동등한 관계일 때는 상속을 사용하고,

      그 이외에는 속성에 인스턴스를 넣는 포함 방식을 사용하면 된다.


## 기반 클래스의 속성 사용하기

# 다음과 같이 Person 클래스에 hello 속성이 있고, Person 클래스를 상속받아 Student 클래스를 만든다.

# 그다음에 Student로 인스턴스를 만들고 hello 속성에 접근해보자.

class Person:
    def __init__(self):
    print('Person __init__')
    self.hello = '안녕하세요.'

class Student(Person):
    def __init__(self):
    print('Student __init__')
    self.school = '파이썬 코딩 도장'

james = Student()

print(james.school)

print(james.hello) # 기반 클래스의 속성을 출력하려고 하면 error가 발생함

## 기반 클래스의 기능을 물려받는다고 하였는데, 왜 기반 클래스의 hello 속성에 접근하지 못하는 걸까

## 이유는 기반 클래스의 __init__메서드가 호출되지 않았기 때문이다

     당연히 hello라는 속성이 만들어지지 못했기 때문이다.

## 즉, 기반 클래스 Person의 __init__ 메서드가 호출되지 않으면

     self.hello = '안녕하세요.'도 실행되지 않아 속성이 만들어지지 않아 호출할 수 없다.

 

## super()로 기반 클래스 초기화하기

## 위와 같이 기반 클래스의 __init__가 호출되지 않는 문제는

     super()를 사용해서 기반 클래스의 __init__ 메서드를 호출해주면 된다.

### 즉, super()를 사용하면 기반 클래스의 메서드를 사용하겠다는 의미로 이해하자.

# 다음과 같이 super() 뒤에 .(점)을 붙여서 메서드를 호출하는 방식이다.

# super().메서드()

class Person:
    def __init__(self):
        print('Person __init__')
        self.hello = '안녕하세요.'
 
class Student(Person):
    def __init__(self):
        print('Student __init__')
        super().__init__()     # super()로 기반 클래스의 __init__ 메서드 호출/ 위코드와 다른점
        self.school = '파이썬 코딩 도장'
 
james = Student()
print(james.school)
print(james.hello)

기반 클래스  Person 의 속성  hello 를 찾는 과정

# 실행을 해보면 기반 클래스 Person의 속성인 hello가 잘 출력된다.

## super().__init__()와 같이 기반 클래스 Person의 __init__ 메서드를 호출해주면 기반 클래스가 초기화되어서 속성이 만들어진다고 한다.

# (기반 클래스를 초기화된다는 말이 이해가 가질 않는다 - 질문)

# 실행 결과를 보면 'Student __init__'과 'Person __init__'이 모두 출력된다.

 

## 여기서 print를 각 사용하였을때의 결과이다.

james = Student()

print(james.school)
#
Student __init__
Person __init__
파이썬 코딩 도장

print(james.hello)
#
Student __init__
Person __init__
안녕하세요.



print(james.school)
print(james.hello)
#
Student __init__
Person __init__
파이썬 코딩 도장
안녕하세요.

## print를 2번 연속사용하면 Student와 Person의 메서드가 "한번씩만" 호출되어 결과가 나오는것으로 생각한다.

 

 

## 기반 클래스를 초기화하지 않아도 되는 경우 (super() 사용하지 않는방법)

class Person:
    def __init__(self):
    print('Person __init__')
    self.hello = '안녕하세요.'


class Student(Person):
    pass


james = Student()

print(james.hello)
#
Person __init__
안녕하세요.

## 이처럼 파생 클래스의 __init__메서드가 없다면

## 기반 클래스의 __init__이 자동으로 호출되므로 기반 클래스의 속성을 사용할 수 있다.

 

## 좀더 명확하게 super 사용하기

## super는 다음과 같이 파생 클래스와 self를 넣어서 현재 클래스가 어떤 클래스인지 명확하게 표시하는 방법도 있다.

# 물론 super()와 기능은 같다.

# super(파생클래스, self).메서드

class Student(Person):
    def __init__(self):
    print('Student __init__')
    super(Student, self).__init__() # super(파생클래스, self)로 기반 클래스의 메서드 호출
    self.school = '파이썬 코딩 도장'

 

728x90
반응형