객체지향 프로그래밍을 할 때 추상클래스라고 나오는 것이 있다.
아직 활용을 완벽하게 이해하지 못하였지만, 간단하게 정리해 보았다.
추상클래스
1. 하나 이상의 정의되지 않은 메소드, 즉 추상메소드가(abstract method)가 있는 클래스를 의미한다.
2. 이를 상속받아 사용하는 자식클래스에서 추상메소드를 오버라이드(override)해서 사용하게 한다.
3. 추상클래스에 공통적인 부분을 모두 정의하고, 자식클래스에서 필요한 부분을 재정의해서 사용하는 것이 목표.
바로 이해가 되지는 않는다.
간단한 코드를 예시로 들어보자.
class 로봇추상클래스:
"""이 클래스는 로봇의 추상클래스 입니다"""
def __init__(self, 이름, 나이):
self.이름 = 이름
self.나이 = 나이
def __str__(self):
return f"SYSTEM - {type(self).__name__}: {self.이름}은 제작된 지 {self.나이}일이 지났습니다."
def 이름바꾸기(self, 이름):
self.이름 = 이름
def 나이바꾸기(self, 나이):
self.나이 = 나이
def 말하기(self):
pass
def 걷기(self, 방향=None):
pass
위 "클래스: 로봇추상클래스" 라고 정의한 클래스는 생성자를 제외하고 5가지 메소드를 가지고 있지만, 2개의 메소드는 내용이 정의되어 있지 않다.
이러한 정의되지 않은 메소드 "함수: 말하기"와 "함수: 걷기"를 추상메소드라고 하고 이러한 추상메소드를 가진 클래스를 추상클래스라고 한다.
추상메소드 "함수: 말하기"와 "함수: 걷기"는 추상클래스를 상속받는 자식클래스에서 재정의 해서 사용해야 한다.
아래 걷기는 못하고 말하기만 하는 말하는로봇 클래스를 작성해보자.
class 말하는로봇(로봇추상클래스):
"""이 클래스는 말하는로봇 객체를 생성하는 클래스 입니다.
말하는 로봇은 말을 할 수 있습니다.
"""
def 말하기(self):
print("나는 말하는 로봇이다. 삐릭삐릭")
print(f"나의 이름은 {self.이름}이고, 삐릭, 나이는 {self.나이}일 됐다.")
# 말하는 로봇 생성 (이름은 말하는로봇v1이며 3일차 로봇이다.)
talk_robot = 말하는로봇("말하는로봇v1",3)
talk_robot.말하기()
print(talk_robot)
# 하루가 지나서 나이가 4일이 되었다.
talk_robot.나이바꾸기(talk_robot.나이 + 1)
print(talk_robot)
-결과-
나는 말하는 로봇이다. 삐릭삐릭
나의 이름은 '말하는로봇v1'이고, 삐릭, 나이는 3일 됐다.
SYSTEM - 말하는로봇: 말하는로봇v1은 제작된 지 3일이 지났습니다.
SYSTEM - 말하는로봇: 말하는로봇v1은 제작된 지 4일이 지났습니다.
위처럼 말하기만 정의를 해주면 부모클래스인 "클래스: 로봇추상클래스"에서 정의한 내용을 사용할 수 있다.
이제 걷기만 하는 로봇을 생성해보자. 말하는 기능은 없다.
class 걷는로봇(로봇추상클래스):
"""이 클래스는 걷는로봇 객체를 생성하는 클래스 입니다.
걷는로봇은 걸을 수 있습니다.
"""
def 걷기(self, 방향=None):
print(f"SYSTEM - {self.이름}은 {방향} 방향으로 걷고 있습니다.")
# 걷는로봇 생성 (이름은 걷는로봇v3이며 135일차 로봇이다.)
walk_robot = 걷는로봇("걷는로봇v3",135)
walk_robot.걷기("남쪽")
print(walk_robot)
# 해외시장에 홍보하기 위해 이름을 영어로 바꾸어 주었다.
walk_robot.이름바꾸기("walking_robot_v3")
print(walk_robot)
-결과-
SYSTEM - 걷는로봇v3은 남쪽 방향으로 걷고 있습니다.
SYSTEM - 걷는로봇: 걷는로봇v3은 제작된 지 135일이 지났습니다.
SYSTEM - 걷는로봇: walking_robot_v3은 제작된 지 135일이 지났습니다.
역시 잘 되는 것을 확인할 수 있다.
그렇다면 걷는로봇에게 말하기를, 말하는로봇에게 걷기를 시키면 어떻게 될까?
talk_robot.걷기()
walk_robot.말하기()
어떠한 에러도 어떠한 결과도 나오지 않는다.
왜나하면 부모클래스로부터 pass라는 실행을 하는 메소드를 상속받았기 때문이다.
추상클래스 말고 그냥 클래스를 써도 같은 결과가 아니냐고 할 수 있다. 나도 그랬다.
내가 생각하는 장점은 같은 부모클래스를 둔 서로 다른 클래스를 같이 작업시킬 때 유용한 것 같다.
하나의 클래스를 더 만들어 보자. 이번에는 말하고걷는로봇이다.
class 말하고걷는로봇(로봇추상클래스):
"""이 클래스는 말하고걷는로봇 객체를 생성하는 클래스 입니다.
말하고걷는로봇은 말하기, 걷기를 수행합니다.
"""
def 말하기(self):
print(f"왜 나에게 말하기를 시키는가, 휴먼? 말 걸지 마라.")
def 걷기(self, 방향=None):
print(f"내가 왜 걸어야 하지, 휴먼? 난 걷고 싶을 때만 걷는다.")
walk_talk_robot = 말하고걷는로봇("건방진로봇",1)
walk_talk_robot.말하기()
walk_talk_robot.걷기()
print(walk_talk_robot)
-결과-
왜 나에게 말하기를 시키는가, 휴먼? 말 걸지 마라.
내가 왜 걸어야 하지, 휴먼? 난 걷고 싶을 때만 걷는다.
SYSTEM - 말하고걷는로봇: 건방진로봇은 제작된 지 1일이 지났습니다.
이렇게 생성한 세가지 객체에 동시에 작업을 시켜보자.
robot_list = [walk_robot,talk_robot,walk_talk_robot]
for robot in robot_list:
print(robot)
robot.걷기("남쪽")
robot.말하기()
print()
-결과-
SYSTEM - 걷는로봇: walking_robot_v3은 제작된 지 135일이 지났습니다.
SYSTEM - walking_robot_v3은 남쪽 방향으로 걷고 있습니다.
SYSTEM - 말하는로봇: 말하는로봇v1은 제작된 지 4일이 지났습니다.
나는 말하는 로봇이다. 삐릭삐릭 나의 이름은 '말하는로봇v1'이고, 삐릭, 나이는 4일 됐다.
SYSTEM - 말하고걷는로봇: 건방진로봇은 제작된 지 1일이 지났습니다.
내가 왜 걸어야 하지, 휴먼? 난 걷고 싶을 때만 걷는다.
왜 나에게 말하기를 시키는가, 휴먼? 말 걸지 마라.
이런 식으로 따로 메소드를 정의하지 않더라도 한 번의 작업 명령으로 에러 없이 모두 동작시킬 수 있다.
또, 자식클래스를 설계할 때 인자arguments를 어떤 형태로 받아야 하는지 등에 대해 알 수 있다.
지금 당장은 추상클래스의 이러한 장점 이 외에는 떠오르지 않는다.
좀 더 공부를 한 후 이 포스트를 수정하겠다.
다음 포스트는 인터페이스(interface)이다.
내가 제대로 이해한 것이 맞다면, 추상클래스의 강화판이다.
무엇이냐면, 추상클래스는 추상메소드를 재정의 해서 사용하라고 알려 주는 역할이라면 인터페이스는 추상메소드를 반드시 재정의 해서 사용하라고 지시한다. 그렇지 않으면 에러를 발생시킨다.
파이썬에서는 내장된 interface 기능이 없기 때문에 abc라는 모듈을 임포트한다.
abc는 abstract base class의 약자이다. 해당 기능을 다음 포스트에서 알아보자.
'파이썬' 카테고리의 다른 글
type - Class에 대한 이해 1(Python) (0) | 2023.11.25 |
---|---|
인터페이스 (interface) in python (0) | 2021.11.13 |
데코레이터 (Decorator) in python (0) | 2021.11.02 |