2.22. 상속¶
상속은 클래스가 다른 클래스를 확장 하게 해줍니다 – 그것의 모든 메서드와 속성으로 시작한 다음, 다른 부분을 추가하거나 재정의합니다. 원본을 기반(base) (또는 부모(parent))이라 부르고, 확장을 서브클래스(subclass) 라고 부릅니다.
Square 는 Shape 의 모든 메서드를 재사용하고 특수화가 필요한 것들만 재정의합니다.¶
2.22.1. 서브클래스 정의하기¶
기반 클래스는 class 줄의 괄호 안에 들어갑니다:
class Shape:
def __init__(self, name):
self.name = name
def describe(self):
return self.name + " with area " + str(self.area())
def area(self):
return 0
class Square(Shape):
def __init__(self, side):
super().__init__("square")
self.side = side
def area(self):
return self.side * self.side
s = Square(4)
print(s.describe())
출력:
square with area 16
Square 는 Shape 로부터 describe 를 변경 없이 상속하고 실제 값을 반환하도록 area 를 재정의합니다. 기반의 describe 는 self.area() 를 호출하기 때문에 여전히 작동합니다 – 이는 실행 시점에 재정의로 해석됩니다.
2.22.2. super()¶
super() 는 메서드가 메서드의 기반 클래스 버전을 호출하게 해주는 프록시를 반환합니다. 가장 흔한 용도는 서브클래스의 __init__ 에서 기반의 __init__ 를 호출하여 기반이 자신의 속성을 초기화하도록 하는 것입니다:
class Square(Shape):
def __init__(self, side):
super().__init__("square") # run Shape.__init__
self.side = side
super().__init__ 를 잊는 것은 흔한 버그의 원인입니다: 기반이 설정했을 속성이 결코 설정되지 않고, 그것에 의존하는 상속된 메서드들이 나중에 충돌합니다.
2.22.2.1. 암묵적 기반 클래스¶
모든 클래스는 Python의 타입 계층 구조의 루트인 object 를 암묵적으로 상속합니다. __repr__, __eq__ 같은 메서드들과 속성 접근 뒤의 모든 메커니즘은 클래스가 기반을 선언하지 않더라도 거기서 옵니다. class Foo(object): 를 명시적으로 작성하는 것과 class Foo: 를 작성하는 것은 현대 Python에서 동등합니다. 후자가 관례적인 형태입니다.
2.22.3. 상속이 도움이 될 때 – 그리고 그렇지 않을 때¶
한 클래스가 진정으로 다른 클래스의 더 구체적인 종류 이고 대부분의 동작을 공유할 때 상속을 사용하세요. 고전적인 판별 기준은 “is-a”(이다) 검사입니다: Square 는 Shape 이다.
두 클래스가 단지 몇 개의 헬퍼를 공유할 뿐이라면 상속은 과합니다. 대신 합성(composition) 을 사용하세요: 한 클래스가 다른 클래스의 인스턴스를 속성으로 보유하고 그 속성을 통해 사용하게 하세요. 형태는 “is-a”가 아니라 “has-a”(가지고 있다)입니다:
class Logger:
def log(self, msg):
print("[log]", msg)
# inheritance: Worker IS-A Logger
class WorkerInherits(Logger):
def run(self):
self.log("starting")
# composition: Worker HAS-A Logger
class WorkerComposes:
def __init__(self):
self.logger = Logger()
def run(self):
self.logger.log("starting")
둘 다 작동합니다. 합성 방식이 보통 더 나은데, 그 이유는:
인터페이스를 작게 유지합니다.
WorkerComposes는run과logger만 노출합니다.WorkerInherits는log도 노출합니다 – 의도했든 안 했든 호출자는worker.log(...)를 직접 작성할 수 있습니다.생명주기를 분리합니다. logger는
Worker클래스를 건드리지 않고도 교체되거나, 워커들 간에 공유되거나, 워커마다 다르게 구성될 수 있습니다. 서브클래스는 그 기반에 단단히 묶여 있습니다.메서드 이름 충돌을 피합니다. 둘 다
run을 정의하는 두 기반 클래스는 클래스가 둘 다로부터 상속하려 할 때 충돌합니다. 두 속성은 결코 충돌하지 않습니다.
실용적인 경험 법칙:
서브클래스가 정말로 기반의 한 종류 이고 기반의 모든 메서드가 그것에도 의미가 있을 때 상속을 사용하세요 –
Square는Shape이다;MemoryError는Exception이다.그 외 모든 경우에는 합성을 사용하세요 – 다른 클래스의 정체성이 아니라 동작 만 원할 때입니다. 대부분의 실제 코드는 상속보다 합성을 훨씬 더 많이 사용합니다.