Python

Class 이해하기 :: Class를 쓰는 이유, Class vs function

슈퍼짱짱 2022. 4. 7. 02:26
반응형

Class VS Object

 

 

먼저 Class와 Object(객체)의 개념에 대해 알아보겠다.

 

예를 들자면 class'인간' 이라는 추상적인 타입이고, object'이효리', '유재석' 처럼 실제로 존재하는 객체를 의미한다.

'인간'은 이름, 나이, 성별 등의 속성(attribute)이 있고, 

숨쉬기, 먹기, 잠자기 등의 행동(method)을 한다.

 

객체인 '이효리'와 '유재석' 역시 이러한 인간의 특징들을 모두 가지고있다.

 

여기서 중요한 것은 각 객체는 본인의 고유한 성격을 가진다는 것이다.

만약 '이효리'가 이름을 바꾼다고 해도 '유재석'의 이름은 그대로인 것처럼 말이다.

 

실제 파이썬에서 예를 들자면 List 라는 타입은 Class를 의미하고, 

a = list() 혹은 a = [1, 2, 3, 4] 와 같이 선언했을 때 a가 Object(객체)가 된다.

 

List라는 Class는 list안에 들어있는 item들을 속성(attribute)로 가지고, append, extend 등의 행동(method)를 할 수 있다.

a = [1, 2, 3, 4]역시 1, 2, 3, 4 라는 item들을 가지고있고, append, extend 등의 행동을 할 수 있다.

 

단, 아래와 같은 상황에서 a 객체의 첫 번째 값을 바꿨다고 해서, b 객체에는 아무런 영향을 주지 않는다. 즉, a와 b는 각자의 고유한 성격을 지니고 있다.

 

a = [1, 2, 3, 4]
b = [1, 2, 3, 4]

a[0] = 0

 


Class를 쓰는 이유

 

한 마디로 효율적인 프로그래밍을 하기 위함이다.

이미 반복을 피하기 위해 함수(function)을 쓰고 있는데도 굳이 Class를 써야 하는 이유를 예를 들어 설명하겠다.

 

예를 들어, 본인이 한 레스토랑의 Chef라 해보자. 

이 레스토랑에는 5명의 라면 요리사와 8명의 국수 요리사가 있다.

이 13명의 요리사 각각에게 요리를 지시해야 하는 상황이다.

 

먼저, 라면 요리사 영희에게 지시한다.

 

영희는

1. 저기 첫 번째 냄비, 물 500ml, 꼬불면, 빨간 양념을 준비해서

2. 만약 냄비에 물이 없으면 냄비에 물채우고

3. 물을 끓이고

4. 냄비에 면, 양념 넣은 후

5. 1분만 더 끓여!

 

이를 코드로 짜면 다음과 같다.

 

냄비1 = "첫 번째 냄비"

물1 = 500

면1 = "꼬불"

양념1  = "빨간양념"

if 냄비1_물1 == 0 :

    물1_채우기

while(! 물1_끓기) :

    물1 끓이기

냄비1 = 냄비1 + 면1 + 양념1

sec = 0

while(sec<60) :

    면_익히기

    sec = sec+1

 

다음은 국수 요리사 철수에게 지시한다.

 

철수는

1. 저기 두 번째 냄비, 육수 600ml, 국수면을 준비해서

2. 만약 냄비에 육수가 없으면 냄비에 물채우고

3. 육수를 끓이고

4. 냄비에 면 넣은 후

5. 30초만 더 끓이고 고명올려!

 

역시 코드로 짜면 다음과 같다.

 

냄비2 = "두번째 냄비"

육수1 = 600

면2 = "국수"

if 냄비2_육수1 == 0 :

    육수1_채우기

while(! 육수1_끓기) :

    육수1_끓이기

냄비2 = 냄비2 + 면2 

sec = 0

while(sec<30) :

    면_익히기

    sec = sec+1

고명_얹기

 

이 과정을 나머지 11명에게도 복붙해주면 된다.

 


똑같은 내용의 이렇게 긴 코드를 몇번이나 반복하는 것은 상당히 비효울 적이다.

이를 function으로 구현하여 코드를 줄여보자.

 

def 냄비에_액체_채우기(냄비, 액체) :

    if 냄비_액체 == 0 :

        액체_채우기

 

def 액체_끓이기(액체로_채워진_냄비) :

    while(!액체_끓기) :

        액체_끓이기

 

def 라면_재료_넣기(냄비, 면, 양념) :

    냄비 = 냄비 + 면 + 양념

 

def 국수_재료_넣기(냄비, 면) :

    냄비 = 냄비 + 면 

 

def 라면_익히기(재료가_채워진_냄비, 몇_초=60) :

    sec = 0

    while(sec<몇_초) :

        면_익히기

        sec = sec+1

 

def 국수_익히기(재료가_채워진_냄비, 몇_초=30) :

    sec = 0

    while(sec<몇_초) :

        면_익히기

        sec = sec+1

    고명_얹기

 

이렇게 모든 과정을 function으로 짜주고 다시 명령한다.

 

라면 요리사 영희는

 

냄비1 = "첫 번째 냄비"

물1 = 500

면1 = "꼬불"

양념1  = "빨간양념"

냄비에_액체_채우기(냄비1, 물1)

액체_끓이기(냄비1_물1)

라면_재료_넣기(냄비1, 면1, 양념1)

라면_익히기(냄비1_면1_양념1, 60)

 

국수 요리사 철수는

 

냄비2 = "두번째 냄비"

육수1 = 600

면2 = "국수"

냄비에_액체_채우기(냄비2, 육수1)

액체_끓이기(냄비2_육수1)

국수_재료_넣기(냄비2, 면2)

국수_익히기(냄비2_면2, 30)

 

라면 요리사 민수는 ...

국수 요리사 만수는 ...

 


처음보다는 줄었지만 여전히 비효율적인 모습이다.

라면 요리사 국수 요리는 엄연히 다르지만, 같은 면요리라는 점에서 겹치는 부분이 많아 여전히 중복되는 부분이 많다.

 

또, 라면 요리사 민수는 영희보다 자극적인 맛을 좋아해서 물을 400ml만 넣고 싶을 수도 있고, 

국수 요리사 만수는 국수면이 아니라 꼬불면을 넣고 싶을 수도 있다.

이럴 때 지금처럼 물1, 물2, ..., 면1, 면2, ... 등으로 변수를 관리하면 나중에 누가 어떤 재료로 요리했는지 알아내기 상당히 복잡하고 비효율적일 것이다.

 

이를 해결하기 위해 Class가 필요하다.

 


중복되는 요리를 해결하기 위해 "면 요리"라는 Class를 먼저 선언하고, "라면 요리"와 "국수 요리"에서 "면 요리"를 상속받아주면 이러한 중복을 막을 수 있고,

 

생성자로 각 객체가 생성될 때마다 냄비, 물, 면, 양념들을 바로 대입해주면 요리사마다 어떤 재료를 가지고 있는지 관리하기도 쉬워진다.

 

class 면_요리 :

    def __init__(self, 냄비, 액체, 면, 몇_초) :

        self.냄비 = 냄비

        self.액체 = 액체

        self.면 = 면

        self.몇_초 = 몇_초

    def 냄비에_액체_채우기(self, 냄비, 액체) :

        if 냄비_액체 == 0 :

            액체_채우기

    def 액체_끓이기(self, 액체로_채워진_냄비) :

        while(!액체_끓기) :

            액체_끓이기

    def 재료_넣기(self, 냄비, 면) :

        냄비 = 냄비 + 면

    def 면_익히기(self, 재료가_채워진_냄비, 몇_초) :

        sec = 0

        while(sec<몇_초) :

            면_익히기

            sec = sec+1

    def 요리하기(self) :

        냄비에_액체_채우기(self.냄비, self.액체)

        액체_끓이기(self.액체로_채워진_냄비)

        재료_넣기(self.냄비, self.면)

        면_익히기(self.재료가_채워진_냄비, self.몇_초)

 

class 라면_요리(면_요리) :

    def __init__(self, 냄비, 액체, 면, 몇_초, 양념):

        super().__init__(냄비, 액체, 면, 몇_초)

        self.양념 = 양념

 

    def 재료_넣기(self, 냄비, 면, 양념) :

        냄비 = 냄비 + 면 + 양념

 

class 국수_요리(면_요리) :

    super().__init__()

    

    def 면_익히기(self, 재료가_채워진_냄비, 몇_초) :

        sec = 0

        while(sec<몇_초) :

            면_익히기

            sec = sec+1

        고명_얹기

 

라면_요리와 국수_요리는 모두 면_요리를 상속 받았고,

라면_요리에서는 재료_넣기를, 국수_요리에서는 면_익히기를 override했다.

 

이렇게 한 번 만 미리 Class로 코드를 짜주면

 

영희 = 라면_요리("첫 번째 냄비", "물_500", "꼬불", 60, "빨간양념")

영희.요리하기()

철수 = 국수_요리("세번째 냄비", "육수_600", "국수", 30)

철수.요리하기()

민수 = 라면_요리("네번째 냄비", "물_400", "꼬불", 60, "빨간양념")

민수.요리하기()

만수 = 국수_요리("다섯번째 냄비", "육수_600", "꼬불", 30) 

만수.요리하기()

...

 

과 같이 훨씬 간단하게 명령할 수 있다.

 

또한, 민수가 물을 얼마나 사용했는지는 "민수.액체"로, 만수가 어떤 면을 썼는지는 "만수.면" 으로 훨씬 직관적으로 확인할 수 있다.

 

이처럼 Class를 사용하면 훨씬 직관적이고 짧게 코드를 짤 수 있고,

객체가 가지는 속성(attribute)들도 각 객체별로 따로 관리할 수 있다.

 

또한, 나중에 코드를 수정 해야 할 때도 코드 전반에 거쳐 수정할 필요 없이 필요한 부분만 수정하면 된다.

 

 


참고

 

https://www.youtube.com/watch?v=vrhIxBWSJ04 

반응형