객체지향 프로그래밍의 4가지 특징에 대해 알아본다.
두 클래스 사이의 부모-자식 관계를 설정하는 것
부모 클래스로부터 상속 받은 것을 자식 클래스의 특성에 맞게 변환하는 것
class Person:
def __init__(self, name, age):
self.name = name
self.age = age
class Man(Person):
def __init__(self, name, age, gender):
Person.__init__(self, name, age)
self.gender = gender
# super()
class Woman(Person):
def __init__(self, name, age, gender):
super().__init__(name, age)
self.gender = gender
super() 를 통해서 부모 클래스의 속성을 그래도 사용할 수 있다. 이 때, self 파라미터는 포함하지 않는다.
부모 클래스의 클래스 속성을 오버라이딩 하면 자식 클래스에 선언된 값을 사용할 수 있다. 이는 우선순위가 자식 클래스에 있기 때문이다. mro 함수를 써보면, 자식클래스가 가장 앞에 나온다. 따라서 오버라이딩 했을 때도 자식 클래스의 메소드나 속성 값을 우선적으로 찾는다는 것을 뜻한다.
두 개 이상의 부모 클래스로부터 상속 받는 경우를 말한다.
__init__
함수에서 super() 를 사용하는 경우, 어떤 부모 클래스인지가 불명확하다는 단점이 있으므로, 해당 부모 클래스로부터 직접 상속받는 방법을 사용하는 것이 좋다.
class Programmer:
def __init__(self, language):
self.language = language
def prefer_language(self):
print(f"He prefers to coding with {self.language}")
class Hobby:
def __init__(self, team):
self.team = team
def prefer_team(self):
print(f"He's favorite team is {self.team}")
class Person(Programmer, Hobby):
def __init__(self, language, team, age):
Programmer.__init__(self, language)
Hobby.__init__(self, team)
self.age = age
kwon = Person("python","Chelsea", 31)
kwon.prefer_language()
kwon.prefer_team()
> He prefers to coding with python
> He's favorite team is chelsea
다중 상속의 경우, 메서드를 오버라이딩하는 것이 혼란을 발생시키지 않는 방법이다. 그렇지 않는다면, 다중 상속 시에 상속 순서에 따라서 앞에 오는 부모 클래스가 우선 순위로 상속받는다.
한 변수가 A 클래스의 인스턴스와 B 클래스의 인스턴스 둘 다 될 수 있는 성질
다형성을 활용하는 데에 있어서 상속은 매우 유용한 성질이다. 모든 클래스가 공통의 성질이나 메서드를 가지는 것은 아니기 때문에 공통된 속성을 갖는 부모 클래스를 생성하는 것이 매우 효율적이다. 이 때, 부모 클래스는 메서드의 목록만을 나열한다. 이는 자식 클래스에서 메서드의 구현(오버라이딩)을 강제하기 위함이다.
다형성의 특성을 활용하기 위해 메서드를 나열하기 위한 부모 클래스를 생성할 때, 이를 추상 클래스라고 한다.
abc 라이브러리의 ABC(Abstract Bass Class), abstractmethod를 활용한다.
from abc import ABC, abstractmethod
class StudentBase(ABC):
@abstractmethod
def study(self) -> str:
pass
@abstractmethod
def go_to_school(self) -> str:
pass
class Student(StudentBase):
def study(self):
print('공부하기')
james = Student()
james.study()
추상 클래스는 인스턴스를 생성할 수 없다. 추상 클래스의 목적이 여러 자식 클래스가 공통으로 갖는 메서드나 속성을 사전에 나열하기 위함이므로 실질적인 기능은 없으며, 인스턴스로 불러올 이유도 없다.
++ type hinting 을 더해 추상 클래스의 결과 값에 대한 타입을 명시할 수 있다.
++ 추상화에 대해 알아둘 점
@property
@abstractmethod
def x(self):
"""도형의 x 좌표 getter 메소드"""
pass
@property
def x(self):
"""_x getter 메소드"""
return self._x
@x.setter
def x(self, value):
"""_x setter 메소드"""
self._x = value
위와 같이 앞서 배운 property decorator
를 사용하여 getter method를 설정하면, getter method를 오버라이딩하지 않을 경우 발생하는 에러를 피하기 위해서 반드시 지정해줘야 한다.
++ 추상 클래스의 다중 상속
추상 클래스의 다중 상속의 경우 메서드 이름이 중복되더라도 큰 문제가 없다. 그 이유는 추상 클래스를 상속받은 자식 클래스는 어차피 추상 메서드를 오버라이딩해야하기 때문에 겹쳐도 전혀 문제가 없다. 하지만 일반 메서드는 추상 클래스라고 할 지라도 일반 클래스와 같은 문제가 발생한다.
파이썬 함수에 대해 공부할 때 정리했던 위치별 인자의 종류(위치 인자, 가변 위치인자, 키워드 인자 등)가 달랐던 점이 바로 함수 혹은 메서드의 다형성이다. 여러 형태로 결과 값을 호출하는 것을 의미한다.
예를 들어, 어떠한 메서드에 대한 인자가 반드시 상위 클래스의 인스턴스여야 하는 경우에
if isinstance(instance, Class):
return pass
와 같이 인스턴스를 확인하는 절차를 거치는 것이 아니라, 일단 모든 인자를 받아들이고 나중에 try/except 문 을 활용해 처리하는 것이 EAFP 스타일이다.