Python_Day.10

조현기·5일 전

Python_Day.10

클래스 복습 문제: 상속과 super(), 파일을 쓰는 클래스, 클래스 변수로 상태 관리. 파이썬 열흘 돌아보기.

파이썬 열 번째 날이다. 새 개념보다는 클래스 문제 세 개를 푸는 복습이었다. 상속(super()), 파일 입출력, 클래스 변수와 상태 검사를 한 문제씩 다시 짚었다.


1. 상속과 super(): 탈것

Vehicle을 상속해서 Car와 Bicycle을 만들고, 각자 move()를 오버라이딩한다.

class Vehicle:
    def __init__(self, name, speed):         # 변수에 처음 값을 대입 → 초기화
        self.name = name
        self.speed = speed

    def move(self):
        return f"{self.name}은 시속{self.speed} 이다"


class Car(Vehicle):
    def move(self):
        return super().move() + "소나타 자동차"


class Bicycle(Vehicle):
    def move(self):
        return super().move() + "자전거"


car = Car("소나타", 120)
bike = Bicycle("자전거", 20)
print(car.move())     # 소나타은 시속120 이다소나타 자동차
print(bike.move())    # 자전거은 시속20 이다자전거

흐름은 이렇다.

  1. Car("소나타", 120)을 만들면 Car에는 __init__이 없어서 부모 Vehicle의 __init__이 호출되고 name, speed가 초기화된다.
  2. car.move()는 Car의 move()가 실행된다. 그 안의 super().move()가 부모의 move()를 불러 "소나타은 시속120 이다"를 얻고, 뒤에 문자열을 덧붙인다.
  3. bike.move()도 같은 방식이다.

__init__을 따로 정의하지 않은 자식 클래스는 부모의 __init__을 그대로 쓴다. Day.5에서 자식이 __init__을 다시 정의할 때만 super().__init__()이 필요하다고 정리한 내용의 반대 경우다.

출력 문장 다듬기

위 결과를 보면 문장이 어색하다. 소나타은처럼 조사가 어긋나고, 이름(소나타)이 뒤에 또 붙었다. 이름은 이미 self.name에 들어 있으므로 덧붙일 것은 종류만 있으면 된다.

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

    def move(self):
        return f"{self.name}: 시속 {self.speed}km"


class Car(Vehicle):
    def move(self):
        return super().move() + " (자동차)"


class Bicycle(Vehicle):
    def move(self):
        return super().move() + " (자전거)"


for v in [Car("소나타", 120), Bicycle("따릉이", 20), Vehicle("탈것", 0)]:
    print(v.move())
# 소나타: 시속 120km (자동차)
# 따릉이: 시속 20km (자전거)
# 탈것: 시속 0km

for문에서 세 객체에 똑같이 v.move()를 호출했는데 각자의 move()가 실행됐다. Day.8의 다형성이다. 한글 문장에서 이름 + 은/는을 코드로 붙이면 받침에 따라 조사가 달라져서 어긋나기 쉽기 때문에, 위처럼 조사를 피하는 형식으로 바꿨다.

상속 관계는 코드로도 확인할 수 있다.

print(isinstance(Car("a", 1), Vehicle))    # True   (Car 객체는 Vehicle이기도 하다)
print(issubclass(Bicycle, Vehicle))        # True
print(Car.__mro__)
# (<class 'Car'>, <class 'Vehicle'>, <class 'object'>)

__mro__는 메소드를 찾는 순서다. Car → Vehicle → object 순으로 찾는다. Day.7의 "모든 클래스는 object를 상속한다"를 눈으로 볼 수 있다.


2. 파일을 쓰는 클래스: 일기장

날짜와 함께 일기를 파일에 쌓고, 한꺼번에 읽어 온다.

from datetime import date

class Diary:
    def __init__(self, filename="diary.txt"):         # 파일 이름의 기본값
        self.filename = filename

    def write(self, content):
        today = date.today()
        # a 모드: 기존 내용 뒤에 이어쓰기
        with open(self.filename, "a", encoding="utf-8") as f:
            f.write(f"{today} : {content}\n")

    # 파일에 저장한 문장들을 읽어 온다
    def read_all(self):
        try:
            with open(self.filename, "r", encoding="utf-8") as f:
                print(f.read())
        except FileNotFoundError:
            print("파일이없어 일기 못읽어옴")


diary = Diary()
diary.write("파이썬 복습")
diary.write("파이썬 오늘 정리함")
diary.read_all()
# 2026-10-03 : 파이썬 복습
# 2026-10-03 : 파이썬 오늘 정리함

(날짜는 실행한 날로 나온다.)

  • date.today()는 오늘 날짜 객체고, f-string에 넣으면 2026-09-18 형태의 문자열이 된다.
  • filename="diary.txt"처럼 매개변수 기본값을 두면 Diary()로 만들 때 파일 이름을 안 넘겨도 된다. 다른 파일을 쓰고 싶으면 Diary("work.txt")로 만들면 된다.
  • 'a' 모드는 이어쓰기라서 실행할 때마다 줄이 계속 쌓인다. 'w'였다면 매번 지워진다. (Day.4)
  • read_all은 파일이 없으면 FileNotFoundError를 except로 처리한다. 아직 한 번도 쓴 적 없는 Diary("nofile.txt").read_all()을 하면 메시지만 출력되고 프로그램은 계속된다.

Day.4의 파일 입출력, Day.6의 예외 처리, Day.5에서 본 time처럼 표준 모듈(datetime)을 가져다 쓰는 것을 한 클래스 안에 모은 문제다.


3. 클래스 변수로 상태 관리: 주문

주문의 상태가 정해진 값 중 하나일 때만 바꿀 수 있게 한다.

class Order:
    ORDER_STATUS = ["결제대기", "결제완료", "배송준비", "배송중", "배송완료"]    # 클래스 변수

    def __init__(self, order_id, customer):
        self.order_id = order_id
        self.customer = customer
        self.status = "결제대기"                 # 처음 상태

    def update_status(self, new_status):
        # new_status가 리스트 안에 없으면 "잘못된 상태"
        if new_status not in self.ORDER_STATUS:
            print("잘못된 상태")
            return
        self.status = new_status
        print(f"{self.order_id} 상태가 {new_status} 로 변경되었다")

    def show(self):
        print(f"주문번호 : {self.order_id}, 주문자 : {self.customer}, 상태 : {self.status}")


order1 = Order("A001", "Tom")
order2 = Order("A002", "Amy")

order1.update_status("결제완료")     # A001 상태가 결제완료 로 변경되었다
order1.update_status("배송준비")     # A001 상태가 배송준비 로 변경되었다
order2.update_status("배송중")       # A002 상태가 배송중 로 변경되었다
order2.update_status("문앞배송")     # 잘못된 상태

order1.show()    # 주문번호 : A001, 주문자 : Tom, 상태 : 배송준비
order2.show()    # 주문번호 : A002, 주문자 : Amy, 상태 : 배송중
  • ORDER_STATUS는 클래스 변수다. 주문 객체마다 따로 갖지 않고 모두가 공유한다. 이름을 대문자로 쓴 것은 "바꾸지 않는 값(상수)"이라는 관례다. 확인해 보면 Order.ORDER_STATUS is order1.ORDER_STATUS가 True로, 같은 리스트다. (Day.7의 클래스 변수)
  • not in으로 유효한 상태인지 검사하고, 아니면 return으로 바로 끝낸다. 이 경우 status는 바뀌지 않는다. 문앞배송은 목록에 없어서 변경되지 않았다.
  • 필기에서는 주문번호와 주문자를 input()으로 받았는데, 실행할 때마다 입력해야 해서 값을 직접 넣었다.

한 걸음 더: 단계 건너뛰기 막기

위 코드는 결제대기에서 바로 배송중으로 바꿔도 통과한다. (order2가 그렇게 바뀌었다) 상태에 순서가 있으니 리스트의 위치(index)를 비교하면 거꾸로 돌아가는 것은 막을 수 있다.

class Order2(Order):
    def update_status(self, new_status):
        if new_status not in self.ORDER_STATUS:
            print("잘못된 상태")
            return
        if self.ORDER_STATUS.index(new_status) < self.ORDER_STATUS.index(self.status):
            print("이전 단계로는 돌아갈 수 없다")
            return
        super().update_status(new_status)       # 나머지는 부모의 기능을 그대로


o = Order2("B1", "Kim")
o.update_status("배송중")       # B1 상태가 배송중 로 변경되었다
o.update_status("결제완료")     # 이전 단계로는 돌아갈 수 없다
o.update_status("문앞배송")     # 잘못된 상태

Day.5의 상속 + 오버라이딩 + super()로 부모는 건드리지 않고 검사만 덧붙였다. 반대로 건너뛰는 것(결제대기 → 배송중)까지 막을지는 업무 규칙에 달려 있어서, 여기서는 거꾸로 가는 것만 막았다.


4. 파이썬 열흘 돌아보기

열흘간 배운 것을 한 장으로 정리하면 이렇다.

Day주제
1자료형 9가지, 슬라이싱, 문자열 포맷(%, format, f-string), 리스트 메소드
2튜플, 딕셔너리, 집합, 참/거짓 판별, id/copy/is, if/elif, in, while
3for/range, 리스트 컴프리헨션, 함수, *args/**kwargs, 기본값 매개변수, input
4변수 스코프, lambda, 내장 함수, 파일 입출력, 클래스 기초
5상속, super(), 오버라이딩, 내장 함수와 time/random/pickle, 모듈
6패키지, 예외 처리와 사용자 정의 예외, 클로저, 데코레이터
7__str__, 이터레이터, 제너레이터, JSON, sys
8예외·상속 실습, 다형성, asyncio, threading, sys.argv
9정규 표현식(re), os 모듈
10클래스 복습: 상속, 파일, 클래스 변수

JavaScript(Day.1~10)에서 Promise와 async/await로 끝낸 비동기를 파이썬에서도 asyncio로 다시 만났다. 언어는 달라도 async/await로 기다리고 동시에 실행한다는 구조는 같았다.


마무리

  • 자식이 __init__을 정의하지 않으면 부모의 __init__이 그대로 쓰인다. 메소드를 오버라이딩하면서 부모 기능도 쓰려면 super().메소드()를 호출한다.
  • 같은 이름의 메소드를 호출해도 객체의 종류에 따라 다르게 동작한다. (다형성)
  • 파일에 쌓는 기능은 'a' 모드와 with, 파일이 없을 때는 FileNotFoundError 처리를 함께 쓴다.
  • 클래스 변수는 모든 객체가 공유한다. 정해진 값 목록을 두고 in/not in으로 검사하면 잘못된 값을 걸러 낼 수 있다.
  • 한글 문장을 코드로 조립할 때는 조사(은/는)가 어긋나지 않는지 출력해서 확인한다.

다음부터는 React와 Git이다.


Tags: Python 클래스 상속 super 다형성 파일입출력 클래스변수 복습 개발자 학습기록

profile
난 뭘까

0개의 댓글