-Object Oriented Programming (OOP)
-객체를 기반으로 하는 프로그래밍으로, 먼저 객체를 만들고 객체 사이에 일어나는 일들을 구현한다.
-c언어(절차지향 프로그래밍) 이외의 대부분의 프로그래밍 언어는 객체 지향 언어이다.
객체란 데이터와 그 데이터를 처리하는 메서드를 묶어서 하나로 만든 개념이다.
-가변성: 리스트, 딕셔너리, 셋과 같은 가변 객체들은 값을 수정할 수 있다.
-불변성: 숫자, 문자열, 튜플 같은 불변 객체는 값을 수정할 수 없고, 값을 변경하면 새로운 객체가 생성된다.
클래스란 객체에 대한 속성과 기능을 코드로 구현한 것이다.
객체의 특성(property), 속성(attribute) --> 멤버 변수
객체가 하는 기능 --> 멤버 함수
-class 클래스이름으로 선언 (이름은 PascalCase = CarmelCase 방식으로)
-변수 = 클래스이름()로 호출, 인스턴스 생성
+)필요한 새로운 자료형을 만든다고 생각하면 편하다!
-클래스: 새로운 형식을 정의, 객체를 만드는 틀
-객체(인스턴스): 클래스로부터 만들어진 것
->하나의 클래스로 여러 객체를 생성 가능하며, 생성된 객체는 서로에게 영향을 주지 않는다.
<클래스 정의 및 객체 선언 예시>
class Car: #클래스 명 Car (PascalCase 방식으로 이름짓기)
model = ""
cc = 0 #(model 과 cc는 Car 라는 클래스의 속성, 초기 값 없음
car1 = Car() #인스턴스 생성
car1.model = "아반떼"
car1.cc = 1600 #객체이름.속성 형식으로 클래스의 속성에 접근
-메서드는 클래스 안에 정의된 함수
-메서드 선언 시 첫번째 매개변수로 self 필수 작성
-> 클래스를 인스턴스화 하지 않고 호출해서 사용하는 경우에는 필요 없음
->호출할 때 self에 값을 직접 넘겨주지 않는다. (자동 할당)
->self는 키워드가 아닌 식별자명이기에 다른 이름을 지정하는 것도 가능하나, 현재 객체 자신을 의미하기 때문에 대부분의 개발자가 관례상 self를 사용한다.
<클래스 함수(메서드) 정의 및 호출 예시>
class Car: #클래스 명 Car (PascalCase 방식으로 이름짓기)
model = ""
cc = 0 #(model 과 cc는 Car 라는 클래스의 속성, 초기 값 없음
def get_info(self): #메서드 정의 (self를 첫 매개변수로)
print(f"모델명: {model}, 배기량: {cc} cc")
car1 = Car() #인스턴스 생성
car1.model = "아반떼"
car1.cc = 1600
car1.get_info() #클래스 함수 호출
def init(self, ...): 생성자 (Constructor, 초기자).
클래스를 생성할 때의 명령어 집합. 리턴 값이 없다.
-> 생성자는 필수가 아니다. (E.g. 객체에 초기화가 필요 없는 경우)
defstr(self): 문자열을 return하는 함수로, 객체의 정보를 담고 있다.
<특수 메서드 코드 예시>
class Car:
def__init__(self, model, year):
self.model = model
delf.year = year
def__str__(self):
return f"모델명: {self.model}, 연식: {self.year}년형"
car1 = Car("BMW", 2024) #인스턴스 생성
print(car1) # def__str__(self) 에서 정의된 함수를 호출
class Four:
def __init__(self, first, second): #첫번째 수와 두번째 수를 초기값으로 받는 클래스 Four를 설정
self.first = first
self.second = second
def my_add(self):
return self.first + self.second
#def my_add(self,num):
# return self.first + self.second+num
def my_sub(self):
return self.first - self.second
def my_mul(self):
return self.first * self.second
def my_div(self):
if self.second == 0:
return "분모가 0이 될 수 없습니다"
#항상 오류 처리 조건은 위에 쓰기!
#리턴은 여기서 함수를 끝낸다는 의미라 굳이 else를 안 써도 여기서 함수 호출이 종료된다.
return self.first / self.second
num1 = Four(20, 5)
print(f" first num: {num1.first}, second num: {num1.second}")
print(f"Addition: {num1.my_add()}")
print(f"Subtraction: {num1.my_sub()}")
print(f"Multiplication: {num1.my_mul()}")
print(f"Division: {num1.my_div()}")
-특정 인스턴스에만 영향을 미치며, 객체마다 독립적으로 유지
-각 객체별로 고유한 데이터를 저장할 때 사용
-접근 방법: 인스턴스명.변수명
-해당 클래스를 사용하는 모두에게 공용으로 사용되는 변
-값을 공유하고 유지하는 특성을 가짐
-클래스 변수의 값을 변경하면, 해당 클래스의 모든 인스턴스에서 값이 변경
-접근 방법: 클래스명.변수명
#사번 자동 발급
class Employee:
serial_num = 1000 #클래스 변수
def __init__(self, name):
#클래스 변수 1씩 증가
Employee.serial_num += 1 #인스턴스 변수에만 self 쓰고 클래스 변수에는 self 쓸 필요 없음
self.id = Employee.serial_num #사번
self.name = name
def __str__(self) -> str:
return f"사번 : {self.id}, 이름 : {self.name}"
e1 = Employee("홍길동")
print(e1)
e2 = Employee("임꺽정")
print(e2)
employees = [Employee("이몽룡"), Employee("심청이"), Employee("변사또"), Employee("전우치")]
for employee in employees:
print(employee)
<출력 결과>
사번 : 1001, 이름 : 홍길동
사번 : 1002, 이름 : 임꺽정
사번 : 1003, 이름 : 이몽룡
사번 : 1004, 이름 : 심청이
사번 : 1005, 이름 : 변사또
사번 : 1006, 이름 : 전우치
-클래스를 선언할 때, 인자로 location, name, product, customer 받기
(location : 위치, name : 가게 이름, product : 파는 물건, customer : 고객의 수)
-print_location() : 위치를 출력하는 함수
-change_category() : 받은 인자로 파는 물건 바꾸는 함수
-show_list() : 현재 파는 물건 출력
-enter_customer() : 손님의 수를 1씩 늘리는 함수
-show_info() : 가게 이름, 위치, 파는 물건, 손님 수 모두 출력
class Supermarket: #클래스 선언
total_customer = 0 # 클래스 변수
def __init__(self, location, name, product, customer):
self.location = location
self.name = name
self.product = product
self.customer = customer
Supermarket.total_customer += customer #클래스 변수 값 증가
def print_location(self): #위치를 출력하는 함수
print(f"위치 : {self.location}")
def change_category(self, product): #받은 인자로 파는 물건을 바꾸는 함수
self.product = product
def show_list(self): #현재 파는 물건 출력
print(f"상품 : {self.product}")
def enter_customer(self): #손님 수를 1씩 늘리는 함수
self.customer += 1
Supermarket.total_customer += 1
def show_info(self): #가게 이름, 위치, 파는 물건, 손님 수 모두 출력
print(f"가게 이름: {self.name}")
print(f"위치: {self.location}")
print(f"상품: {self.product}")
print(f"가게의 손님 수: {self.customer}")
print(f"총 손님 수: {Supermarket.total_customer}\n\n\n")
sup1 = Supermarket("마포구 염리동", "마켓온", "음료", 12)
sup2 = Supermarket("마포구 신설동", "마켓플리", "라면", 7)
sup1.show_info()
sup2.show_info()
sup1.enter_customer()
sup1.enter_customer()
sup1.enter_customer()
sup1.enter_customer() #손님 4명 증가
sup1.show_info()
sup2.show_info()
<실행 결과>
가게 이름: 마켓온
위치: 마포구 염리동
상품: 음료
가게의 손님 수: 12
총 손님 수: 19
가게 이름: 마켓플리
위치: 마포구 신설동
상품: 라면
가게의 손님 수: 7
총 손님 수: 19
----마켓온에 손님 4명 추가 이후-----
가게 이름: 마켓온
위치: 마포구 염리동
상품: 음료
가게의 손님 수: 16
총 손님 수: 23
가게 이름: 마켓플리
위치: 마포구 신설동
상품: 라면
가게의 손님 수: 7
총 손님 수: 23
-데이터를 하나의 단위로 묶고, 외부로부터의 직접 접근을 제한
-필요한 경우 메서드를 통해 데이터를 제어하도록 하는 프로그래밍 기법
-데이터 보호와 모듈화 목적
-파이썬에서는 변수 이름 앞에 밑줄(_ 또는 __)을 붙여 접근 제한을 표현
-파이썬은 기본적으로 Public! 따라서 엄격한 의미의 정보 은닉은 없지만, 접근 제한자를 이용해 표현한다.
Public: 어디서나 접근 가능
Private: 해당 클래스 내에서만 접근 가능
-> 해당 멤버 앞에 __(double underscore)를 붙여서 표시
Protected: 보통은 해당 클래스 & 하위 클래스 내에서만 접근 가능이라는 의미.
->해당 멤버 앞에 _(single underscore)를 붙여서 표시
파이썬에서는 실제로 제약되지 않고, 경고 표시로만 사용된다.
정보 은닉은 캡슐화로 인해 달성되는 결과이다.
-> getter와 setter라는 함수 생성
set + 변수이름(): 정보를 설정할 때
get + 변수이름(): 정보를 가져올 때
<정보 은닉 (getter와 setter 활용) 예시 코드>
class Person:
def __init__(self) -> None: #생성자
self._name = "" #접근 제한자, protected.
self._age = 0 #외부에서 접근하지 않을 거라는 의미!
#이름을 설정
def setname(self, name): #setter
self._name = name
#이름을 출력
def getname(self): #getter
return self._name
#나이를 설정
def setage(self, age):
self._age = age
#나이를 출력
def getage(self):
return self._age
p1 = Person()
p1.setname("홍길동")
print(p1.getname())
p1.setage(20)
print(p1.getage()) #정보 은닉을 위함
-운동을 하면 체력이 1증가하고(hp += 1), 술을 마시면 체력이 1감소함 (hp -= 1)
-건강 상태 : hp로 설정 , hp의 범위 : 1 ~ 100
# 실습. 건강상태 클래스 만들기
class Health:
def __init__(self, name): #생성자
self._hp = 100 #접근 제한자, protected
self._name = "" # 초기값
def get_name(self): #getter
return self._name
def set_name(self, value): #setter
self._name = value
def set_hp(self, value): #setter
self._hp = value
if self._hp >= 100: #hp가 100 이상이 될 경우 100으로 값 수정해주기
self._hp = 100
else:
self._hp = value
def get_hp(self): #getter
return self._hp
def exersise(self, hour):
self.set_hp(self._hp + hour)
print(f"{hour}시간 운동하다")
def drink(self, cups):
self.set_hp(self._hp - cups)
print(f"술을 {cups}잔 마셨다")
def info(self):
print(f"{self.get_name()} - hp: {self.get_hp()}")
p1 = Health("나몸짱")
# p1.set_hp(70)
p1.exersise(5)
p1.drink(2)
p1.info()
p2 = Health("나약해")
p2.set_hp(20)
p2.exersise(1)
p2.drink(10)
p2.info()
->파이썬에서는 @property라는 데코레이터를 이용하여 이를 정의할 수 있다.
(여기서 데코레이터란? 기존 함수나 메서드의 동작을 수정하거나 확장할 때 사용한다.)
-getter 함수에는 @property
-setter 함수에는 @gettername.setter
<데코레이터 사용 예시 코드>
# getter, setter 데코레이터
class Person: #정보 은닉하는 사람 클래스
def __init__(self, name, age): #생성자(conductor)
self.__name = name #접근 제한자, private
self.__age = age
#getter
@property #바로 위에 있어야 함. 한 줄이라도 밑에 띄워 두면 함수를 인식하지 못함!
def name(self):
return self.__name
#setter
@name.setter
def name(self, value):
self.__name = value
#getter
@property
def age(self):
return self.__age
#setter
@age.setter
def age(self, value):
self.__age = value
p1 = Person("홍길동", 28)
print(p1.name) #name getter 호출
print(p1.age) #age getter 호출
p1.name = "이몽룡" #name setter 호출
p1.age = 25 #age setter 호출 -> p1의 값들 변경
print(p1.name) #name getter 호출
print(p1.age) #age getter 호출
<실행 결과>
홍길동
28
이몽룡
25
실질적으로 파이썬 코드의 경우 정보 은닉이 엄격하게 되지 않기 때문에 getter와 setter의 개념을 이해하는 것이 어려웠다.
이 후 이어질 클래스 활용 예시를 보며 이해를 해보자!