Python의 클래스(Class)는 객체지향 프로그래밍의 핵심 개념으로, 관련된 데이터(속성)와 기능(메서드)을 하나로 묶어서 정의하는 템플릿 역할을 합니다. 클래스를 통해 실제 객체(인스턴스)를 생성할 수 있으며, 각 인스턴스는 클래스에서 정의된 속성과 메서드를 가지면서도 독립적인 상태를 유지한다. Python에서는 class 키워드로 클래스를 정의하고, init 메서드를 통해 객체 초기화를 수행하며, self 매개변수를 사용해 인스턴스 자신을 참조한다. 클래스를 사용하면 코드의 재사용성을 높이고, 복잡한 프로그램을 더 체계적으로 구조화할 수 있으며, 상속을 통해 기존 클래스의 기능을 확장하거나 수정할 수 있습니다. 실무에서는 데이터 모델링, API 설계, 게임 개발 등 다양한 분야에서 클래스를 활용하여 현실 세계의 개념을 코드로 모델링한다.
클래스 정의 (Class Definition)
클래스는 class 키워드를 사용하여 정의하며, 클래스명은 일반적으로 PascalCase(첫 글자 대문자) 규칙을 따른다. 클래스 내부에는 속성과 메서드를 정의할 수 있으며, 들여쓰기를 통해 클래스의 범위를 구분한다. 클래스는 객체를 생성하기 위한 템플릿 역할을 하며, 실제 메모리에 할당되는 것은 클래스로부터 생성된 인스턴스다.
속성 (Attributes)
속성은 클래스가 가지는 데이터나 상태를 나타내며, 인스턴스 속성과 클래스 속성으로 구분된다. 인스턴스 속성은 각 객체마다 독립적으로 존재하는 데이터로 self.속성명 형태로 정의하며, 객체마다 다른 값을 가질 수 있다. 클래스 속성은 모든 인스턴스가 공유하는 데이터로 클래스 내부에 직접 정의하며, 클래스명으로 접근할 수 있다.
메서드 (Methods)
메서드는 클래스 내부에 정의된 함수로, 객체의 행동이나 기능을 구현한다. 모든 인스턴스 메서드는 첫 번째 매개변수로 self를 받아야 하며, 이를 통해 해당 인스턴스의 속성과 다른 메서드에 접근할 수 있다. 메서드는 인스턴스 메서드, 클래스 메서드(@classmethod), 정적 메서드(@staticmethod)로 구분되며, 각각 다른 용도와 접근 방식을 가진다.
생성자 (init)
init 메서드는 클래스의 생성자로, 객체가 생성될 때 자동으로 호출되어 인스턴스를 초기화한다. 이 메서드를 통해 객체 생성 시 필요한 초기값을 설정하고, 인스턴스 속성을 정의할 수 있다. 생성자는 매개변수를 받을 수 있어 객체 생성 시 필요한 데이터를 전달받을 수 있으며, 객체의 초기 상태를 설정하는 중요한 역할을 한다.
self 키워드
self는 클래스의 인스턴스 자신을 가리키는 특별한 매개변수로, 모든 인스턴스 메서드의 첫 번째 매개변수로 사용된다. self를 통해 해당 인스턴스의 속성에 접근하거나 수정할 수 있으며, 같은 클래스의 다른 메서드를 호출할 수도 있다. Python에서는 메서드 호출 시 self를 자동으로 전달하므로 명시적으로 전달할 필요는 없지만, 메서드 정의 시에는 반드시 포함해야 한다.
특수 메서드 (Magic Methods / Dunder Methods)
특수 메서드는 로 시작하고 끝나는 메서드들로, Python의 내장 함수나 연산자와 상호작용할 수 있게 해준다. 대표적으로 __str(문자열 표현), repr(개발자용 표현), len(길이), eq(동등 비교) 등이 있습니다. 이러한 메서드들을 정의하면 사용자 정의 클래스도 Python의 내장 타입처럼 자연스럽게 사용할 수 있다.
상속 (Inheritance)
상속은 기존 클래스(부모 클래스)의 속성과 메서드를 새로운 클래스(자식 클래스)가 물려받는 메커니즘이다. 자식 클래스는 부모 클래스의 모든 기능을 사용할 수 있으며, 추가적인 기능을 정의하거나 기존 기능을 재정의(오버라이딩)할 수 있습니다. Python은 다중 상속을 지원하여 여러 부모 클래스로부터 기능을 상속받을 수 있으며, super() 함수를 통해 부모 클래스의 메서드를 호출할 수 있다.
캡슐화 (Encapsulation)
캡슐화는 클래스 내부의 데이터와 메서드를 외부로부터 보호하는 개념으로, Python에서는 언더스코어(_)를 사용하여 접근 제한을 나타낸다. 단일 언더스코어(_attribute)는 내부용 속성임을 의미하는 관례이고, 이중 언더스코어(__attribute)는 이름 맹글링을 통해 외부 접근을 어렵게 만든다. 완전한 private는 아니지만 이러한 규칙을 통해 클래스의 내부 구현을 숨기고 인터페이스를 명확히 할 수 있다.
프로퍼티 (Properties)
프로퍼티는 메서드를 속성처럼 사용할 수 있게 해주는 기능으로, @property 데코레이터를 사용하여 정의한다. getter, setter, deleter를 정의하여 속성에 대한 접근을 제어할 수 있으며, 속성 값의 유효성 검사나 계산된 속성을 구현할 때 유용하다. 이를 통해 단순한 속성 접근처럼 보이지만 내부적으로는 복잡한 로직을 수행할 수 있다.
클래스 변수와 인스턴스 변수
클래스 변수는 모든 인스턴스가 공유하는 변수로 클래스 레벨에서 정의되며, 클래스명이나 인스턴스를 통해 접근할 수 있다. 인스턴스 변수는 각 객체마다 독립적으로 존재하는 변수로 주로 init 메서드에서 self.변수명 형태로 정의된다. 클래스 변수는 모든 인스턴스가 같은 값을 공유하는 반면, 인스턴스 변수는 각 객체마다 다른 값을 가질 수 있어 객체의 개별 상태를 나타낸다.
# 1. 클래스 정의 (Class Definition)
# - 모든 인스턴스가 공유할 '클래스 변수'를 정의합니다.
class Vehicle:
# 10. 클래스 변수: 모든 Vehicle 객체가 공유하는 데이터
category = "탈것"
# 4. 생성자 (__init__)와 5. self 키워드
# - 객체 생성 시 호출되며, '인스턴스 변수'를 초기화합니다.
def __init__(self, make, model):
# 10. 인스턴스 변수: 각 객체마다 독립적인 데이터
self.make = make
self.model = model
# 8. 캡슐화 (Encapsulation): _는 내부용(protected) 속성임을 나타내는 관례
self._speed = 0
# 3. 메서드 (Methods)
def accelerate(self, value):
self._speed += value
def brake(self, value):
self._speed -= value
if self._speed < 0:
self._speed = 0
# 6. 특수 메서드 (Magic Methods)
# - print(객체)를 호출할 때 이 메서드가 반환하는 문자열이 출력됩니다.
def __str__(self):
return f"[{self.make} {self.model}] - 현재 속도: {self._speed}km/h"
# 7. 상속 (Inheritance)
# - Vehicle 클래스의 모든 속성과 메서드를 Car 클래스가 물려받습니다.
class Car(Vehicle):
# 생성자 재정의 (Overriding)
def __init__(self, make, model, license_plate):
# super()를 통해 부모 클래스(Vehicle)의 생성자를 호출
super().__init__(make, model)
# 8. 캡슐화: __는 외부 접근을 어렵게 만드는 private 속성
self.__license_plate = license_plate
# 9. 프로퍼티 (Properties)
# - 메서드를 속성처럼 접근할 수 있게 해줍니다. (Getter)
@property
def speed(self):
return self._speed
# - 'speed' 속성에 값을 할당할 때 호출되는 메서드 (Setter)
@speed.setter
def speed(self, value):
print("(속도 설정 시도)")
if value < 0:
print("경고: 속도는 음수가 될 수 없습니다.")
self._speed = 0
else:
self._speed = value
def get_license_plate(self):
"""private 속성에 접근하기 위한 public 메서드"""
return self.__license_plate
# --- 코드 실행 및 개념 확인 ---
if __name__ == "__main__":
# 2. 속성 (Attributes) - 객체(인스턴스) 생성
my_car = Car("현대", "아이오닉 5", "12가 3456")
# 클래스 변수와 인스턴스 변수 접근
print(f"분류: {Car.category}") # 클래스명으로 클래스 변수 접근
print(f"제조사: {my_car.make}") # 인스턴스 변수 접근
print("-" * 20)
# 메서드 호출
my_car.accelerate(80)
print(my_car) # __str__ 특수 메서드 호출
print("-" * 20)
# 프로퍼티 사용
my_car.speed = -10 # Setter(@speed.setter) 호출 -> 유효성 검사
print(f"현재 속도 (Getter): {my_car.speed}") # Getter(@property) 호출
print("-" * 20)
# 캡슐화 확인
print(f"내부용(protected) 속도 변수 직접 접근: {my_car._speed}")
# print(my_car.__license_plate) # AttributeError: __로 시작하는 private 변수는 직접 접근 불가
print(f"메서드를 통한 private 번호판 접근: {my_car.get_license_plate()}")