PY_class | 상속(Inheritance)과 다형성(polymorphism)

Stellar·2023년 11월 6일
0

Python

목록 보기
32/36
post-custom-banner

상속(Inheritance)과 다형성(polymorphism)

1. 상속(Inheritance)

상속은 객체지향 언어에만 있는 개념이다.
상위 -> 하위 -> 자식 클래스 내에 있는 변수, 함수를 상속하여 연동해서 사용 가능하다.
상속은 클래스 간의 is-a 관계이다. ex. 푸들은 강아지다. 꽃은 식물이다.처럼 상위 클래스에 해당 됨.
자식 클래스와 부모 클래스가 갖는 공통 변수와 함수는 부모 클래스에 정의를 해둔다.

✔️ 기본 문법

  • 자식 클래스 또는 서브 클래스라 칭한다.
  • 부모 클래스 또는 슈퍼 클래스라 칭한다.
class 부모클래스 :
	생성자
	메소드

# 자식 클래스에서 부모 클래스 이름을 인수로 넣어줘 변수 및 함수들을 사용할 수 있다.
class 자식 클래스 (부모 클래스명) :
	#부모 클래스의 인수 작성
	def __init__(self, make, model, color, price, payload) : 
		#부모클래스에 대한 생성자 작성 (상속되기 때문에 다시 정의할 필요 X)
        		super().__init__(make, model, color, price) 
		#자식클래스에서 정의한 인수는 정의해줘야 함.
       		self.payload = payload 
	메소드

class 자식 클래스 (부모 클래스명) :
	생성자
	메소드

✔️ 상속 클래스 예제.

Q. 근데 아래 예제에서 자식클래스를 호출할 때 인수에 100, True란 값을 넘겨 줬잖아? 부모클래스는 인수가 1갠데 넘기는 걸 2개로 넘겨도 되는거임?

A. obj에 자식클래스를 정의한거라 자식클래스는 인수를 2개 받으니까 인수가 2개여도 되는거임.

#Super Class
class Car :
    def __init__(self, speed) :
        self.speed = speed

    def setSpeed(self, speed) :
        self.speed = speed

    def getDesc(self) :
        return '차량 = ({})'.format(self.speed)

#Sub Class
class SportsCar(Car) :
    def __init__(self, speed, turbo) : #전달 받는 인수가 2개
        super().__init__(speed)
        self.turbo = turbo

    def setTurbo(self, turbo) :
        self.turbo = turbo

obj = SportsCar(100, True)
print(obj.getDesc())
obj.setTurbo(False)

상속 클래스 예제2.

#Inheritance
class Human :
    def __init__(self, age, name) :
        self.age = age
        self.name = name

    def intro(self) :
        print('{}살, {}입니다.'.format(self.age, self.name))

class Student(Human) :
    def __init__(self, age, name, st_code) :
        super().__init__(age, name)
        self.st_code = st_code

    #부모클래스에서 상속 받음
    def intro(self) :
        super().intro() #상속 받기 위한 코드
        print('학번 : {}'.format(self.st_code)) #부모 클래스에서 상속받은 코드에 추가한 코드
        
    def study(self) :
        print('하늘 천, 땅 지, 검을 현, 누를 황')

#내가 생각한 코드는 학번도 study에 들어가야한다 싶어 함께 묶음.
    #def study(self) :
        #print('학번 : {}'.format(self.st_code))
        #print('하늘 천, 땅 지, 검을 현, 누를 황')
        
kim = Human(29, '김상현')
kim.intro()
lee = Student(40, '이지수', 930011)
lee.intro()
lee.study()

class 메소드 종류

set 설정자 메소드
get 접근자 메소드

✔️ class의 매개변수

Q. class에 인수가 있는건 뭐고? 없는건 뭐지?

A. 클래스에 인수가 들어가는 경우는 자식 클래스가 부모 클래스를 상속받을 때 사용한다.
아래 코드는?

iter(), 반복되는 객체

class MyCounter(object) :
    #생성자 메소드를 정의.
    def __init__(self, low, high) :
        self.current = low
        self.high = high

    #이터레이터 객체로서 자신을 반환.
    def __iter__(self) :
        return self

    def __next__(self) :
        #current가 high 보다 크면 StopIteration 예외 발생.
        #current가 high 보다 작으면 다음 값을 반환.
        if self.current > self.high :
            raise StopIteration
        else :
            self.current += 1
            return self.current -1

c = MyCounter(1, 10)
for i in c :
    print(i, end=' ')
print()

list = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
for i in list :
    print(i, end=' ')

✔️ 클래스 생성자(초기화)

클래스를 호출할 인수를 작성하는데 그 인수는 생성자를 통해 초기화가 되어 정의되고 정의된 값을 클래스 내 함수에서 변수로 사용된다.

✔️ over riding

덮어쓴다. 부모클래스에서 정의한 함수를 자식클래스에서 재정의할 경우 자식 클래스의 함수가 우선 시 된다.

over riding 예제.

#Overriding
class Animal :
    def __init__(self, name=' ') :
        self.name = name

    def eat(self):
        print('동물이 먹고 있습니다.')

class Dog(Animal) :
    def __init__(self) :
        super().__init__()

    def eat(self) :
        print('강아지가 먹고 있습니다.')

d = Dog()
d.eat()

=============================== RESTART: C:\Users\GIEC\Desktop\기초문법\1103\test.py ==============================
강아지가 먹고 있습니다.

#자식 클래스에서 eat 메소드를 재정의 했으므로 '강아지가 먹고 있습니다.'가 출력된다.

over riding 예제2. 오버라이딩을 잘 나타낸 예제.

원의 면적과 둘레를 구해야 하는데 원 클래스의 내용과 사각형 클래스의 내용은 같다. 하지만 둘 다 다른 클래스로 부모 클래스만 공유한다. 따라서 원의 클래스와 사각형의 클래스는 똑같은 이름의 함수를 사용해도 내용을 달리하여 다른 결과를 도출해낼 수 있다.

import math

class Shape :
    def __init__(self, x, y) :
        self.x = x
        self.y = y

    def area(self) :
        print('계산할 수 없음!')

    def perimeter(self) :
        print('계산할 수 없음!')

class Rectangle(Shape) :
    def __init__(self, x, y, w, h) :
        super().__init__(x, y)
        self.w = w
        self.h = h

    def area(self) :
        return self.w * self.h

    def perimeter(self) :
        return 2 * (self.w + self.h)

#부모클래스에서 필요없는 값은 받을 필요가 없다.
class Circle(Shape) : #부모클래스를 상속 하지만
    def __init__(self, r) : #내가 정의할 값만 가져옴.
        self.r = r

    def area(self) :
        return math.pi * self.r**2

    def perimeter(self) :
        return 2 * math.pi * self.r
    
r = Rectangle(0, 0, 100, 200)
c = Circle(10)

print('사각형의 면적', r.area())
print('사각형의 둘레', r.perimeter())
print('원의 면적', c.area())
print('원의 둘레', c.perimeter())

✔️ 클래스 상속 종합 예제

종합 예제. 학생과 선생님의 정보를 출력하자.

class Person :
    def __init__(self, name, number) :
        self.name = name
        self.number = number

class Student(Person) :
    UNDERGRADUATE = 0
    POSTGRADUATE = 1
    
    def __init__(self, name, number, studentType ) :
        super().__init__(name, number)
        self.studentType = studentType
        self.gpa = 0

        self.classes =[ ]

    def enrollCourse(self, course) :
        self.classes.append(course)

    def __str__(self) :
        return '''이름 = {}
주민번호 = {}
학생타입 = {}
수강과목 = {}
평점 = {}'''.format(self.name, self.number, self.studentType, self.classes, self.gpa)

class Teacher(Person) :
    def __init__(self, name, number ) :
        super().__init__(name, number)
        self.classes =[ ]
        self.pay = 3000000

    def assignTeaching(self, course) :
        self.classes.append(course)

    def __str__(self) :
        return '''이름 = {}
주민번호 = {}
강의과목 = {}
월급 = {}'''.format(self.name, self.number, self.classes, self.pay)

hong = Student('홍길동', '12345678', Student.UNDERGRADUATE)
hong.enrollCourse('자료구조')
print(hong)
print('=' * 20)
kim = Teacher('김철수', '87654321')
kim.assignTeaching('Python')
print(kim)

종합 예제2. 은행 계좌.

#종합 예제 2
class BankAccount :
    def __init__(self, name, number, balance) :
        self.name = name
        self.number = number
        self.balance = balance

    def withdraw(self, amount) :
        self.balance -= amount
        return self.balance

    def deposit(self, amount) :
        self.balance += amount
        return self.balance

class SavingsAccount(BankAccount) :
    def __init__(self, name, number, balance, interest_rate) :
        super().__init__(name, number, balance)
        self.interest_rate = interest_rate
        
    def set_interest_rate(self, interest_rate) :
        self.interest_rate = interest_rate
    
    def get_interest_rate(self) :
        return self.interest_rate

    def add_interest(self) :        #예금에 이자를 더 함.
        self.balance += self.balance * self.interest_rate

class CheckAccount(BankAccount) :
    def __init__(self, name, number, balance) :
        super().__init__(name, number, balance)
        self.withdraw_charge = 10000        #수표 수수료 발행.

    def withdraw(self, amount) :
        return BankAccount.withdraw(self, amount + self.withdraw_charge)
    
a1 = SavingsAccount('홍길동', 123456, 10000, 0.05)
a1.add_interest()
a1.deposit(1000) #SavingsAccount에서 deposit 함수를 정의하지 않았지만 사용 가능.
print('저축 예금의 잔액 =', a1.balance)
print('=' * 20)
a2 = CheckAccount('김철수', 1234567, 2000000)
a2.withdraw(100000) 
print('저축 예금의 잔액 =', a2.balance)

종합 예제3. 매니저 월급 구하기.

자식클래스에서 부모클래스의 함수를 사용하려면 super().메소드() 형식으로 불러와야 한다.

class Employee :
    def __init__(self, name, salary) :
        self.name = name
        self.salary = salary

    def getSalary(self) :
        return salary

class Manager(Employee) :
    def __init__(self, name, salary, bonus) :
        super().__init__(name, salary)
        self.bonus = bonus
    
    def getSalary(self) :
        salary = super().getSalary()
        return salary + self.bonus
        
    def __str__(self) :
        return '이름 : {}, 월급 : {}, 보너스 : {}'.format(self.name, self.salary, self.bonus)

kim = Manager('김철수', 2000000, 1000000)
print(kim)

Q. 클래스에서의 변수를 인스턴스 변수라고 하는건가? 인스턴스 메소드? 인스턴스는 클래스를 의미하는가?

A. 클래스 외부에서 변수에 클래스를 정의하면 클래스가 정의된 변수를 인스턴스라 함.

Q. 클래스의 캡슐화는 보안인건 알겠는데 왜 변수 앞에 _를 하나만 붙였지??

https://blog.naver.com/codeitofficial/221684462326


2.다형성(polymorphism)

동일한 코드로 다양한 객체를 처리할 수 있는 기법.
리스트를 만들어 for문에 사용할 수 있다.

✔️ 다형성 예제.

다형성 예제. 동물에 맞는 울음소리.

class Animal :
    def __init__(self, name) :
        self.name = name

    def speak(self) :
        return '알수 없음.'

class Dog(Animal) :
    #def __init__(self, name) : #부모 클래스와 인수가 동일하면 생략 가능
        #super().__init__(name) #생략 가능.
        
    def speak(self) :
        return '멍멍!'

class Cat(Animal) :
    #def __init__(self, name) : 
        #super().__init__(name) 
        
    def speak(self) :
        return '야옹!'

animalList = [Dog('dog1'), Dog('dog2'), Cat('cat1')]

for a in animalList :
    print(a.name + ' : ' + a.speak())

✔️ raise NotImplementedError()

자식 클래스에서 함수를 재정의 하지 않았을 경우 부모 클래스에서 처리할 코드가 아니라서 개발자가 알 수 있도록 일부러 에러를 발생 시키고 자식 클래스에서 코드를 재정의 하도록 유도한다.
init도 작성할 필요가 없으면 pass를 작성해도 된다.

다형성 예제2.

class Vehicle :
    def __init__(self, name) :
        self.name = name

    def drive(self) :
        raise NotImplementedError('이것은 추상 메소드입니다.') #자식 클래스에서 함수를 재정의 하지 않았을 경우 에러를 낸다.

    def stop(self) :
        raise NotImplementedError('이것은 추상 메소드입니다.')

class Car(Vehicle) :
    def drive(self) :
        return '승용차를 운전합니다.'

    def stop(self) :
        return '승용차를 정지합니다.'

class Truck(Vehicle) :
    def drive(self) :
        return '트럭을 운전합니다.'

    def stop(self) :
        return '트럭을 정지합니다.'

cars = [Truck('truck1'), Truck('truck2'), Car('car1')]

for car in cars :
    print(car.name + ' : ' + car.drive())

✔️ 클래스의 메소드

repr
init

✔️ 클래스 관계

is-a
has-a

post-custom-banner

0개의 댓글