코드스테이츠 AI 부트캠프/Section 3

AIB_321_복습정리 : 디버깅, 함수, 클래스, 데코레이터, 파이썬 기본문법

yourhm 2021. 12. 27. 09:12

1. 클래스 & 인스턴스

class      (=클래스 =설계서)     : '부류' ---------- PART A

instance (=인스턴스 =제품)    : '실체' ---------- PART B

object    (=오브젝트 =객체)     : 모든 '실체'를 대표하는 포괄적인 의미를 갖는다.

 

• 클래스의 인스턴스는 객체다.

• 실제 세계에 존재하는 인스턴스를 '객체'라고 하는 것.

• 객체들의 공통점을 간추려서 개념적으로 먼저 나타내주는 게 '클래스'

 

예시1

클래스 가수 '가수'라는 단 하나의 사람 or 물건이 실제 존재하지 않는다. 노래하는 사람을 '가수'라고 말함.
객체 영지 '가수'라는 클래스를 이용해 실체를 만들 수 있다. '영지'라는 객체가 만들어졌다.
객체 폴킴 '가수'라는 클래스를 이용해 실체를 만들 수 있다. '폴킴'이라는 객체가 만들어졌다.

 

예시2

클래스 포켓몬 '포켓몬'이라는 단 하나의 실체가 실제 존재하지 않는다. 포켓몬들을 통틀어 '포켓몬'이라고 말함.
객체 피카츄  
객체  

 

 

 

2. 속성 & 메서드

attribute (=속성 =변수)     : 해당 클래스로 생성된 객체들이 갖는 '공통적인' 특성? 특징? 정의

method  (=메서드 =기능)  : 해당 클래스로 생성된 객체들이 할수있는 행동? 기능? 정의

 

- 클래스가 갖는 속성(변수) / 객체가 갖는 속성(변수) -> 둘을 항상 구분해서 생각해야한다

- def example(self)

 : 클래스 내에서 메서드 정의시 'self'라는 인자를 받지만, 객체 생성후 객체의 메서드 호출할 때는 인자를 써주지 않아도, 객체 스스로가 첫번째 인자로 전달된다. 'self' 라는 것은 바로 그 클래스의 객체를 가리키는데, 예를 들어 영지와 폴킴이 클래스 내에 정의된 sing(노래하기) 메서드를 똑같이 가지기 때문에 서로 구별하기 위해서 사용한 것. 우리는 객체를 더 만들 수 있고 만들어진 객체들은 서로 구별되어야 하기 때문에.

- def __init__(self)

: 파이썬에서 특별하게 약속된 메서드 가운데 하나인, 초기화(initialize) 메서드. 어떤 클래스의 객체를 생성할 때 자동으로 처음에 호출되는 특수한 메서드로, '객체'가 갖게 될 여러가지 성질(= 속성, 변수)를 미리 정해두는 것.  반드시 첫 번째 인수로 self를 지정해야하고, 다른 인수를 받고 싶다면 self 다음에 이어서 적어주면 된다. 왜 객체의 성질을 미리 정해주는게 좋느냐? CODE로 보기 (2)으로!

 

 

  [예시1] [예시2] [예시3] [예시4]
클래스 가수 포켓몬 사람 학생
객체 영지 피카츄 Alice Chris
객체 폴킴 Ben Diana
속성 음정 파워
속성 박자 HP 몸무게
메서드 노래하기 훈련하기 밥먹기 공부하기
메서드 연습하기 진화하기 얘기하기 발표하기

 

 

1) 실행 단계

 PART A 

-> 클래스를 정의하고, 해당 클래스의 성질이나 행동을 정의하는 파트

 

step1

클래스를 정의한다. = 클래스를 만든다

class '클래스 이름' 

 

step 2

해당 클래스의 성질이나 행동을 정의한다.

'변수 이름' = 값 -> 클래스가 갖는 공통 성질을 정의 (클래스의 특징)

def __init___(self) -> 객체가 갖는 성질을 초기화 메서드로 미리 정의 (객체의 특징)

def  '메서드 이름'(self) -> 클래스가 하는 행동을 정의 (기능)

 

step 3

미리 정의해둔 클래스를 상속받는 새로운 클래스를 정의한다.

class '새로운 클래스 이름' ('미리 정의해둔 가져올 클래스 이름')

 

 

 PART B 

-> 정의된 클래스를 이용하여 인스턴스를 생성하고, 생성된 인스턴스의 성질이나 행동 실행하는 파트

 

step 4

클래스를 이용하여 인스턴스를 생성한다.

'객체 이름' = '클래스 이름'( )

 

step 5

생성된 인스턴스에게 미리 정의해둔 속성, 메서드를 실행시켜 본다.

'객체이름'.'변수 이름'

'객체 이름'.'메서드이름'( )

 

- 클래스로 만든 모든 객체들은, 클래스 안에 정의된 모든 메서드 사용할 수 있다.

- 클래스로 만든 모든 객체들은, 클래스 안에 정의된 모든 특성을 공통적으로 갖는다. 

 

 

2) CODE로 보기 (self & __init__ 개념)

Singer 라는 클래스 안에 클래스 변수(성질) tone, beat 와 메서드(기능) sing 을 미리 정의해놓는다.

그 다음 Singer 라는 클래스를 통해 youngji라는 객체를 생성하면, youngji는 Singer라는 클래스를 이용해 만들었기 때문에 Singer 라는 클래스 안에 정의해둔 '성질'을 가질 수 있고, '기능'도 모두 실행할 수 있게 된다. 따라서 youngji에게 sing(노래하기) 메서드를 실행시킬 수 있다.

 

 PART A 

class Singer:          # 1단계 Singer 클래스 정의
  tone = 90            # 2단계 Singer 클래스 안에서 tune(음정) 특징 정의
  beat = 90            # 2단계 Singer 클래스 안에서 beat(박자) 특징 정의
  
  def sing(self):      # 2단계 Singer 클래스 안에서 sing(노래하기) 메서드 정의
    return "yeah~~~"
  
  def practice(self):  # 2단계 Singer 클래스 안에서 practice(연습하기) 메서드 정의
    self.tone += 2
    self.beat += 1
    
  def __init__(self):  # 2단계 Singer 클래스 안에서 초기화 메서드 정의
    print("새로운 가수가 데뷔했습니다!")

 

 PART B 

첫번째 객체 생성(youngji) -> 객체가 생성되면서 초기화 함수가 자동으로 실행된 것을 확인할 수 있다.

youngji = Singer()     # Part2_1단계 youngji 라는 이름의 객체 생성
# 새로운 가수가 데뷔했습니다!

youngji.beat           # Part2_2단계 youngji 라는 이름의 객체가 갖는 beat(박자) 변수 실행
# 90
youngji.sing()         # Part2_2단계 youngji 라는 이름의 객체에 sing(노래하기) 메서드 실행
# 'yeah~~~'

 

두번째 객체 생성(paulkim) -> 객체가 생성되면서 초기화 함수가 자동으로 실행된 것을 확인할 수 있다.

연습하기 메서드를 실행하고 음정 변수를 호출하면 '92' 로 출력된 것을 확인할 수 있다.

paulkim = Singer()     # Part2_1단계 paulkim 이라는 이름의 객체 생성
# 새로운 가수가 데뷔했습니다!

paulkim.practice()     # Part2_2단계 paulkim 이라는 이름의 객체에 practice(연습하기) 메서드 실행
paulkim.tone           # Part2_2단계 paulkim 이라는 이름의 객체의 tone(음정) 속성 호출
# 92

 

 

3) CODE로 보기 (self & __init__)

포켓몬 라는 클래스 안에 공통적으로 갖는 클래스 변수(성질)이 아닌 각 객체마다 다르게 가질 인스턴스 변수(성질)을 정의할 수 있다. inputspec 메서드에서 파워, hp, 레벨 인자를 받아서 가질 수 있게 정의해둔다. 정의가 끝나면 먼저 'pikachu' 객체를 생성하고나서, inputspec 메서드를 실행하여 피카츄의 스펙을 입력할 수 있다.

 

 PART A 

class Pokemon:
  def inputspec(self, power, hp, level):
    self.power = power
    self.hp = hp
    self.level = level
  
  def training(self):
    self.power += 10
    self.hp += 5
    print("power: " self.power ", hp: " self.hp)
    
  def levelup(self):
    self.power *= 2
    self.hp *= 2
    self.level += 1
    print("진화 성공 레벨업! level: " self.level)

 

 PART B 

pikachu = Pokemon()               # Part2_1단계 pikachu 라는 이름의 객체 생성

pikachu.inputspec(60, 70, 2)      # Part2_2단계 pikachu 객체에 inputspec 메서드 실행하여, 스펙을 입력 받음.
pikachu.training()
# power: 70, hp: 75
pikachu.levelup()
# 진화 성공 레벨업! level: 3

위에서 처럼 피카츄 라는 객체를 생성만 하고 아무것도 하지 않으면, 피카츄는 파워/힘/레벨 이런 성질들을 아무것도 갖지 못한 상태인 것이다. 즉 inputspec 메서드를 실행해주지 않으면 training 이나 levelup 같은 미리 정의해둔 다른 메서드들도 모두 사용할수가 없다. 그냥 인형.... 꼭 inputspec 메서드를 실행해주어야만 피카츄는 성질을 갖게되고, training 이나 levelup 메서드도 사용할 수 있는 것이다. 

 

그래서 바로 여기서 __init__이 필요한 이유가 나온다.

객체가 생성되자마자 바로 초기값으로 스펙을 갖게되면 굳이 inputspec 메서드를 실행해주지 않아도, training 이나 levelup 과 같은 메서드를 실행해줄 수 있기 때문에 편하다. 단순히 편함 뿐만아니라 굳이 꼭 처음에 무언가를 수행해주어야 하는 과정이 있으면 나중에 실행할때 그걸 누락하여 오류가 발생되는 상황이 생길 수도 있지만, 초기값을 설정하게 되면 그런 상황이 안생기게 되니까 보다 안전하다. 

 

이제 위 코드에 있었던 inputspec 대신, 객체를 생성하자마자 성질을 갖도록 __init__(초기화메서드)를 정의해보자. 

 

 PART A 

방법1 (inputspec 없애고 __init__ 안에 inputspec에 있던 내용 모두 다시 적어주기)

class Pokemon:
  def __init__(self, power, hp, level):
    self.power = power
    self.hp = hp
    self.level = level
  
  def training(self):
    self.power += 10
    self.hp += 5
    print("power: " self.power ", hp: " self.hp)
    
  def levelup(self):
    self.power *= 2
    self.hp *= 2
    self.level += 1
    print("진화 성공 레벨업! level: " self.level)

 

방법2 (inputspec 살리고 __init__ 안에 inputspec 그대로 적어주기)

class Pokemon:
  def inputspec(self, power, hp, level):
    self.power = power
    self.hp = hp
    self.level = level
  
  def training(self):
    self.power += 10
    self.hp += 5
    print("power: " self.power ", hp: " self.hp)
    
  def levelup(self):
    self.power *= 2
    self.hp *= 2
    self.level += 1
    print("진화 성공 레벨업! level: " self.level)
    
  def __init__(self, power, hp, level):
    self.inputspec(power, hp, level)

객체를 생성시키면서 힘, hp, 레벨을 인자로 받아서, inputspec 메서드에게 넘겨주도록 했다(방법2). 물론 초기화 메서드에서 직접 변수를 다뤄도(방법1) 상관없지만 이왕 inputspec 메서드를 미리 만들어뒀으니까 이용을 한 것! 이제부터는 객체를 만들 때는 다음과 같이 세 개의 인자를 반드시 넘겨줘야한다.

 

 PART B 

eevee = Pokemon(40, 50, 1)     # Part2_1단계 eevee 라는 이름의 객체 생성되면서, 스펙 인자값들 받으며 갖게됨.
     
eevee.training()               # Part2_2단계 eevee 객체에 트레이닝 메서드 실행
# power: 50, hp: 55
eevee.levelup()
# 진화 성공 레벨업! level: 2

 

 

3) CODE로 보기 (self & __init__)

__init__  인자를 받을수도 있지만, 값을 미리 정해줄 수도 있다.

master 는 무조건 지우라고 정하려면, 메서드 정의할 때 먼저 적어준다.

 

 PART A 

class Pokemon:
  def inputspec(self, power, hp, level):
    self.power = power
    self.hp = hp
    self.level = level
  
  def training(self):
    self.power += 10
    self.hp += 5
    print("power: " self.power ", hp: " self.hp)
    
  def levelup(self):
    self.power *= 2
    self.hp *= 2
    self.level += 1
    print("진화 성공 레벨업! level: " self.level)
    
  def __init__(self, power, hp, level, master='jiwoo'):
    self.inputspec(power, hp, level)
    self.master = master

 

 PART B 

pidgeot = Pokemon(70, 80, 2)     # Part2_1단계 피존투 라는 이름의 객체 생성되면서, 스펙 인자값들 받으며 갖게됨.
     
print(pidgeot.master)
# jiwoo

 

 

 

3. 상속 (Inthritance)

: 어떤 클래스가 다른 클래스의 성질을 물려받는 것. 어떤 클래스를 만들 때 처음부터 모든 것을 새로 만들 필요 없이, 핵심적인 성질을 갖고 있는 다른 클래스로부터 상속을 받아서 조그만 손을 보면 쓸만한 클래스를 만들 수 있다.

 

1) CODE로 보기

 PART A 

class Person:
    # 눈 두 개, 입 하나...
    eyes = 2
    mouth = 1

    # 먹고 이야기하고...
    def eat(self):
        print 'yammy~'

    def talk(self):
        print 'blah~'

먼저, 보통사람을 나타낸 Person 이라는 클래스를 정의했다.

ㄴ 눈/입 속성을 정의했고, 밥먹기/얘기하기 메서드를 정의했다.

 

class Student(Person):     # Person 클래스를 상속받은 'Student' 라는 클래스를 정의
    def study(self):
        print 'go go!'

이제, Student 라는 클래스를 만들건데, 학생도 사람이니까 Person의 눈/입 이런 속성을 갖고 있을 것이고, 밥먹기/얘기하기 같은 행동(메서드)을 당연히 할 수 있을 것이다. 거기에 Student만 갖는 특징적인 속성이나 메서드를 추가적으로 갖도록 하면 되는 것이다. 이 때 Student 클래스 안에 눈/입 속성, 밥먹기/얘기하기 메서드까지 모든 것을 새로 정의해주려면 너무 비효율적이라는 것이다.

 

이럴 때 '상속'을 이용하여 Student 클래스를 만들면 된다.

 

Student 클래스 안에 굳이 눈/입 속성이나 밥먹기/얘기하기 메서드를 굳이 하나하나 새로 만들어주지 않아도 Person의 속성과 메서드를 모두 물려받아서 갖게되는것이다. 

 

❓ 상속받지 말고 그냥 위에 정의해둔 내용 복붙해서, 그대로 Student 클래스 안에 써주면 그렇게 귀찮지 않다고 생각할 수 있지 않을까? 물론 그렇게도 가능하지만, 나중에 속성이나 메서드를 수정하거나 추가해야하는 상황이 생기면 그럴 때마다 Person 클래스와 Student 클래스 모두 각각 수정이 필요해진다. 하지만 상속을 받으면 각각 수정해야 하는 상황이 생기지 않는다.

 

 PART B 

alice = Person()     # Person 클래스로 생성된 객체 'alice'

alice.mouth
# 1
alice.talk()
# blah~

chris = Student()    # Student 클래스로 생성된 객체 'chris'

chris.mouth
# 1
chris.talk()
# blah~

Student 클래스의 객체인 chris 도 Person  클래스의 객체인 alice 처럼 눈/입 속성을 갖고 밥먹기/얘기하기 행동을 한다. 왜냐면 Student 클래스가 Person 클래스를 상속받아 정의된 클래스이므로! 

 

chris.study()
# go go!

추가적으로 chris는 Student 클래스의 객체이므로, Student 클래스 만의 메서드인 'study'라는 행동을 추가적으로 할 수 있다. 

 

 

 

[참고자료]

https://wikidocs.net/88