객체지향 (상속)

6. 예제로 이해하는 객체지향 문법 (상속)

6.1. Class Inheritance (상속)

  • 추상화(abstraction): 여러 클래스에 중복되는 속성, 메서드를 하나의 기본 클래스로 작성하는 작업
  • 상속(inheritance): 기본 클래스의 공통 기능을 물려받고, 다른 부분만 추가 또는 변경하는 것
    • 이 때 기본 클래스는 부모 클래스(또는 상위 클래스), Parent, Super, Base class 라고 부름
    • 기본 클래스 기능을 물려받는 클래스는 자식 클래스(또는 하위 클래스), Child, Sub, Derived class 라고 부름

  • 코드 재사용이 가능, 공통 기능의 경우 기본 클래스 코드만 수정하면 된다는 장점
  • 부모 클래스가 둘 이상인 경우는 다중 상속 이라고 부름

예: 사각형, 삼각형, 원 클래스

  • 공통점과 차이점 찾아보기
    • 사각형: 사각형 이름, 사각형 색, 사각형 너비/높이, 사각형 넓이
    • 삼각형: 삼각형 이름, 삼각형 색, 삼각형 한 변 길이, 삼각형 넓이
    • 원: 원 이름, 원 색, 원 반지름, 원 넓이
  • 부모 클래스를 자식 클래스에 인자로 넣으면 상속이 됨
    - 다음 코드는 __init__(self, name, color) 메서드가 상속되고,
    - self.name과 self.color 도 __init__ 실행시 생성됨
    
class Figure:
    def __init__(self, name, color):
        self.__name = name
        self.__color = color
class Quadrangle(Figure):
    def set_area(self, width, height):
        self.__width = width 
        self.__height = height

    def get_info(self):
        print (self.__name, self.__color, self.__width * self.__height)
square = Quadrangle('dave', 'blue')
square.set_area(5, 5)
square.get_info()
class Quadrangle:
    def __init__(self, name, color):
        self.name = name
        self.color = color
        
    def set_area(self, width, height):
        self.__width = width 
        self.__height = height

    def get_info(self):
        print (self.name, self.color, self.__width * self.__height)
square = Quadrangle()
class Quadrangle(Figure):
    def set_area(self, width, height):
        self.__width = width 
        self.__height = height

    def get_info(self):
        print (self.name, self.color, self.__width * self.__height)
square = Quadrangle('dave', 'blue')
square.name
square = Quadrangle('dave', 'blue')
square.set_area(5, 5)
square.get_info()

보며 다시 한번 이해해보기

  • 상속 관계인 클래스 확인하기
    • 내장함수 issubclass(자식 클래스, 부모 클래스) 사용하기
# Quadrangle 클래스가 Figure 클래스의 자식 클래스인지 확인
issubclass(Quadrangle, Figure)
  • 클래스와 객체간의 관계 확인하기
    • 내장함수 isinstance(객체, 클래스) 사용하기
figure1 = Figure('figure1', 'black')
square = Quadrangle('square', 'red')
print(isinstance(figure1, Figure))
print(isinstance(square, Figure))
print(isinstance(figure1, Quadrangle))
print(isinstance(square, Quadrangle))
초간단 연습6
* 다음 세 코드가 실행된 후, 최종 값을 예상해보고, 왜 이와 같이 작성하였는지 이해하기(상속 개념 이해)
class Quadrangle(Figure):
    def set_area(self, width, height):
        self.__width = width 
        self.__height = height

    def get_info(self):
        print (self.name, self.color, self.__width * self.__height)
square = Quadrangle("square1", "black")
square.set_area(5, 5)
square.get_info()

또 다른 예: 사람과 학생

# 클래스 선언
class Person:
    def __init__(self, name):
        self.name = name
        
class Student(Person):
    def study(self):
        print (self.name + " studies hard")

class Employee(Person):
    def work(self):
        print (self.name + " works hard")
# 객체 생성
student1 = Student("Dave")
employee1 = Employee("David")
# 객체 실행
student1.study()
employee1.work()

6.2. Method Override (메서드 재정의)

  • 부모 클래스의 method를 자식 클래스에서 재정의(override)
  • 자식 클래스에서 부모 클래스 method를 재정의
  • 자식 클래스 객체에서는 재정의된 메소드가 호출됨
  • 자식 클래스에서 부모 클래스의 메서드와 이름만 동일하면 메서드 재정의가 가능함
    • C++/Java언어 등에서는 메서드와 인자도 동일해야 함
# 클래스 선언
class Person:
    def __init__(self, name):
        self.name = name

    def work(self):
        print (self.name + " works hard")        

class Student(Person):
    def work(self):
        print (self.name + " studies hard")
# 객체 생성
student1 = Student("Dave")
# 자식 클래스(Student)의 재정의된 work(self) 호출
student1.work()

Sub 클래스에 메서드를 더 추가할 수도 있죠!

class Person:
    def work(self):
        print('work hard')

class Student(Person):
    def work(self):
        print('Study hard')
        
    def go_to_school(self):
        print('Go to school')
p1 = Person()
s1 = Student()

p1.work()
#p1.go_to_school()

s1.work()
s1.go_to_school()
초간단 연습7
* Car class 만들기
- Car class
- attribute: 생성자에서 self.name 설정
- method: info(self) - 이름 출력

- Eletronic Car class
- attribute: 생성자에서 self.name 설정
- method overide: info(self) - 이름과 사용 연료(Eletronic) 출력

- Gasoline Car class
- attribute: 생성자에서 self.name 설정
- method overide: info(self) - 이름과 사용 연료(Gasoline) 출력
class Car:
    def __init__(self, name):
        self.name = name
    
    def get_info(self):
        print (self.name)

class ElecCar(Car):
    def get_info(self):
        print (self.name, 'Fuel: Eletronic')

        
class GasoCar(Car):
    def get_info(self):
        print (self.name, 'Fuel: Gasoline')

elec = ElecCar('dave')
gaso = GasoCar('david')
print (elec.get_info(), gaso.get_info())

6.3. 자식 클래스에서 부모 클래스 메서드 호출 (super 와 self)

super

  • 자식 클래스에서 부모 클래스의 method를 호출할 때 사용
    • super().부모 클래스의 method명
# 클래스 선언
class Person:
    def work(self):
        print('work hard')

class Student(Person):
    def work(self):
        print('Study hard')
        
    def parttime(self):
        super().work()
student1 = Student()
student1.work()
student1.parttime()

self

  • self는 현재의 객체를 나타냄
    • self.method명 또는 attribute명 으로 호출함
  • C++/C#, Java언어에서는 this 라는 키워드를 사용함
# 클래스 선언
class Person:
    def work(self):
        print('work hard')

class Student(Person):
    def work(self):
        print('Study hard')
        
    def parttime(self):
        super().work()

    def general(self):
        self.work() 
student1 = Student()
student1.work()
student1.parttime()
student1.general()

6.4. 자식 클래스에서 부모 클래스 메서드 확장하는 방법 (심화)

  • 부모 클래스 메서드 기능에 추가적인 기능이 필요할 경우
    • 부모 클래스 메서드는 그대로 이용하면서 자식 클래스 메서드에서 필요한 기능만 정의하는 기법
    • 해당 메서드에서 상속 받은 부모 클래스명.메서드명 을 호출하고, 필요한 기능 추가 정의
# 클래스 선언
class Person:
    def work(self):
        print('work hard')

class Student(Person):
    def work(self):
        Person.work(self) # 부모 클래스 메서드 호출
        print('Study hard')
student1 = Student()
student1.work()
class Figure:
    # 생성자(initializer)
    def __init__(self, width, height):
        # self.* : 인스턴스변수
        self.width = width
        self.height = height
        
    def print_info(self):
        print ('사이즈:', self.width, self.height)

class Rectangle(Figure):
    def print_info(self):
        Figure.print_info(self) # 부모 클래스 메서드 호출
        print ('너비:', self.width * self.height)
rectangle1 = Rectangle(2, 3)
rectangle1.print_info()

6.5. 추상 클래스 사용하기

  • 메서드 목록만 가진 클래스, 상속받는 클래스에서 해당 메서드 구현해야 함
  • 예: 게임에서 모든 캐릭터는 공격하기, 이동하기의 공통 기능을 가지고 있음
    • 공통 기능을 추상 클래스로 만들고, 각 세부 기능은 해당 메서드에서 구현하는 것

사용법

  • abc 라이브러리를 가져옴 (from abc import * )
  • 클래스 선언시 ( ) 괄호 안에 metaclass=ABCMeta 를 지정
  • 해당 클래스에서 메서드 선언시 상단에 @abstractmethod 를 붙여줘야 함
# 추상 클래스 선언하기
from abc import *

class Character(metaclass=ABCMeta):
    @abstractmethod
    def attack(self):
        pass
    
    @abstractmethod
    def move(self):
        pass
# 추상 클래스 상속하기
class Elf(Character):
    def attack(self):
        print ("practice the black art")
    
    def move(self):
        print ("fly")
        
class Human(Character):
    def attack(self):
        print ("plunge a knife")
    
    def move(self):
        print ("run")
# 객체 실행하기

elf1 = Elf()
human1 = Human()

elf1.attack()
elf1.move()
human1.attack()
human1.move()

다른 예

# 추상 클래스 선언하기
from abc import *

class Character(metaclass=ABCMeta):
    def __init__(self, hp):
        self.hp = hp
        
    def get_hp(self):
        return self.hp
    
    @abstractmethod
    def attack(self):
        pass
    
    @abstractmethod
    def move(self):
        pass
character = Character(10)  # 추상 클래스는 객체로 만들 수 없어요!!! 추상 메서드를 다 채워야 합니다.
# 추상 클래스 상속하기
class Elf(Character):
    def attack(self):
        print ("practice the black art")
    
    def move(self):
        print ("fly")
character = Elf(10)
print(character.get_hp())
초간단 연습8
* Car class 추상 클래스 만들기
- attribute: 자동차 이름 - method: info(self) - 이름 출력
- abstract method: fuel(self)

- Eletronic Car class
- attribute: 생성자에서 self.name 설정
- method: info(self) - 이름 출력
- method: fuel(self) - 사용 연료(Eletronic) 출력
# 추상 클래스 선언하기
from abc import *

class Car(metaclass=ABCMeta):
    def __init__(self, name):
        self.name = name
    
    def get_info(self):
        return self.name 
    
    @abstractmethod
    def fuel(self):
        pass

class ElecCar(Car):
    def fuel(self):
        return 'Electronic'

elec_car = ElecCar('dave')
print (elec_car.get_info(), elec_car.fuel())