Class
1. 객체지향이란 : 설계도(Class) 작성 -> 실제 물건(객체; Object)
'사용자 정의 데이터 타입'이라고도 한다.
사용 이유: 여러명의 개발자가 코드를 효율적으로 작성해서 프로젝트를 완성시키기 위한 방법
2. Class란 : 변수와 함수를 묶어놓은 개념이라고 간단하게 생각
Class의 사용 방법:
- 변수와 함수가 들어있는 Class를 선언
- Class를 객체로 만들어서 Class안에 선언된 변수와 함수를 사용한다.
- 변수와 함수가 들어있는 Class를 선언
- Class를 객체로 만들어서 Class안에 선언된 변수와 함수를 사용한다.
#1. Class의 선언 - 계산기 class
class Calculator:
#변수 작성
num1 = 1
num2 = 2
#함수 작성
def plus(self): # class에 선언되는 모든 함수는 가장 첫번째 parameter로 self를 사용한다
return self.num1 + self.num2
def minus(self):
return self.num1-self.num2
self란 객체 자신을 말한다.
1) class의 사용
Class를 객체로 만들어서 Class안에 선언된 변수와 함수를 사용한다.
calc = Calculator() #Class명() 을 해 줍니다. calc: Class가 있는 calc객체가 선언된다.
calc #결과: <__main__.Calculator at 0x110431d10>
2) 객체 내의 변수 확인
#2. 객체 내의 변수 확인
calc.num1, calc.num2, calc.plus() #실행 결과: (1, 2, 3)
3. 생성자: Class가 객체로 만들어질 때 객체에 선언된는 변수를 설정하는 방법
Class가 변수(재료)를 추가할 때 사용된다. 그래서 주로 생성자 함수 안에, 사용할 변수를 선언한다.
위의 Class Calculator 는 객체 생성 후 바로(무조건) num1=1, num2=2가 생성되어 나중에 객체 선언할 때 수정까지 해 주어야한다.
그래서 위와 달리, 객체 선언시에 변수를 만들어주고 싶다하면, 생성자인 __init__ 를 사용한다.
1) 생성자(__init__)의 사용
def __init__(self, num1, num2):
self.num1 = num1
self.num2 = num2
그래서 다시 class Calculator을 생성자를 사용해서 위의 코드를 바꿔보면 아래와 같이 쓸 수 있다.
# 3. 생성자(__init__)을 사용하여 객체 선언시에 변수 생성
class Calculator:
def __init__(self, num1, num2): #def__init__(self, num1, num2=10): 처럼 default 값도 쓸 수 있고 *args, **kargs 도 사용가능
self.num1 = num1
self.num2 = num2
def plus(self):
return self.num1 + self.num2
def minus(self):
#pass
return self.num1 - self.num2
calc2 = Calculator(3, 6)
calc2.plus() #실행결과: 9
예제)
# Class 예제 : 스타크래프트의 마린을 클래스로 설계
# 체력(health), 공격력(attack_pow), 공격(attack())
# 마린 클래스로 마린 객체 2개를 생성해서 마린1이 마린2를 공격하는 코드를 작성해보시오
class Marine:
def __init__(self, health, attack_pw):
self.health = health
self.attack_pw = attack_pw
def attack(self, you):#나(self)가 상대방(you)를 공격한다.
return you.health - self.attack_pw
#객체 선언
my_marine = Marine(40,5)
your_marine = Marine(40,5)
my_marine.attack(your_marine)
my_marine.health, your_marine.health #실행결과: 40, 35
4. 상속: Class의 기능을 그대로 가져다가 기능을 수정/추가 할 때 사용하는 방식
1) 사용 방법: class 클래스 명(상속 받을 것)
#4. 상속
class Calculator:
def __init__(self, num1, num2): #def__init__(self, num1, num2=10): 처럼 default 값도 쓸 수 있고 *args, **kargs 도 사용가능
self.num1 = num1
self.num2 = num2
def plus(self):
return self.num1 + self.num2
def minus(self):
#pass
return self.num1 - self.num2
#Calculator2는 Calculator에 곱하기 기능을 추가한 것이라면,
class Calculator2(Calculator): # class 클래스 명(상속 받을 것)
#이러면 Calculator기능이 다 들어오고
#추가/수정 할 것만 여기에 적어주면 된다.
#추가
def mul(self):
return self.num1 * self.num2
#수정(재정의): 메서드 오버라이딩을 사용하여 수정한다.
def plus(self):
return self.num1 + self.num2 + 10
cal3 = Calculator2(2, 4)
cal3.plus(), cal3.minus(), cal3.mul() #실행 결과 16, -2, 8
5. Super: 부모 class에서 사용된 함수의 코드를 다시 자식의 class에서 재사용할 때 사용되는 것
바로 위의 코드에서 잠깐 상속해서 overriding해서 다시 수정해서 쓰는 것 까지 배워봤는데,
부모 class에서 사용된 함수의 코드를 다시 자식의 class에서 재사용할때 사용되는 것이 super이다.
1) 사용 방법:
class A:
def plus(self):
code1
class B(A):
def minus(self):
code1 # -> super().plus()
code2
이런 코드가 있다고 할때, 자식 class인 B에 부모 class의 code1이 중복(동일)하다. 이럴 때, 이를 code1로 쓰는게 아니라 super().plus() 라고 쓰면 A의 code1이 그대로 이 위지에 적용된다.
그리고 뭔가를 수정할 때도, class A에서 code1만 수정해주면 된다.
예를들어
이런 코드가 있을 때
class Marine:
def __init__(self):
self.health = 40
self.attack_pw = 5
def attack(self, unit):
unit.health -= self.attack_pw
if unit.health <= 0:
unit.health = 0
여기서 maxhealth를 추가하여 Marine2를 만들고 싶다면,
#여기서 maxhealth를 추가하여 Marine2를 만들고 싶다면,
class Marine2(Marine):
#수정: overriding
def __init__(self):
self.health = 40 #이거랑
self.attack_pw = 5 #이게 중복된다.
self.max_health = 40
#이를 super을 이용해서 대체해 줄 수 있다.
class Marine2(Marine):
def __init__(self):
super().__init__() #super().뭘 그대로 상속받을지
self.max_health = 40
my_marine = Marine2()
my_marine.health, my_marine.attack_pw, my_marine.max_health #실행 결과: 40, 5, 40
6. class의 getter, setter: 객체의 내부 변수에 접근할 때 특정 로직을 거쳐서 접근시키는 방법
class User:
def __init__(self, first_name, last_name):
self.first_name = first_name
self.last_name = last_name
def disp(self):
print(self.first_name, self.last_name)
이 코드가 적절하게 사용된다면 이런 식일 것이다.
user1 = User("andy","kim")
user1.first_name = "Jone"
user1.disp()#실행결과: Jone kim
그런데 실수로라도 이렇게 이름에 숫자를 적는 등의 적절하지 않게 사용할 수도 있다.
user1.last_name = 99
user1.disp()
#이럴 경우, 문자만 들어가야하는데 필터나 규제를 걸어놓지 않아서 임의의 형식이 들어갈 수 있게 되어놨다. 그래서,
# 실행결과: Jone 99
# 제약조건을 걸어주어 특정 함수의 로직을 거쳐서 변수를 넣어주는겍 getter, setter이다.
class User:
def __init__(self, first_name):
self.first_name = first_name
def setter(self, first_name): #저장시
#setter에 제약조건 달아주기:
if len(first_name) >= 3: #first_name은 3글자 이상이어야해~ 안그럼 에러다~
self.first_name = first_name
else:
print("error")
def getter(self, first_name): #출력시
print("getter")
return self.first_name
def disp(self):
print(self.first_name, self.last_name)
name = property(getter, setter) #property: getter, setter에 print처럼 접근할 수 있도록 도와주는 함수
user1 = User("Grace")
#getter함수 실행
user1.name = "Grace" #setter함수 실행 #good!
user1.name = "Jo" #에러 발생
7. is a & has a : 클래스를 설계하는 개념
- A is a B
- A는 B이다. 상속을 이용해서 클래스를 만드는 방법
#class 사람: 이름, 이메일, 정보출력()을 is a 로 만들기
class Person():
def __init__(self, name, email):
self.name = name
self.email = email
class Person2(Person):
def info(self):
print(self.name, self.email)
p = Person2("Grace", "hey@gmail.com")
p.info() #실행결과: Grace hey@gmail.com
- A has a B
- A는 B를 가진다. A가 B객체를 가지고 클래스를 만드는 방법
#class 사람: 이름, 이메일, 정보출력()을 has a 로 만들기
class Name:
def __init__(self, name):
self.name_str = name
class Email:
def __init__(self, email):
self.email_str = email
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)
my_name = Name("Grace")
my_email = Email("he__y@gmail.com")
p = Person(name, email)
p.info() #실행 결과: Grace he__y@gmail.com
'test' == 'test' #문자열 객체와의 비교 방법1. == 쓰기
#실행결과: True
'test'.__eq__('test') #문자열 객체와의 비교 방법2. __eq__ magic method
#실행결과: True
1+2, #이건 3,
"1"+"2" #이건 '12'
#이 둘이 다르게 작동하는 이유는, +는 int 객체에 있는 add함수와, str 객체에 있는 add함수가 다르게 작동하기 때문이다.
#직접 class 만들어보자.
class Txt:
def __init__(self, txt):
self.txt= txt
t1 = Txt("python")
t2 = Txt("python2")
t3 = t1
t1==t2, t1==t3, t2==t3
# 실행 결과 False, True, False
9. 클래스 변수 인스턴스 변수
# 클래스 구조
# 구조 설계 후 재사용성 증가, 코드 반복 최소화, 메소드 활용
class Student():
# 해당 class에 대한 설명을 """로 설명을 달아두는 것이 좋다.
"""
Student Class
Author: Grace Chung
Date: 2021.02.17
"""
#클래스 변수
student_count = 0
def __init__(self, name, number, grade, details):
#인스턴스 변수
self._name = name
self._number = number
self._grade = grade
self._details = details
#클래스명을 기반으로 클래스 변수에 접근
Student.student_count += 1
def __str__(self):
return 'str: {}'.format(self._name)
#클래스 메소드
def detail_info(self):
print('Current Id: {}'.format(id(self)))
print('Student Detail Info : {} {} {}'.format(self._name, self._details))
def __del__(self):
Student.student_count -= 1
stud1 = Student('Kim', 1, 1, {'gender': 'Male', 'score1': 95, 'score2': 100})
stud2 = Student('Kim', 2, 2, {'gender': 'Female', 'score1': 90, 'score2': 80})
stud3 = Student('Lee', 3, 4, {'gender': 'Female', 'score1': 70, 'score2': 60})
print(stud1) #실행결과: str: Kim (__str__을 안해줬다면 <__main__.Student object at 0x00001D3B1110898> 이렇게 객체형식으로 나온다.)
print(stud1.__dict__) #실행결과: {'Kim', 1, 1, {'gender': 'Male', 'score1': 95, 'score2': 100}}
# ID 확인
print(id(stud1))
print(id(stud2))
print(stud1._name == stud2._name)
print(stud1 is stud2)
# dir & __dict__의 사용
print(dir(stud1))
print(stud1.__dict__)
# Doctring의 사용
print(Student.__doc__)
# 실행
stud1.detail_info()
# 또는 class로 직접 접근하여서 메소드를 실행시킬 수도 있다. Student.detail_info(stud1)
# 해당 인스턴스의 클래스 확인하기
print(stud1.__class__) #실행결과: <class '__main__.Student'>
# 현업에서는 인스턴스 변수에 직접 접근하는 것을 PEP 문법적으로 권장하지 않는다.
stud1._name = 'Park'
print(stud1._name) #이렇게 stud1의 인스턴스 변수인 _name에 직접 접근 하여 수정 것을 권장하지 않는다.
# 그래서 권장하는 것은 캡슐화
# 인스턴스 네임스페이스 없으면 상위에서 검색
# 즉, 동일한 이름으로 변수 생성 가능 (인스턴스 검색 후 --> 상위(클래스 변수, 부모 클래스 변수))를 찾는 순서니까.
'Programming Language > Python' 카테고리의 다른 글
Python Algorithm) Queue을 이용한 알고리즘 문제 (0) | 2021.01.05 |
---|---|
Python Algorithm) Stack을 이용한 알고리즘 문제 (0) | 2021.01.05 |
Python Algorithm) 3. k번째 큰 수 (0) | 2020.04.03 |
Python Algorithm) 2. k번째 작은 수 (0) | 2020.04.03 |
Python Algorithm) 1. k번째 약수 (0) | 2020.04.03 |