객체지향적 Python

 

Python의 특징을 살리면서 객체지향적으로 코드 설계를 하기 위한 포스트

Class(클래스)

attribute(속성)

attribute란 클래스 내부에 선언된 메소드, 변수를 의미한다

  • 파이썬의 속성은 2가지로 나뉜다
    1. class attribute
        class Person:
      
       name = 0                # class attribute
      
       def __init__(self):     # instance attribute
           self.name = "hojun" # instance attribute
      
      • name 변수는 self를 참조하여 선언된것이 아니여서 class attribute이다.
      • name 변수는 인스턴스화(생성자 호출)없이 접근 가능하다. 즉 정적변수(static variable)이다.
        • static은 객체마다 달라지지 않고, 객체 생성을 따로 하지 않아도 라는 의미를 담고 있다
          • 이러한 개념에따라 객체 생성을 따로 하지 않아도 호출 가능한 메소드가 2가지 존재한다

            1. @classmethod
              • 인자로 class 자체를 받음. self와 달리 cls로 하는것이 국룰
              • 팩토리 메서드로 활용하여 다양한 포맷을 입력으로 받아 인스턴스화 하는데 쓰일 수 있다
                class User:
                    def __init__(self, email, password):
                        self.email = email
                        self.password = password
                
                    @classmethod
                    def fromTuple(cls, tup):
                        return cls(tup[0], tup[1])
                
                    @classmethod
                    def fromDictionary(cls, dic):
                        return cls(dic["email"], dic["password"])
                
            2. @staticmethod
              • 인자로 받는것이 따로 없음
              • 클래스 속성엔 접근 가능하지만, 인스턴스 속성에는 접근 불가
              • 유틸리티 메서드를 구현하는데 많이 사용되며 아래 코드를 참고하면 이해가 더 쉽다

                class StringUtils:
                    @staticmethod
                    def toCamelcase(text):
                        words = iter(text.split("_"))
                        return next(words) + "".join(i.title() for i in words)
                
                    @staticmethod
                    def toSnakecase(text):
                        letters = ["_" + i.lower() if i.isupper() else i for i in text]
                        return "".join(letters).lstrip("_")
                
              • 위 두 메서드는 매개변수로 넘어온 문자열에만 의존하는 순수한(pure) 함수여서 클래스의 일부로 선언할 필요는 없지만 이렇게 비슷한 류의 여러 유틸리티 메서드를 하나의 클래스에 묶어두고 싶을 때 사용 가능
    2. instance attribute
      • self 키워드에 집중할 것. instance attribute의 시작은 self
      • 파이썬의 메소드에 어떠한 decorator도 붙지 않았다면 instance 메소드로 간주한다. 즉 위의 init 함수는 instance attribute이
      • 인스턴스를 의미하는 self를 참조해서 선언된 변수는 instance attribute이다.
class Daeheeyun:

    class_value = 0

    def __init__(self):
        self.instance_value = 0

    def set_class_value(self):
        Daeheeyun.class_value = 10

    def set_instance_value(self):
        self.class_value = 20

print(Daeheeyun.class_value)

instance1 = Daeheeyun()
instance2 = Daeheeyun()

print("--클래스 속성 변경--")
instance1.set_class_value()
print(instance1.class_value, instance2.class_value)

print("--인스턴스 속성 변경--")
instance1.set_instance_value()
print(instance1.class_value, instance2.class_value)

print("--속성(Attribute) 출력--")
print(instance1.__dict__)       # __dict__는 인스턴스 속성만 보여준다
print(instance2.__dict__)

# 출력 결과

0
--클래스 속성 변경--
10 10
--인스턴스 속성 변경--
20 10
--속성(Attribute) 출력--
{'instance_value': 0, 'class_value': 20}
{'instance_value': 0}

Inheritance(상속)

  • instance attribute, class attribute 헷갈리지 말아야 할 부분

class Family:
    def __init__(self):
        self.lastname = "홍"

    def lname(self):
        print(f"성은 {self.firstname} 입니다.")

class Person(Family):
    def __init__(self, name):
        self.firstname = name
    
    def fname(self):
        print(f"이름은 {self.firstname} 입니다.")

a = Person("호준")
b = Person("민석")


a.lname()
b.lname()
a.lname()

# 출력결과
성은 호준 입니다.
성은 민석 입니다.
성은 호준 입니다.
  • 위 코드를 보면 Person 클래스의 생성자만 사용해도 부모 클래스의 lname 함수를 사용할 수 있다.
  • 그렇지만 lname은 Instance attribute 이다. class attribute가 아니다!
  • 하지만 self.lastname에는 접근이 불가능하다. 부모 클래스의 생성자가 self.lastname 초기화를 진행하는데 아직 부모 클래스 생성자가 호출된적이 없다!
    • 위 출력결과를 보면 알겠지만, 두번째 a.lname() 호출해도 속성값(lastname)이 호준으로 유지되는것을 볼 수 있다. 따라서 인스턴스 속성으로 봐야 할 듯 하다
    • 의문점: Instance attribute는 무조건 생성자를 한번 호출해야 정의가 되어 사용할 수 있는것 아닌가? Family 생성자 호출이 안됐는데 왜 lname 호출이 가능한거지? 상속관계니까 Person 클래스만 호출해도 충분한것으로 받아들여야 하는것인가?
  • lname의 self 파라미터에는 Family 객체가 아닌 Person 객체가 들어간다.
class Student(Person):
    def __init__(self, name, age, GPA):
        super().__init__(name, age)     # 이 super().__init__() 호출을 통해 부모의 속성을 가져올 수 있음
        self.GPA = GPA

    def get_GPA(self):
        super().get_name()      # 부모의 get_name 함수를 호출하기 위한 코드
        print(f'제 학점은 {self.GPA}입니다.')
  • 추가적으로 자식클래스에 __init__()이 따로 없다면 자동으로 super()가 호출되서 부모 클래스의 속성 사용이 가능함

Interface(인터페이스)

  • 정통 Interface의 개념
    • 추상 클래스의 포함된 메서드가 모두 추상 메서드로 이루어져있을때 이를 Interface 라고 함
  • 파이썬은 Interface를 위한 키워드가 존재하지 않음
from abc import *       # abc -> Abstract Base Class

class Parent(metaclass=ABCMeta):
    @abstractmethod
    def run(self):
        pass

class Sub(Parent):

    def hello(self):
        print("hi")

    def run(self):
        print("run run")

person = Sub()
person.hello()
person.run()
  • Python은 NotImplementedError 구문을 통해 구현을 강제하는 동일한 기능을 제공한다. 결정적인 차이점이 무엇인지 아는가?
class Parent():
    def run(self):
        raise NotImplementedError

class Sub(Parent):

    def hello(self):
        print("Hi")
    
    def run(self):
        print("run run)

person = Sub()
person.hello()
person.run()
  • 구현의 강제성을 엄격하게 둘것인가 그렇지 않을것인가의 차이다
    • 추상 클래스를 활용하면 엄격
      • run 함수가 구현되지 않은 상태로 Sub 클래스를 인스턴스화하면 에러 발생
      • 즉, Sub 클래스를 인스턴스로 활용하기 위해선(메모리에 올리기 위해선) 반드시 추상메서드의 구현이 선행되어야 한다
    • NotImplementedError를 활용하면 비교적 자유로움
      • run 함수가 구현되지 않은 상태로 Sub 클래스 인스턴스화해도 괜찮음
      • 단, Sub.run()을 호출할 때 에러 발생
      • 추상 메서드를 호출하는것이 아니라면 Sub클래스의 인스턴스화는 허용한다

위 개념들을 다 섞어서 파이썬 코드 구현

얄팍한 코딩사전 유튜버님의 객체지향 강의 참고했으며, 자바 언어를 파이썬으로 변경해보았다

School 클래스 선언

School 안에 들어갈 ClassRoom 클래스도 추가로 선언

class School:    
    def __init__(self, num_list):
        self.rooms = {}
        
        for i in num_list:
            self.rooms[i] = self.ClassRoom(i)
    
    def getRooms(self, num):
        return self.rooms[num]
    
    class ClassRoom:
        def __init__(self, num):
            self.num = num
            self.window_status = 0
            self.bottom_status = 0
            self.board_status = 0
            self.experiment_status = 0
        
        def clean_window(self, hangju, windex_lister):
            self.window_status += 5
            return hangju - 10, windex_lister - 1.0
        
        def clean_bottom(self, trash):
            self.bottom_status += 5
            return trash + 10

        def clean_board(self, eraser):
            self.board_status += 5
            return eraser - 10

        def clean_experiment(self, experiment):
            self.experiment_status += 5
            return experiment - 10
        
        def get_window_status(self):
            if self.window_status >= 100:
                return True
            else:
                return False
        
        def get_bottom_status(self):
            if self.bottom_status >= 100:
                return True
            else:
                return False
        
        def get_board_status(self):
            if self.board_status >= 100:
                return True
            else:
                return False

        def get_experiment_status(self):
            if self.experiment_status >= 100:
                return True
            else:
                return False
num_list = [100, 101, 102, 103, 104, 105, 106, 107, 108, 109, 110]
school = School(num_list)

jun_hangju_clean = 100
jun_windex_liter = 10.0

jun_hangju_clean, hojun_windex_liter = school.getRooms(100).clean_window(jun_hangju_clean, jun_windex_liter)
print(jun_hangju_clean, hojun_windex_liter)

if jun_hangju_clean < 10:
    # 행주 빨기
    jun_hangju_clean = 100

    # 세제 채우기
if jun_windex_liter < 1:
    jun_windex_liter = 10.0

ho_trash_full = 0
ho_trash_full = school.getRooms(101).clean_bottom(ho_trash_full)
print(ho_trash_full)

if ho_trash_full > 90:
    # 쓰레기통 비우기
    ho_trash_full = 0

chul_eraser_clean = 100
chul_eraser_clean = school.getRooms(100).clean_board(chul_eraser_clean)
print(chul_eraser_clean)

if chul_eraser_clean < 10:
    # 지우개 털기
    chul_eraser_clean = 100
90 9.0
10
90
  • 절차지향적으로 하나씩 명령어를 치다보니 코드가 토나옴. 반복문을 사용해보자!
import time

num_list = [100, 101, 102, 103, 104, 105, 106, 107, 108, 109, 110]
school = School(num_list)

jun_hangju_clean = 100
jun_windex_liter = 10.0

while(not school.getRooms(100).get_window_status()):
    if(jun_hangju_clean <= 10):
        print("행주 빨아옴")
        jun_hangju_clean = 100
    
    if(jun_windex_liter <= 1):
        print("세제 리필")
        jun_windex_liter = 10.0

    print("창문 청소")
    jun_hangju_clean, jun_windex_liter = school.getRooms(100).clean_window(jun_hangju_clean, jun_windex_liter)

ho_eraser_clean = 100

while(not school.getRooms(100).get_board_status()):
    if(ho_eraser_clean < 10):
        print("지우개 털기")
        ho_eraser_clean = 100
    
    print("칠판 청소")
    ho_eraser_clean = school.getRooms(100).clean_board(ho_eraser_clean)

chul_trash = 0

while(not school.getRooms(100).get_bottom_status()):
    if(chul_trash > 90):
        print("쓰레기통 비우기")
        chul_trash = 0
    
    print("바닥 청소")
    chul_trash = school.getRooms(100).clean_bottom(chul_trash)


창문 청소
창문 청소
창문 청소
창문 청소
창문 청소
창문 청소
창문 청소
창문 청소
창문 청소
행주 빨아옴
세제 리필
창문 청소
창문 청소
창문 청소
창문 청소
창문 청소
창문 청소
창문 청소
창문 청소
창문 청소
행주 빨아옴
세제 리필
창문 청소
창문 청소
칠판 청소
칠판 청소
칠판 청소
칠판 청소
칠판 청소
칠판 청소
칠판 청소
칠판 청소
칠판 청소
칠판 청소
지우개 털기
칠판 청소
칠판 청소
칠판 청소
칠판 청소
칠판 청소
칠판 청소
칠판 청소
칠판 청소
칠판 청소
칠판 청소
바닥 청소
바닥 청소
바닥 청소
바닥 청소
바닥 청소
바닥 청소
바닥 청소
바닥 청소
바닥 청소
바닥 청소
쓰레기통 비우기
바닥 청소
바닥 청소
바닥 청소
바닥 청소
바닥 청소
바닥 청소
바닥 청소
바닥 청소
바닥 청소
바닥 청소
  • 일일이 하나씩 코드를 치는것보다 훨씬 줄었지만 여전히 너무 길다..
  • 여기까지가 절차지항적으로 짤 수 있는 코드
class board_cleaner:
    
    def __init__(self, room:School.ClassRoom):
        self.__room = room
        self.__eraser_clean = 100
        
    def clean_board(self):
        while(not self.__room.get_board_status()):
            if(self.__eraser_clean < 10):
                print("지우개 털기")
                self.__eraser_clean = 100
            
            print(f"{self.__room.num}호 칠판 청소")
            self.__eraser_clean = self.__room.clean_board(self.__eraser_clean)

class window_cleaner:
    
    def __init__(self, room:School.ClassRoom):
        self.__room = room
        self.__hangju_clean = 100
        self.__windex_liter = 10.0
        
    def clean_window(self):
        while(not self.__room.get_window_status()):
            if(self.__hangju_clean < 10):
                print("행주 빨기")
                self.__hangju_clean = 100
            
            if(self.__windex_liter < 1):
                print("세제 리필")
                self.__windex_liter = 10.0

            print(f"{self.__room.num}호 창문 청소")
            self.__hangju_clean, self.__windex_liter = self.__eraser_clean = self.__room.clean_window(self.__hangju_clean, self.__windex_liter)

class bottom_cleaner:
    
    def __init__(self, room:School.ClassRoom):
        self.__room = room
        self.__trashback_full = 0
        
    def clean_bottom(self):
        while(not self.__room.get_bottom_status()):
            if(self.__trashback_full >= 90):
                print("쓰레기통 비우기")
                self.__trashback_full = 0
            
            print(f"{self.__room.num}호 바닥 청소")
            self.__trashback_full = self.__room.clean_bottom(self.__trashback_full)
num_list = [100, 101, 102, 103, 104, 105, 106, 107, 108, 109, 110]
school = School(num_list)

hj = board_cleaner(school.getRooms(100))
hj.clean_board()

cs = board_cleaner(school.getRooms(101))
cs.clean_board()

mh = window_cleaner(school.getRooms(101))
mh.clean_window()

nh = bottom_cleaner(school.getRooms(102))
nh.clean_bottom()

board_cleaners = []
board_cleaners.append(board_cleaner(school.getRooms(102)))
board_cleaners.append(board_cleaner(school.getRooms(103)))
board_cleaners.append(board_cleaner(school.getRooms(104)))
board_cleaners.append(board_cleaner(school.getRooms(105)))

for person in board_cleaners:
    person.clean_board()


# 유의해야할 점!
# 자바와 다르게 파이썬은 메서드명만 똑같이 하면 인터페이스로 묶지 않아도 아래 for문과 같이 호출이 가능하다

# cleaners = []
# cleaners.append(bottom_cleaner(school.getRooms(100)))
# cleaners.append(board_cleaner(school.getRooms(100)))
# cleaners.append(window_cleaner(school.getRooms(100)))
# cleaners.append(bottom_cleaner(school.getRooms(101)))
# cleaners.append(board_cleaner(school.getRooms(101)))
# cleaners.append(window_cleaner(school.getRooms(101)))
# cleaners.append(bottom_cleaner(school.getRooms(102)))
# cleaners.append(board_cleaner(school.getRooms(102)))
# cleaners.append(window_cleaner(school.getRooms(102)))

# for people in cleaners:
#     people.clean()
100호 칠판 청소
100호 칠판 청소
100호 칠판 청소
100호 칠판 청소
100호 칠판 청소
100호 칠판 청소
100호 칠판 청소
100호 칠판 청소
100호 칠판 청소
100호 칠판 청소
지우개 털기
100호 칠판 청소
100호 칠판 청소
100호 칠판 청소
100호 칠판 청소
100호 칠판 청소
100호 칠판 청소
100호 칠판 청소
100호 칠판 청소
100호 칠판 청소
100호 칠판 청소
101호 칠판 청소
101호 칠판 청소
101호 칠판 청소
101호 칠판 청소
101호 칠판 청소
101호 칠판 청소
101호 칠판 청소
101호 칠판 청소
101호 칠판 청소
101호 칠판 청소
지우개 털기
101호 칠판 청소
101호 칠판 청소
101호 칠판 청소
101호 칠판 청소
101호 칠판 청소
101호 칠판 청소
101호 칠판 청소
101호 칠판 청소
101호 칠판 청소
101호 칠판 청소
101호 창문 청소
101호 창문 청소
101호 창문 청소
101호 창문 청소
101호 창문 청소
101호 창문 청소
101호 창문 청소
101호 창문 청소
101호 창문 청소
101호 창문 청소
행주 빨기
세제 리필
101호 창문 청소
101호 창문 청소
101호 창문 청소
101호 창문 청소
101호 창문 청소
101호 창문 청소
101호 창문 청소
101호 창문 청소
101호 창문 청소
101호 창문 청소
102호 바닥 청소
102호 바닥 청소
102호 바닥 청소
102호 바닥 청소
102호 바닥 청소
102호 바닥 청소
102호 바닥 청소
102호 바닥 청소
102호 바닥 청소
쓰레기통 비우기
102호 바닥 청소
102호 바닥 청소
102호 바닥 청소
102호 바닥 청소
102호 바닥 청소
102호 바닥 청소
102호 바닥 청소
102호 바닥 청소
102호 바닥 청소
쓰레기통 비우기
102호 바닥 청소
102호 바닥 청소
102호 칠판 청소
102호 칠판 청소
102호 칠판 청소
102호 칠판 청소
102호 칠판 청소
102호 칠판 청소
102호 칠판 청소
102호 칠판 청소
102호 칠판 청소
102호 칠판 청소
지우개 털기
102호 칠판 청소
102호 칠판 청소
102호 칠판 청소
102호 칠판 청소
102호 칠판 청소
102호 칠판 청소
102호 칠판 청소
102호 칠판 청소
102호 칠판 청소
102호 칠판 청소
103호 칠판 청소
103호 칠판 청소
103호 칠판 청소
103호 칠판 청소
103호 칠판 청소
103호 칠판 청소
103호 칠판 청소
103호 칠판 청소
103호 칠판 청소
103호 칠판 청소
지우개 털기
103호 칠판 청소
103호 칠판 청소
103호 칠판 청소
103호 칠판 청소
103호 칠판 청소
103호 칠판 청소
103호 칠판 청소
103호 칠판 청소
103호 칠판 청소
103호 칠판 청소
104호 칠판 청소
104호 칠판 청소
104호 칠판 청소
104호 칠판 청소
104호 칠판 청소
104호 칠판 청소
104호 칠판 청소
104호 칠판 청소
104호 칠판 청소
104호 칠판 청소
지우개 털기
104호 칠판 청소
104호 칠판 청소
104호 칠판 청소
104호 칠판 청소
104호 칠판 청소
104호 칠판 청소
104호 칠판 청소
104호 칠판 청소
104호 칠판 청소
104호 칠판 청소
105호 칠판 청소
105호 칠판 청소
105호 칠판 청소
105호 칠판 청소
105호 칠판 청소
105호 칠판 청소
105호 칠판 청소
105호 칠판 청소
105호 칠판 청소
105호 칠판 청소
지우개 털기
105호 칠판 청소
105호 칠판 청소
105호 칠판 청소
105호 칠판 청소
105호 칠판 청소
105호 칠판 청소
105호 칠판 청소
105호 칠판 청소
105호 칠판 청소
105호 칠판 청소

인터페이스 활용

  • 파이썬은 추상클래스에 추상메소드를 선언하는 방식으로 인터페이스를 구현한다
from abc import *

class CleanRole(metaclass=ABCMeta):

    @abstractmethod
    def clean(self):
        pass

    @abstractmethod
    def class_room_change(self):
        pass

class board_cleaner(CleanRole):
    
    def __init__(self, room:School.ClassRoom):
        self.__room = room
        self.__eraser_clean = 100
        
    def clean_board(self):
        while(not self.__room.get_board_status()):
            if(self.__eraser_clean < 10):
                print("지우개 털기")
                self.__eraser_clean = 100
            
            print(f"{self.__room.num}호 칠판 청소")
            self.__eraser_clean = self.__room.clean_board(self.__eraser_clean)
    
    def clean(self):
        self.clean_board()
    
    def class_room_change(self, room:School.ClassRoom):
        self.__room = room

class window_cleaner(CleanRole):
    
    def __init__(self, room:School.ClassRoom):
        self.__room = room
        self.__hangju_clean = 100
        self.__windex_liter = 10.0
        
    def clean_window(self):
        while(not self.__room.get_window_status()):
            if(self.__hangju_clean < 10):
                print("행주 빨기")
                self.__hangju_clean = 100
            
            if(self.__windex_liter < 1):
                print("세제 리필")
                self.__windex_liter = 10.0

            print(f"{self.__room.num}호 창문 청소")
            self.__hangju_clean, self.__windex_liter = self.__eraser_clean = self.__room.clean_window(self.__hangju_clean, self.__windex_liter)
    
    def clean(self):
        self.clean_window()
    
    def class_room_change(self, room:School.ClassRoom):
        self.__room = room

class bottom_cleaner(CleanRole):
    
    def __init__(self, room:School.ClassRoom):
        self.__room = room
        self.__trashback_full = 0
        
    def clean_bottom(self):
        while(not self.__room.get_bottom_status()):
            if(self.__trashback_full >= 90):
                print("쓰레기통 비우기")
                self.__trashback_full = 0
            
            print(f"{self.__room.num}호 바닥 청소")
            self.__trashback_full = self.__room.clean_bottom(self.__trashback_full)

    def clean(self):
        self.clean_bottom()
    
    def class_room_change(self, room:School.ClassRoom):
        self.__room = room

num_list = [100, 101, 102, 103, 104, 105, 106, 107, 108, 109, 110]
school = School(num_list)


cleaners = []
cleaners.append(bottom_cleaner(school.getRooms(100)))
cleaners.append(board_cleaner(school.getRooms(100)))
cleaners.append(window_cleaner(school.getRooms(100)))
cleaners.append(bottom_cleaner(school.getRooms(101)))
cleaners.append(board_cleaner(school.getRooms(101)))
cleaners.append(window_cleaner(school.getRooms(101)))
cleaners.append(bottom_cleaner(school.getRooms(102)))
cleaners.append(board_cleaner(school.getRooms(102)))
cleaners.append(window_cleaner(school.getRooms(102)))

for people in cleaners:
    people.clean()
100호 바닥 청소
100호 바닥 청소
100호 바닥 청소
100호 바닥 청소
100호 바닥 청소
100호 바닥 청소
100호 바닥 청소
100호 바닥 청소
100호 바닥 청소
쓰레기통 비우기
100호 바닥 청소
100호 바닥 청소
100호 바닥 청소
100호 바닥 청소
100호 바닥 청소
100호 바닥 청소
100호 바닥 청소
100호 바닥 청소
100호 바닥 청소
쓰레기통 비우기
100호 바닥 청소
100호 바닥 청소
100호 칠판 청소
100호 칠판 청소
100호 칠판 청소
100호 칠판 청소
100호 칠판 청소
100호 칠판 청소
100호 칠판 청소
100호 칠판 청소
100호 칠판 청소
100호 칠판 청소
지우개 털기
100호 칠판 청소
100호 칠판 청소
100호 칠판 청소
100호 칠판 청소
100호 칠판 청소
100호 칠판 청소
100호 칠판 청소
100호 칠판 청소
100호 칠판 청소
100호 칠판 청소
100호 창문 청소
100호 창문 청소
100호 창문 청소
100호 창문 청소
100호 창문 청소
100호 창문 청소
100호 창문 청소
100호 창문 청소
100호 창문 청소
100호 창문 청소
행주 빨기
세제 리필
100호 창문 청소
100호 창문 청소
100호 창문 청소
100호 창문 청소
100호 창문 청소
100호 창문 청소
100호 창문 청소
100호 창문 청소
100호 창문 청소
100호 창문 청소
101호 바닥 청소
101호 바닥 청소
101호 바닥 청소
101호 바닥 청소
101호 바닥 청소
101호 바닥 청소
101호 바닥 청소
101호 바닥 청소
101호 바닥 청소
쓰레기통 비우기
101호 바닥 청소
101호 바닥 청소
101호 바닥 청소
101호 바닥 청소
101호 바닥 청소
101호 바닥 청소
101호 바닥 청소
101호 바닥 청소
101호 바닥 청소
쓰레기통 비우기
101호 바닥 청소
101호 바닥 청소
101호 칠판 청소
101호 칠판 청소
101호 칠판 청소
101호 칠판 청소
101호 칠판 청소
101호 칠판 청소
101호 칠판 청소
101호 칠판 청소
101호 칠판 청소
101호 칠판 청소
지우개 털기
101호 칠판 청소
101호 칠판 청소
101호 칠판 청소
101호 칠판 청소
101호 칠판 청소
101호 칠판 청소
101호 칠판 청소
101호 칠판 청소
101호 칠판 청소
101호 칠판 청소
101호 창문 청소
101호 창문 청소
101호 창문 청소
101호 창문 청소
101호 창문 청소
101호 창문 청소
101호 창문 청소
101호 창문 청소
101호 창문 청소
101호 창문 청소
행주 빨기
세제 리필
101호 창문 청소
101호 창문 청소
101호 창문 청소
101호 창문 청소
101호 창문 청소
101호 창문 청소
101호 창문 청소
101호 창문 청소
101호 창문 청소
101호 창문 청소
102호 바닥 청소
102호 바닥 청소
102호 바닥 청소
102호 바닥 청소
102호 바닥 청소
102호 바닥 청소
102호 바닥 청소
102호 바닥 청소
102호 바닥 청소
쓰레기통 비우기
102호 바닥 청소
102호 바닥 청소
102호 바닥 청소
102호 바닥 청소
102호 바닥 청소
102호 바닥 청소
102호 바닥 청소
102호 바닥 청소
102호 바닥 청소
쓰레기통 비우기
102호 바닥 청소
102호 바닥 청소
102호 칠판 청소
102호 칠판 청소
102호 칠판 청소
102호 칠판 청소
102호 칠판 청소
102호 칠판 청소
102호 칠판 청소
102호 칠판 청소
102호 칠판 청소
102호 칠판 청소
지우개 털기
102호 칠판 청소
102호 칠판 청소
102호 칠판 청소
102호 칠판 청소
102호 칠판 청소
102호 칠판 청소
102호 칠판 청소
102호 칠판 청소
102호 칠판 청소
102호 칠판 청소
102호 창문 청소
102호 창문 청소
102호 창문 청소
102호 창문 청소
102호 창문 청소
102호 창문 청소
102호 창문 청소
102호 창문 청소
102호 창문 청소
102호 창문 청소
행주 빨기
세제 리필
102호 창문 청소
102호 창문 청소
102호 창문 청소
102호 창문 청소
102호 창문 청소
102호 창문 청소
102호 창문 청소
102호 창문 청소
102호 창문 청소
102호 창문 청소

클래스 안에 클래스들 넣기

하나의 교실을 팀 단위로 청소하고 이동하고 할 수 있게 구성

class CleanTeam:
    
    def __init__(self, room:School.ClassRoom):
        print("CleanTeam 부모 생성자 호출")
        self.room = room
        self.team = [
            window_cleaner(room), window_cleaner(room),
            bottom_cleaner(room), bottom_cleaner(room), bottom_cleaner(room),
            board_cleaner(room)
        ]
    
    def clean(self):
        for cleaner in self.team:
            cleaner.clean()     # 3개의 cleaner 클래스들은 clean 메서드를 인터페이스를 상속해서 구현해놓은 상태임
    
    def class_room_change(self, room:School.ClassRoom):
        self.room = room
        
        for cleaner in self.team:
            cleaner.class_room_change(room)       # 3개의 cleaner 클래스들은 class_room_change 메서드를 인터페이스를 상속해서 구현해놓은 상태임
    
    def complete(self):
        return self.room.get_board_status() and self.room.get_bottom_status() and self.room.get_window_status()

from abc import *

class CleanRole(metaclass=ABCMeta):

    @abstractmethod
    def clean(self):
        pass

    @abstractmethod
    def class_room_change(self):
        pass

class board_cleaner(CleanRole):
    
    def __init__(self, room:School.ClassRoom):
        self.__room = room
        self.__eraser_clean = 100
        
    def clean_board(self):
        while(not self.__room.get_board_status()):
            if(self.__eraser_clean < 10):
                print("지우개 털기")
                self.__eraser_clean = 100
            
            print(f"{self.__room.num}호 칠판 청소")
            self.__eraser_clean = self.__room.clean_board(self.__eraser_clean)
    
    def clean(self):
        self.clean_board()
    
    def class_room_change(self, room:School.ClassRoom):
        self.__room = room

class window_cleaner(CleanRole):
    
    def __init__(self, room:School.ClassRoom):
        self.__room = room
        self.__hangju_clean = 100
        self.__windex_liter = 10.0
        
    def clean_window(self):
        while(not self.__room.get_window_status()):
            if(self.__hangju_clean < 10):
                print("행주 빨기")
                self.__hangju_clean = 100
            
            if(self.__windex_liter < 1):
                print("세제 리필")
                self.__windex_liter = 10.0

            print(f"{self.__room.num}호 창문 청소")
            self.__hangju_clean, self.__windex_liter = self.__eraser_clean = self.__room.clean_window(self.__hangju_clean, self.__windex_liter)
    
    def clean(self):
        self.clean_window()
    
    def class_room_change(self, room:School.ClassRoom):
        self.__room = room

    def get_room_num(self):
        return self.__room
        
class bottom_cleaner(CleanRole):
    
    def __init__(self, room:School.ClassRoom):
        self.__room = room
        self.__trashback_full = 0
        
    def clean_bottom(self):
        while(not self.__room.get_bottom_status()):
            if(self.__trashback_full >= 90):
                print("쓰레기통 비우기")
                self.__trashback_full = 0
            
            print(f"{self.__room.num}호 바닥 청소")
            self.__trashback_full = self.__room.clean_bottom(self.__trashback_full)

    def clean(self):
        self.clean_bottom()
    
    def class_room_change(self, room:School.ClassRoom):
        self.__room = room
num_list = [100, 101, 102, 103, 104, 105, 106, 107, 108, 109, 110]
school = School(num_list)

team = CleanTeam(school.getRooms(100))
while(not team.complete()):
    print("청소중")
    team.clean()

print("청소 완료")

team = CleanTeam(school.getRooms(101))
while(not team.complete()):
    print("청소중")
    team.clean()

print("청소 완료")
CleanTeam 부모 생성자 호출
청소중
100호 창문 청소
100호 창문 청소
100호 창문 청소
100호 창문 청소
100호 창문 청소
100호 창문 청소
100호 창문 청소
100호 창문 청소
100호 창문 청소
100호 창문 청소
행주 빨기
세제 리필
100호 창문 청소
100호 창문 청소
100호 창문 청소
100호 창문 청소
100호 창문 청소
100호 창문 청소
100호 창문 청소
100호 창문 청소
100호 창문 청소
100호 창문 청소
100호 바닥 청소
100호 바닥 청소
100호 바닥 청소
100호 바닥 청소
100호 바닥 청소
100호 바닥 청소
100호 바닥 청소
100호 바닥 청소
100호 바닥 청소
쓰레기통 비우기
100호 바닥 청소
100호 바닥 청소
100호 바닥 청소
100호 바닥 청소
100호 바닥 청소
100호 바닥 청소
100호 바닥 청소
100호 바닥 청소
100호 바닥 청소
쓰레기통 비우기
100호 바닥 청소
100호 바닥 청소
100호 칠판 청소
100호 칠판 청소
100호 칠판 청소
100호 칠판 청소
100호 칠판 청소
100호 칠판 청소
100호 칠판 청소
100호 칠판 청소
100호 칠판 청소
100호 칠판 청소
지우개 털기
100호 칠판 청소
100호 칠판 청소
100호 칠판 청소
100호 칠판 청소
100호 칠판 청소
100호 칠판 청소
100호 칠판 청소
100호 칠판 청소
100호 칠판 청소
100호 칠판 청소
청소 완료
CleanTeam 부모 생성자 호출
청소중
101호 창문 청소
101호 창문 청소
101호 창문 청소
101호 창문 청소
101호 창문 청소
101호 창문 청소
101호 창문 청소
101호 창문 청소
101호 창문 청소
101호 창문 청소
행주 빨기
세제 리필
101호 창문 청소
101호 창문 청소
101호 창문 청소
101호 창문 청소
101호 창문 청소
101호 창문 청소
101호 창문 청소
101호 창문 청소
101호 창문 청소
101호 창문 청소
101호 바닥 청소
101호 바닥 청소
101호 바닥 청소
101호 바닥 청소
101호 바닥 청소
101호 바닥 청소
101호 바닥 청소
101호 바닥 청소
101호 바닥 청소
쓰레기통 비우기
101호 바닥 청소
101호 바닥 청소
101호 바닥 청소
101호 바닥 청소
101호 바닥 청소
101호 바닥 청소
101호 바닥 청소
101호 바닥 청소
101호 바닥 청소
쓰레기통 비우기
101호 바닥 청소
101호 바닥 청소
101호 칠판 청소
101호 칠판 청소
101호 칠판 청소
101호 칠판 청소
101호 칠판 청소
101호 칠판 청소
101호 칠판 청소
101호 칠판 청소
101호 칠판 청소
101호 칠판 청소
지우개 털기
101호 칠판 청소
101호 칠판 청소
101호 칠판 청소
101호 칠판 청소
101호 칠판 청소
101호 칠판 청소
101호 칠판 청소
101호 칠판 청소
101호 칠판 청소
101호 칠판 청소
청소 완료
  • 코드가 현저히 줄은것을 확인할 수 있다
  • 만약 실험실과 같은 특별 케이스를 청소해야 한다면?
    • 단, 바닥, 창문, 칠판은 실험실에도 있으므로 교실청소팀 클래스의 모든 속성과 메서드가 중복됨
    • 상속을 이용한다
class ScienceRoomTeam(CleanTeam):
    def __init__(self, room:School.ClassRoom):
        print("ScienceRoomTeam 생성자 호출")
        # super().__init__(room)
        # print(self.team)
        # self.team.append(Experiment_Cleaner(room))

    
    def getName(self):
        print(self.team)

class Experiment_Cleaner(CleanRole):
    
    def __init__(self, room:School.ClassRoom):
        self.__room = room
        self.__experiment_clean = 100
        
    def clean_experiment(self):
        while(not self.__room.get_experiment_status()):
            if(self.__experiment_clean < 10):
                print("실험기구용 세척기 빨기")
                self.__experiment_clean = 100

            print(f"{self.__room.num}호 실험기구 청소")
            self.__experiment_clean = self.__room.clean_experiment(self.__experiment_clean)
    
    def clean(self):
        self.clean_experiment()
    
    def class_room_change(self, room:School.ClassRoom):
        self.__room = room

    def get_room_num(self):
        return self.__room
        
from abc import *

class 교실_청소당번(metaclass=ABCMeta):

    def __init__(self, room:School.ClassRoom):     # 자바에서는 생성자 호출하면서 클래스속성에 저장할 수 있지만 파이썬은 추상 클래스는 객체 생성 자체가 안됨
                                                    # 즉 현 시점에서 abstract class는 무의미하다
        self.room = room                                
    
    @abstractclassmethod
    def clean(self):
        pass

    @abstractclassmethod
    def class_room_change(self, room:School.ClassRoom):
        pass


science_room_team = ScienceRoomTeam(school.getRooms(106))
science_room_team.getName()
# science_room_team.clean()

science_room_team.class_room_change(school.getRooms(108))
science_room_team.clean()
ScienceRoomTeam 생성자 호출



---------------------------------------------------------------------------

AttributeError                            Traceback (most recent call last)

C:\Users\HOJUN_~1\AppData\Local\Temp/ipykernel_43704/3350493964.py in <module>
      1 science_room_team = ScienceRoomTeam(school.getRooms(106))
----> 2 science_room_team.getName()
      3 # science_room_team.clean()
      4 
      5 # science_room_team.class_room_change(school.getRooms(108))


C:\Users\HOJUN_~1\AppData\Local\Temp/ipykernel_43704/2545886379.py in getName(self)
      8 
      9     def getName(self):
---> 10         print(self.team)
     11 
     12 class Experiment_Cleaner(CleanRole):


AttributeError: 'ScienceRoomTeam' object has no attribute 'team'