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 형태로 바꿔준다는 점