Posts 클래스(2)
Post
Cancel

클래스(2)

1. 클래스 기본


1.1 클래스(1) 요약

  • 변수와 함수들이 모여있는 집합
  • 기본클래스 사용법
    • 클래스의 선언 -> 객체로 만듬 -> 객체의 함수를 호출
  • 생성자 함수
    • 클래스가 객체로 만들어질때 객체에 선언되는 변수를 설정하는 방법


1.2 Class 기본 실습 - 스타크래프트

  • 마린을 클래스로 설계
  • 체력(health :40), 공격력(attack_pow : 5), 공격(attack())
  • 마린 클래스로 마린 객체 2개를 생성해서 마린1이 마린2를 공격하는 코드를 작성
  • attack(self, unit)
1
2
3
4
5
6
7
8
9
10
11
12
13
# 마린
class Marine:
    
    def __init__(self, max_health = 40, attact_pow = 5):
        self.health = max_health
        self.max_health = max_health
        self.attact_pow = attact_pow
        
    def attact(self, unit):
        unit.health -= self.attact_pow
        if unit.health <= 0:
            unit.health = 0
            print('사망')
  • 마린 클래스 생성
  • health = 40, attack_pow = 5가 생성자
  • attact 메서드로 상대 유닛을 공격 가능함


1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
# 메딕 : heal_pow, heal(unit) = 함수

class Medic:
    
    def __init__(self, max_health = 40, heal_pow = 6):
        self.health = max_health
        self.max_health = max_health
        self.heal_pow = heal_pow
        
    def heal(self, unit):
        if unit.health > 0:
            unit.health += self.heal_pow
            
            if unit.health >= unit.max_health :
                unit.health = unit.max_health
                print(unit.health)
            
            elif unit.health > 0:
                print(unit.health)
                
            else:
                print('이미 사망')
  • 메딕 클래스 생성
  • 체력은 40, heal_pow는 6으로 기본 생성자 설정
  • heal 메서드로 다른 유닛을 힐할수 있음
  • heal 메서드는 유닛의 체력은 max_health를 넘어갈수없고, 만약 health가 0이라면 사망한 유닛이라고 알림을 프린트 해주는 조건을 if문을 활용하여 걸어줌


1
2
3
4
5
medic = Medic()
marine_1 = Marine()
marine_2 = Marine()
marine_1.attact(marine_2)
marine_1.health, marine_2.health
1
(40, 35)
  • 마린 클래스를 이용하여 2개의 마린 객체, 메딕의 클래스를 이용하여 1개의 메딕 객체를 생성
  • 마린1이 마린2를 공격함
  • 마린1과 마린2의 health를 확인
  • 마린2의 health가 공격을 받아 35인것을 알수 있음


1
medic.heal(marine_2)
1
40
  • 메딕이 마린2를 heal 메서드를 사용하여 체력을 채워줌
  • 체력이 35에서 40이 된것을 알수있음.


1
2
3
marine_3 = Marine(attact_pow= 20)
marine_3.attact(marine_1)
marine_1.health
1
20
  • 마린객체를 생성할떄 attact_pow를 5가 아닌 20으로 영웅마린을 생성함


2. 상속


2.1 상속이란

  • 클래스의 기능을 가져다가 기능을 수정하거나 추가할때 사용하는 방법


2.2 상속 실습

1
2
3
4
5
6
7
8
9
10
11
# 클래스 생성
class Calculator:
    
    def __init__(self, num1, num2):
        self.num1 = num1
        self.num2 = num2
        
    def plus(self):
        return self.num1 + self.num2
calc = Calculator(2,3)
calc.plus()
1
5
  • plus 메서드가 있는 Calculator 클래스를 생성함


1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
# minus 기능을 추가한 계산기
class Calculator2 :
    def __init__(self, num1, num2):
        self.num1 = num1
        self.num2 = num2
        
    def plus(self):
        return self.num1 + self.num2
    
    # 여기까지 Calculator 에 있는 코드로 중복됨
    # 이럴떄는 상속의 기능을 사용하여 클래스를 만듬
    
    def minus(self):
        return self.num1 - self.num2
    
calc2 = Calculator2(1, 2)
calc2.minus()
1
-1
  • 만일 minus 기능이 있는 Calculator2 클래스를 생성하고 싶다면 위처럼 코드를 다시 다 써야함
  • 하지만 중간 plus 까지 있는 부분은 기존의 Calculator의 클래스와 중복되는 코드 부분임


1
2
3
4
5
6
class Calculator3(Calculator) :
    
    def minus(self):
        return self.num1 - self.num2
calc3 = Calculator3(1, 2)
calc3.plus(), calc3.minus()
1
(3, -1)
  • 상속기능을 사용하여 minus 함수 추가한 Calculator3 클래스를 생성함
  • 클래스 선언시 뒤에 상속해주는 클래스를 넣어주면 상속됨


3. 메서드 오버라이딩


3.1 메서드 오버라이딩이란

1
2
3
4
5
6
7
8
class Calculator4(Calculator3):
    
    def plus(self):
        return self.num1 ** 2 + self.num2 ** 2
    
calc3 = Calculator3(1, 2)
calc4 = Calculator4(1, 2)
calc3.plus(), calc4.plus()
1
(3, 5)
  • Calculator3 클래스의 Plus 메서드를 제곱 후 더하는 메서드로 변경하는 클래스 Calculator4를 생성하는 코드
  • 부모 클래스를 괄호 안에 넣고 클래스를 선언 후 변경을 원하는 메서드를 재 정의 하면됨
  • 많은 코드들 중 수정하고 싶은 함수만 바꾸고 싶을때 사용함
  • 같은 함수 이름을 사용하면 기존 코드가 수정이 됨
  • 부모 클래스의 메서드는 그대로임
  • 기존 상속 전 기능은 그대로 유지


3.2 상속 실습

3.2.1 아이폰 1, 2, 3 만들기

1
2
3
class Iphone1 :
    def calling(self):
        print('calling')
  • 아이폰1 : calling - print(‘calling)


1
2
3
class Iphone2(Iphone1):
    def send_msg(self):
        print('send msg')
  • 아이폰2 : 아이폰1 + send msg-


1
2
3
class Iphone3(Iphone2):
    def internet(self):
        print('internet')
  • 아이폰3 : 아이폰2 + internet


1
2
3
4
iphone3 = Iphone3()
iphone3.calling()
iphone3.send_msg()
iphone3.internet()
1
2
3
calling
send msg
internet
  • 아이폰3의 객체를 생성 후
  • 아이폰1과 아이폰2에서 사용가능한 메서드들(calling, send_msg)을 아이폰3에서 사용가능함을 확인


3.2.2 다중상속

  • 다중상속 을 이용하여 Iphhon3에 갤럭시 기능을 추가한 DssPhone 생성
1
2
3
class Galuxy :
    def show_img(self):
        print('show_img')
  • show_img(사진을 보여주는 메서드)가 있는 Galuxy 클래스


1
2
3
class DssPhone(Iphone3, Galuxy):
    def camera(self):
        print('camera')
  • 앞에서 만든 Iphone3와 Galuxy기능이 모두 있고 camera 기능이 있는 DssPhone 정의
  • 클래스의 상속은 1개만 가능한것이 아닌, 2개도 가능하다


1
2
dss_phone = DssPhone()
[func for func in dir(dss_phone) if func[:2] != '__']
1
['calling', 'camera', 'internet', 'send_msg', 'show_img']
  • dss_phone 객체를 생성 후 사용가능한 메서드들을 출력
  • 기존의 Iphone3 Class와 Galuxy Class의 메서드와 새로 생성한 Camera 메서드까지 확인 가능함


4. Super


4.1 Super 기본

  • 부모 클래스에서 사용된 함수의 코드를 가져다가 자식 클래스의 함수에서 재사용 할때 사용
1
2
3
4
5
6
7
8
9
10
11
12
13
class A:
    def plus(self):
        code1
        
class B(A) :
    
    def plus(self):
        super().plus()
        code4 # ClassA plus 메서드에 code4가 추가됨, code1은 classA에서 바꿔도 같이 바뀜
    def minus(self):
        code2
        code3

1
2
3
4
5
6
7
8
9
10
class Marine :
    
    def __init__(self):
        self.health = 40
        self.attact_pow = 5
    
    def attact(self, unit):
        unit.health == self.attact_pow
        if unit.health <= 0:
            unit.health = 0
1
2
3
4
5
6
7
8
9
10
11
12
class Marine2(Marine):
    
    def __init__(self):
        # self.health = 40
        # self.attact_pow = 5
        
        # super를 안쓰고 함수를 작성하면 오버라이딩 되서 코드가 사라짐
        # super().함수이름.()
        super().__init__()
        
        # __init__에 추가한 변수
        self.max_health = 40
  • Marine 클래스를 상속받는 Marine2 클래스를 생성
  • __init__생성자 함수에 super를 사용하여 max_health를 추가하여 생성함
  • 만일 super를 안쓰고 그냥 함수를 작성하면 메서드 오버라이딩이 되어 코드가 사라지게 됨
  • super().함수이름() 으로 사용함


1
2
marine = Marine2()
marine.health, marine.attact_pow, marine.max_health
1
(40, 5, 40)
  • Marine2 클래스로 marine 객체를 생성하여 super로 추가한 max_health를 확인함


5. Class의 getter, setter


5.1 Getter, Setter란?

  • 객체의 내부 변수에 접근할때 특정 로직을 거쳐서 접근 시키는 방법
1
2
3
4
5
6
7
8
9
10
11
12
13
class User :
    def __init__(self, first_name):
        self.first_name = first_name
        
    def setter(self, first_name):
        print('setter')
        self.first_name = first_name
        
    def getter(self):
        print('getter')
        return self.first_name
    
    name = property(getter, setter)
  • setter와 getter를 포함한 User 클래스 정의
  • property는 변수에 getter, setter 가 접근하도록 하는 함수


1
2
user1 = User('andy')
user1.first_name
1
'andy'
  • andy를 first_name으로 가지는 user1 객체 생성


1
2
# setter 함수 실행
user1.name = 1
1
setter
  • name 함수로 setter를 실행, name은 first_name을 1로 바꿈


1
2
# getter 함수 실행
user1.name
1
2
getter
1
  • getter가 실행되며 first_name이 return되어 저장됨


1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
# 변수, 함수마다 getter, setter를 각각 만들어줘야함
# first_name이 3글자 이상이면 실행, 아니면 error를 출력

class User:
    def __init__(self, first_name):
        self.first_name = first_name
        
    def setter(self, first_name):
        if len(first_name) >= 3:
            self.first_name = first_name
            print('setter')
        else :
            print('error')
            
    def getter(self):
        print('getter')
        return self.first_name
    
    # property 변수에 getter, setter 가 접근하도록 하는 함수
    name = property(getter, setter)
  • 변수, 함수마다 getter, setter를 각각 만들어줘야함
  • first_name이 3글자 이상이면 실행, 아니면 error를 출력


1
2
user1 = User('andy')
user1.first_name
1
'andy'
1
user1.name = 'a'
1
error
  • setter의 조건에 의해 a는 3글자 이하이므로 error이 출력됨


6. Non public


6.1 Non public이란?

  • mangling 이라는 방법으로 다이렉트로 객체의 변수에 접근하지 못하게 하는 방법


1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
class Calculator :
    
    def __init__(self, num1, num2):
        self.num1 = num1
        self.num2 = num2
        
    def getter(self):
        return self.num2
    
    # num2에 0이 들어가지 않도록 함
    
    def setter(self, num2):
        num2 = 1 if num2 == 0 else num2
        self.num2 = num2
        
    def div(self):
        return self.num1 / self.num2
    
    number2 = property(getter, setter)
1
2
calc = Calculator(1, 2)
calc.div()
1
0.5
  • Calculator 클래스를 생성
  • div 메서드를 위해 setter의 조건으로 num2에 0이 들어가지 않게 함
    • div 메서드는 num2를 분모로 가지는데 0이 분모로 되게 되면 에러가 나게됨


1
2
# 처음 생성시에 2를 넣어서 2가 나옴
calc.number2
1
2
  • 처음 객체 생성시 number2는 2를 넣어서 2가 출력됨


1
2
3
# setter 로 인해서 0을 넣어도 1로 바뀌게됨
calc.number2 = 0
calc.number2
1
1
  • setter의 조건으로 인해 number2에 0을 넣어도 1로 바뀜


1
2
3
# 하지만 num2 의 변수에 직접 넣으면 setter 함수를 거치지 않고 값이 바뀌게됨
calc.num2 = 0
calc.num2
1
0
  • 하지만 num2 의 변수에 직접 넣으면 0을 넣으면 setter 함수를 거치지 않고 값이 바뀌게됨


1
2
# 결국 num2에 0이 들어가게되어 에러뜸
calc.div()
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
---------------------------------------------------------------------------

ZeroDivisionError                         Traceback (most recent call last)

<ipython-input-58-eb2d47ff24ac> in <module>
      1 # 결국 num2에 0이 들어가게되어 에러뜸
----> 2 calc.div()


<ipython-input-55-4a08af25feab> in div(self)
     15 
     16     def div(self):
---> 17         return self.num1 / self.num2
     18 
     19     number2 = property(getter, setter)


ZeroDivisionError: division by zero
  • 결국 num2에 0이 들어가게되어 에러뜸


1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
class Calculator :
    
    def __init__(self, num1, num2):
        self.num1 = num1
        self.__num2 = num2 #앞에 __를 넣어서 바뀌지 않게 함 mangling
        
    def getter(self):
        return self.__num2
    
    # num2에 0이 들어가지 않도록 함
    
    def setter(self, num2):
        num2 = 1 if num2 == 0 else num2
        self.__num2 = num2
    
    def div(self):
        return self.num1 / self.__num2 # num2에 0이 들어가면 에러가뜸
        
    number2 = property(getter, setter)
  • calculator를 정의할때 변수에 직접 접근하지 못하게 변수 앞에 __ 를 붙여서 Mangling을 사용하여 클래스를 생성


1
2
calc = Calculator(1, 2)
calc.num1
1
1
  • calc 객체를 생성하여 num1를 출력해보면 1로 나옴


1
2
# calc에 num2라는 변수는 없음
calc.num2
1
2
3
4
5
6
7
8
9
10
---------------------------------------------------------------------------

AttributeError                            Traceback (most recent call last)

<ipython-input-42-fc1602e37b0a> in <module>
      1 # calc에 num2라는 변수는 없음
----> 2 calc.num2


AttributeError: 'Calculator' object has no attribute 'num2'
  • 마찬가지로 num2를 확인해보면 없는 변수라고 에러가 뜸


1
2
# 마찬가지로 __num2도 없는것으로 나옴
calc.__num2
1
2
3
4
5
6
7
8
9
---------------------------------------------------------------------------

AttributeError                            Traceback (most recent call last)

<ipython-input-43-9422eadacb85> in <module>
----> 1 calc.__num2


AttributeError: 'Calculator' object has no attribute '__num2'

-__num2로 확인해봐도 없는것으로 나옴


1
2
3
# 작성할땐 __num2지만 실제로는 _(class명)__(변수명) [_calculator__num2]로 저장됨
# 따라서 불러올때는 (객체명)._(class명)__(변수명)으로 불러옴
calc._Calculator__num2 
1
2
  • 작성할땐 __num2지만 실제로는 _(class명)__(변수명) [_calculator__num2]로 저장됨
  • 따라서 불러올때는 (객체명)._(class명)__(변수명)으로 불러옴


1
2
3
4
5
6
# num2라는 변수를 생성하면서 0을 넣을수는 있음
# num2 != __num2 (_class명__변수명) 임.
# 결국 div 함수의 self.num1 / self.__num2 와는 상관이 없는 변수임

calc.num2 = 0 
calc.num2
1
0
  • num2라는 변수를 생성하면서 0을 넣을수는 있음
  • 하지만 num2 != __num2 (_class명__변수명)이 아님
  • 결국 div 함수의 self.num1 / self.__num2 와는 상관이 없는 변수임


6.2 함수에 Mangling

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
# 함수에 mangling 하기
class Calculator():
    def __init__(self, num1, num2):
        self.num1 = num1
        self.__num2 = num2 # 앞에 __를 넣어서 바뀌지 않게 함, mangling
        
    def getter(self):
        return self.__num2
    
    # num2에 0이 들어가지 않게 함
    def setter(self, num2):
        num2 = 1 if num2 == 0 else num2
        self.__num2 = num2
        
    # __ 를 함수앞에 붙여 mangling 하여 단독 함수 호출로 사용을 못함
    
    def __disp(self):
        print(self.num1, self.__num2)
        
    def div(self):
        self.__disp()
        return self.num1 / self.__num2
    
    number2 = property(getter, setter)
  • Calculator를 생성할때 __를 함수앞에 붙여 Mangling하여 단독 함수로 호출하여 사용하지 못하게 함


1
2
calc = Calculator(1, 2)
calc.div()
1
0.5


7. is a & has a


7.1 is a & has a 란?

  • 클래스를 설계하는 개념
  • A is a B
    • A는 B이다. 상속을 이용해서 클래스를 만드는 방법
  • A has a b
    • A는 B를 가진다. A가 B객체를 가지고 클래스를 만드는 방법


7.2 is a 실습

1
2
3
4
5
# is a
class Person:
    def __init__(self, name, email):
        self.name = name
        self.email = email
1
2
3
class Person2(Person):
    def info(self):
        print(self.name, self.email)
1
2
person = Person2('andy', 'andy@email.com')
person.info()
1
andy andy@email.com
  • Person 클래스를 만들고 name, email를 아규먼트로 받음
  • Person2 클래스에 info 메서드를 추가하여 name, email을 출력하게 함
  • Person2는 Person 클래스를 상속하여 생성


7.3 has a 실습

1
2
3
4
5
6
7
# has a
class Name:
    def __init__(self, name):
        self.name_str = name
class Email:
    def __init__(self, email):
        self.email_str = email
1
2
3
4
5
6
7
class Person:
    def __init__(self, name_obj, email_obj):
        self.name = name_obj
        self.email = email_obj
    
    def info(self):
        print(name.name_str, email.email_str)
1
2
3
4
# Name, Email 클래스를 Person 클래스에 적용
name = Name('andy')
email = Email('andy@email.com')
Person = Person(name, email)
1
Person.info()
1
andy andy@email.com
  • Name, Email 클래스를 만듬
  • Person 클래스는 Name, Email을 적용하여 생성
  • Person 클래스에 info 메서드를 추가함


8. Magic(Special) method


8.1 Magic(Special) method란?

  • compare
    • __eq__ : ==
    • __ne__ : !=
    • __lt__ : <
  • calculate
    • __add__ : +
    • __sub__ : -
  • __repr__ : 객체의 내용을 출력(개발자용)
  • __str__


8.2 equal

1
'test' == 'test'
1
True


1
'test'.__eq__('test')
1
True
  • ==는 __eq__로 사용이 가능함


1
1 + 2 == '1' + '2'
1
False
  • 당연히 1 + 2 와 ‘1’ + ‘2’ 는 다르다


8.3 Magic method 실습

1
2
3
4
5
6
7
8
9
10
11
12
class Txt:
    def __init__(self, txt):
        self.txt = txt
        
    def __eq__(self, txt_obj):
        return self.txt.lower() == txt_obj.txt.lower()
    
    def __repr__(self):
        return f'Txt(txt = {self.txt})'
    
    def __str__(self):
        return self.txt
  • Txt 클래스를 생성
  • __eq__ : Txt로 생성된 객체들이 같은지 확인(lower 함수로 대소문자는 상관없게 함)
  • __repr__ : 객체의 정보를 출력함, Txt 에서는 아규먼트로 받은 txt를 (Txt(txt = {self.txt}) 형식으로 출력함
  • __str__ : 객체의 정보를 출력함, __repr__와 다른점은 str 형식이라는 점


1
2
3
t1 = Txt('python')
t2 = Txt('Python')
t3 = t1
1
t1 == t2, t1 == t3, t2 == t3 
1
(True, True, True)
  • Txt 클래스를 사용하여 t1과 t2를 생성 t3 는 t1를 복사함


1
t1
1
Txt(txt = python)
  • __repr__을 출력함


1
t1.__str__
1
<bound method Txt.__str__ of Txt(txt = python)>
  • __str__ 메서드 사용, __repr__의 정보를 가지고 있음. __repr__와 다른점은 str 형태로 바꿔준다는 점
This post is licensed under CC BY 4.0 by the author.