13. 파이썬의 객체지향과 클래스
1. 객체지향 프로그래밍
- 객체지향 프로그래밍(Object-Oriented Programming, OOP)은 소프트웨어를 설계하고 구현하는 데 사용
- 프로그램을 '객체'라고 불리는 독립적인 개체로 나누고, 상호작용을 통해 프로그램을 구성하는 개발 방법론
2. 클래스
- 객체(Object) : 객체는 현실 세계에서의 실제 개체나 추상적인 개념을 모델링한 것입니다. 예를 들어, 자동차, 사람, 은행 계좌 등이 객체가 될 수 있습니다. 객체는 데이터(속성, 상태)와 메서드(동작, 함수)로 구성됩니다.
- 클래스(Class) : 클래스는 객체를 만들기 위한 템플릿 또는 설계도입니다. 클래스는 객체의 공통 속성과 동작을 정의하며, 객체를 생성하는데 사용됩니다. 예를 들어, "자동차" 클래스는 모든 자동차 객체가 가져야 하는 속성(색상, 속도)과 메서드(주행, 멈춤)를 정의할 수 있습니다.
- 인스턴스(Instance) : 클래스를 기반으로 실제로 생성된 객체를 인스턴스라고 합니다. 클래스는 여러 인스턴스를 생성할 수 있으며, 각 인스턴스는 독립적인 데이터와 메서드를 가집니다.
2-1. 클래스 만들기
# 기본형태
class 클래스이름:
# 클래스 속성(멤버 변수) 정의
속성1 = 초기값1
속성2 = 초기값2
# 생성자 메서드 (생략 가능)
def __init__(self, 매개변수1, 매개변수2, ...):
# 인스턴스 속성 초기화
self.속성1 = 매개변수1
self.속성2 = 매개변수2
# 메서드(멤버 함수) 정의
def 메서드1(self, 매개변수1, 매개변수2, ...):
# 메서드 동작 정의
pass
def 메서드2(self, 매개변수1, 매개변수2, ...):
# 메서드 동작 정의
pass
# class만 모이는 쪽으로 메모리가 올라감
# class를 불러서 변수만 쓸 수 있음
2-2. 객체(인스턴스) 생성
class Dog:
pass
Rucy = Dog() # 메모리에 Rucy라는 변수가 생기고 Dog()이라는 객체를 가르키게 됨
print(Rucy)
print(type(Rucy))
PPomi = Dog()
print(PPomi)
print(type(PPomi)) # 같은 Dog()를 하나 더 만들어서 가르키게 됨
2-3. 객체 속성 초기화
class Dog:
name = ''
age = 0
family = ''
Rucy = Dog()
print(Rucy.name)
print(Rucy.age)
print(Rucy.family)
Rucy.name = '루시'
Rucy.age = 14
Rucy.family = '포메'
print(Rucy.name)
print(Rucy.age)
print(Rucy.family)
PPomi = Dog()
PPomi.name = '뽀미'
PPomi.age = 7
PPomi.family = '폼피츠'
print(PPomi.name)
print(PPomi.age)
print(PPomi.family)
# class 는 붕어빵 틀
# 모양은 같지만 서로 다르게 만들어짐
2-4. 메서드의 사용
class Dog:
name = ''
age = 0
family = ''
def eat(self):
print('사료를 먹습니다.')
# 함수는 객체가 생길때마다 생기지 않고, 메모리에 하나만 생성되고 그 함수를 객체들이 공유함
# self는 메모리 주소를 전달하기 위해 씀. Rucy가 불렀냐 PPomi가 불렀냐의 구분
# (꼭 self로 안 적어도 됨)
Rucy = Dog()
Rucy.eat()
PPomi = Dog()
PPomi.eat()
3. 생성자
- 클래스의 인스턴스가 생성될 때 자동으로 호출되는 특별한 메서드
- 생성자는 객체의 초기화를 담당하며, 객체가 생성될 때 필요한 속성을 초기화하고 설정하는 역할
- 생성자 메서드는 __init__라고 이름이 정해져 있음
# 기본형태
class 클래스이름:
def __init__(self, 매개변수1, 매개변수2):
self.속성1 = 매개변수1
self.속성2 = 매개변수2
# 만들면 자동호출, 만들지 않으면 내부적으로 빈 생성자를 만듬
class Dog:
def __init__(self):
print(self, 'init 호출!')
Rucy = Dog()
class Dog:
def __init__(self):
print(self, 'init 호출!')
self.name = ''
self.age = 0
family = '포메'
self.temp = family # self를 붙여야 접근 가능
Rucy = Dog()
print(Rucy)
print(Rucy.name)
print(Rucy.age)
# print(Rucy.family)
print(Rucy.temp)
class Dog:
def __init__(self, name, age, family='족보없음'):
self.name = name
self.age = age
self.family = family
# 생성자에 전달할 파라미터(변수)
Rucy = Dog('루시', 14, '포메') # '족보없음'은 안 쓰임
print(Rucy.name)
print(Rucy.age)
print(Rucy.family)
Pomi = Dog('포미', 5) # family 기본값 '족보없음'
print(Pomi.name)
print(Pomi.age)
print(Pomi.family)
4. 메서드
- 클래스 내부에 정의되어 특정 객체에 연결된 함수
- 메서드는 해당 클래스의 모든 객체에서 공유되며, 객체의 동작을 정의하거나 특정 작업을 수행하는 데 사용
4-1. 메서드 정의
class Counter:
# 생성자
def __init__(self):
self.num = 0
# 메소드
def increment(self): # 번호 증가
self.num += 1 # self를 붙였기 때문에 다른 함수에 있는 변수를1
# 가져다 쓸 수 있음
def current_value(self): # 현재 번호
return self.num
def decrement(self): # 번호 감소
self.num -= 1
def reset(self): # 0으로 초기화
self.num = 0
KBbank = Counter()
print(KBbank.num) # 0
print(KBbank.current_value()) # 0
KBbank.increment() # 손님이 세명 왔다
KBbank.increment()
KBbank.increment()
print(KBbank.num) # 3
print(KBbank.current_value()) # 3
KBbank.decrement() # 손님이 한명 빠졌다
print(KBbank.num) # 2
print(KBbank.current_value()) # 2
# 메소드는 여러 방어코드를 넣을 수 있어서 값을 넣거나 뺄 때
# 값(생성자)에다 직접 넣는 것이 아니라 메소드로 만들어서 처리하는 것이 좋다
HanaBank = Counter()
print(HanaBank.current_value()) # KBbank 와는 관련 없음
HanaBank.increment()
HanaBank.increment()
HanaBank.increment()
HanaBank.increment()
HanaBank.increment()
print(HanaBank.current_value()) # 5번 증가
HanaBank.reset()
print(HanaBank.current_value()) # 리셋 0
4-2. 메서드 타입
- 인스턴스 메서드(Instance Method) : 객체의 상태를 조작하거나 객체에 특정 작업을 수행하는 메서드, 대부분의 클래스 메서드는 인스턴스 메서드, 위의 예제에서 보여진 __init__ 메서드도 인스턴스 메서드.
- 클래스 메서드(Class Method) : 클래스 레벨에서 동작하며, 모든 인스턴스가 공유하는 메서드, 클래스 메서드 @classmethod 데코레이터를 사용하여 정의하며, 첫 번째 매개변수로 cls를 사용.
- 정적 메서드(Static Method) : 특정 클래스나 인스턴스와 관련이 없는 메서드로, 클래스 내부에 정의되어 있지만 클래스나 인스턴스와 독립적으로 호출될 수 있습니다. 정적 메서드는 @staticmethod 데코레이터를 사용하여 정의.
class Math:
def add(self, x, y):
return x + y
def multiply(self, x, y):
return x * y
math = Math()
result1 = math.add(10, 3)
print(result1)
result2 = math.multiply(10, 3)
print(result2)
# 굳이 객체로 만들어야 하나? 바로 함수로 쓰면 되지 않겠냐
# 함수는 타이틀이 없어서 개별적으로 외워야함
# 클래스 메서드
class Math:
str1 = 'Hello! '
@classmethod # classmethod라고 불리는 메소드가 됨
def add(cls, x, y): # 함수로 기능하는 것뿐아니라 미리 제공되어있는 기능을
return x + y # 전달받아서 변화가 일어남
# 바로 객체를 만들지 않아도 되는 메소드로 둔갑
@classmethod
def multiply(cls, x, y):
return x * y
@classmethod # class 메모리(Math)와 함수 메모리(hello)가 따로 저장되어 있음
def hello(cls, name): # cls. <- 을 붙여주면 class 메모리 주소에 있는
return cls.str1 + name # str1을 찾아감
print(Math.add(10, 3)) # math = Math() 할 필요가 없음
print(Math.multiply(10, 3))
print(Math.hello('김사과'))
# 정적 메서드
class Math:
def __init__(self, x, y):
self.x = x
self.y = y
def add(self):
return self.x + self.y
def multiply(self):
return self.x * self.y
@staticmethod # 같은 클래스에 있지만 add, multiply / subtract은
def subtract(x, y): # 서로 자료를 주고받을 수 없다
return x * y # 클래스에서 전혀 관련 없는 독립적인 메소드를 만들 때
# staticmethod를 쓴다
math = Math(10, 3) # add 메소드와 multiply 메소드는 객체를 생성해야 쓸 수 있음
print(math.add())
print(math.multiply())
Math.subtract(10, 3) # Math 클래스가 실행되는 동시에 subtract메소드가
# 따로 메모리에 올라감 (객체 생성 필요 없음)