
클래스를 정의하는 간단한 예제
class Person:
pass
Person이라는 이름의 클래스를 정의하는 구문
보통 클래스 이름은 대문자로 시작하는 ‘카멜 케이스(CamelCase)’로 작성하는 것이 관례 (예: Person, MyClass, StudentInfo 등)
파이썬에서 ‘아무것도 하지 않고 넘어간다’는 의미로 쓰이는 키워드.
우리는 아직 클래스를 비워 둘 것이므로 pass를 사용.
이렇게만 작성하면 아무런 속성(변수)과 메서드(함수)가 없는 완전히 빈 클래스된다.
클래스를 정의했다면, 이제 실제로 사용할 객체(인스턴스)를 만들 수 있다. 클래스를 “붕어빵 틀”이라고 생각한다면, 객체는 그 틀로부터 찍어낸 “붕어빵”과 비슷하다.
# Person 클래스를 정의한 후
class Person:
pass
# Person 클래스로부터 객체(인스턴스) 생성
person1 = Person()
person2 = Person()
print(person1) # <__main__.Person object at ...>
print(person2) # <__main__.Person object at ...>
이제 클래스 안에 실제로 동작할 수 있는 요소들을 만들어 봅시다. 파이썬 클래스에서 다룰 수 있는 요소는 크게 다음 두 가지가 있다.
class Person:
# 생성자(초기화 메서드)
def __init__(self, name, age):
self.name = name # 인스턴스 변수 name
self.age = age # 인스턴스 변수 age
# 메서드
def say_hello(self):
print(f"안녕하세요. 저는 {self.name}이고, {self.age}살이다.")
(1) init 메서드
init(이닛)는 클래스가 인스턴스화될 때(즉, Person()으로 객체를 생성할 때) 자동으로 호출되는 메서드이다.
(2) 메서드(say_hello)
클래스 내부에 정의된 일반적인 함수이다.
이제 완성한 Person 클래스를 실전에서 사용.
class Person:
def __init__(self, name, age):
self.name = name
self.age = age
def say_hello(self):
print(f"안녕하세요. 저는 {self.name}이고, {self.age}살입니다.")
# Person 클래스를 사용해 인스턴스(객체) 생성
person1 = Person("Alice", 30)
person2 = Person("Bob", 25)
# say_hello 메서드 호출
person1.say_hello() # 안녕하세요. 저는 Alice이고, 30살입니다.
person2.say_hello() # 안녕하세요. 저는 Bob이고, 25살입니다.
파이썬 클래스에는 클래스 변수와 인스턴스 변수가 있다.
class Person:
# 클래스 변수
species = "Homo sapiens" # 모든 'Person' 인스턴스가 공유
def __init__(self, name, age):
# 인스턴스 변수
self.name = name
self.age = age
def say_hello(self):
print(f"안녕하세요. 저는 {self.name}이고, {self.age}살입니다.")
print(f"저는 {Person.species} 종입니다.")
person1 = Person("Alice", 30)
person2 = Person("Bob", 25)
print(person1.species) # Homo sapiens
print(person2.species) # Homo sapiens
print(Person.species) # Homo sapiens
객체지향 프로그래밍에서는 캡슐화(encapsulation), 정보 은닉(information hiding) 이라는 개념 존재.
파이썬에서는 다른 언어(C++, Java 등)와 달리 특별한 키워드(private, public)가 없지만, 다음과 같이 접두사로 _(언더스코어)를 사용하여 “이 변수는 클래스 내부에서만 쓰는 것이 좋겠다”는 관례를 표시.
class BankAccount:
def __init__(self, owner, balance):
self.owner = owner
self._balance = balance # _balance : 외부에서 직접 건드리지 말라는 의미 (관례)
def deposit(self, amount):
self._balance += amount
def withdraw(self, amount):
if amount <= self._balance:
self._balance -= amount
else:
print("잔액이 부족합니다.")
def get_balance(self):
return self._balance
# 사용 예시
account = BankAccount("Alice", 1000)
account.deposit(500)
account.withdraw(300)
print(account.get_balance()) # 1200
# 굳이 account._balance 로 직접 접근하기보다, get_balance() 메서드를 이용하는 것이 권장됨.
이처럼 _balance 같은 “비공개로 하고 싶은” 속성의 직접 접근을 최소화하고, deposit, withdraw, get_balance 같은 메서드를 통해 간접적으로 조작하거나 조회하도록 설계.
클래스를 정의하다 보면, 기존에 만든 클래스를 확장해 사용하고 싶을 수 있다. 예를 들어 Person 클래스를 상속해서 학생(Student) 클래스를 만들면, Person의 속성과 메서드를 물려받아 재사용하고, 필요한 부분만 새로 추가할 수 있다.
class Person:
def __init__(self, name, age):
self.name = name
self.age = age
def say_hello(self):
print(f"안녕하세요. 저는 {self.name}이고, {self.age}살입니다.")
# Person 클래스를 상속하는 Student 클래스
class Student(Person):
def __init__(self, name, age, student_id):
# 부모 클래스의 __init__ 호출
super().__init__(name, age)
self.student_id = student_id
def study(self):
print(f"{self.name} 학생이 공부하고 있습니다.")
# 사용 예시
student1 = Student("Charlie", 20, "S12345")
student1.say_hello() # 안녕하세요. 저는 Charlie이고, 20살입니다.
student1.study() # Charlie 학생이 공부하고 있습니다.
class Student(Person):
Student 클래스는 Person 클래스를 상속받는다.
super().init(name, age)
부모 클래스의 생성자를 명시적으로 호출하여, Student 객체도 Person이 갖는 속성을 정상적으로 초기화할 수 있다.
이제 Student 객체는 Person이 가진 say_hello() 메서드도 그대로 사용할 수 있고, 추가로 study() 메서드 등 새로운 기능을 추가할 수 있다.
상속은 이미 정의해 놓은(부모) 클래스의 속성과 메서드를 “그대로” 물려받고, 필요한 기능만 추가하거나 변경할 수 있게 해 주는 기법.
아래 예시는 Person 클래스를 만들어 두고, 이를 상속받아 Student 클래스를 정의
class Person:
def __init__(self, name, age):
self.name = name
self.age = age
def say_hello(self):
print(f"안녕하세요. 저는 {self.name}이고, {self.age}살입니다.")
# Person 클래스를 상속하여 Student 클래스 정의
class Student(Person):
def __init__(self, name, age, student_id):
# 부모 클래스의 __init__() 호출
super().__init__(name, age)
self.student_id = student_id
def study(self):
print(f"{self.name} 학생이 공부하고 있습니다.")
# 사용 예시
student1 = Student("Charlie", 20, "S12345")
student1.say_hello() # 안녕하세요. 저는 Charlie이고, 20살입니다.
student1.study() # Charlie 학생이 공부하고 있습니다.
class Student(Person):
Student는 Person을 상속받습니다.
super().init(name, age)
부모 클래스 Person의 초기화 로직을 재사용(호출)하여 name, age를 세팅.
상속받은 자식 클래스(Student)는 부모 클래스(Person)의 메서드 say_hello를 그대로 사용하며, 추가로 study 라는 메서드를 갖는다.
파이썬은 다중 상속(한 클래스가 여러 부모 클래스를 동시에 상속받는 것)을 지원.
하지만 복잡도가 올라가기 때문에, 꼭 필요한 상황이 아니라면 단일 상속만으로도 충분한 경우가 많음.
class A:
pass
class B:
pass
class C(A, B): # A와 B를 동시에 상속
pass
다중 상속을 사용하면 메서드 탐색 순서(MRO, Method Resolution Order)가 다소 복잡.
MRO는 파이썬이 메서드를 찾기 위해 어떤 클래스부터 탐색하는지 순서를 정의한 규칙.
캡슐화는 객체가 내부에서 사용하는 데이터(속성)와 메서드를 외부에서 직접 건드리지 못하게 보호하고, 꼭 필요한 기능만 공개(Public)하는 것을 말한다.
C++이나 Java 같은 언어와 달리, 파이썬에는 private 혹은 protected 같은 접근 제한자 키워드가 없다. 하지만 네이밍 컨벤션(naming convention)을 통해 “이건 건드리지 않는 편이 좋다”고 암묵적으로 표시이다.
_balance처럼 단일 언더스코어로 시작하는 이름은, “외부에서 직접 접근하기보다는 내부용으로만 쓰는 게 좋다”는 관례이다.
__balance처럼 이중 언더스코어로 시작하면, 파이썬이 이름을 맹렬히 바꿔버리는(name mangling) 기법을 사용.
class BankAccount:
def __init__(self, owner, balance):
self.owner = owner
self._balance = balance # 내부용(관례)
def deposit(self, amount):
self._balance += amount
def withdraw(self, amount):
if amount <= self._balance:
self._balance -= amount
else:
print("잔액이 부족합니다.")
def get_balance(self):
return self._balance
account = BankAccount("Alice", 1000)
account.deposit(500)
print(account.get_balance()) # 1500
print(account._balance) # 1500 (접근은 되지만, 권장하지 않음)
다형성은 같은 이름의 메서드(또는 연산자)를 호출했을 때, 그 대상 객체의 종류(클래스)에 따라 다르게 동작하는 성질을 말함.
예: “동물” 클래스의 make_sound() 메서드가 “개” 클래스에서는 “멍멍”을 출력하고, “고양이” 클래스에서는 “야옹”을 출력하게 하는 것.
class Animal:
def make_sound(self):
print("동물이 소리를 냅니다.")
class Dog(Animal):
def make_sound(self):
print("멍멍!")
class Cat(Animal):
def make_sound(self):
print("야옹!")
animals = [Animal(), Dog(), Cat()]
for a in animals:
a.make_sound()
# Animal: 동물이 소리를 냅니다.
# Dog: 멍멍!
# Cat: 야옹!
파이썬에서는 덕 타이핑(“오리처럼 걷고 소리 내면 오리다”라는 의미) 을 많이 사용.
예를 들어, 아래와 같이 make_sound() 메서드만 있다면, 클래스가 달라도 함께 처리할 수 있다.
class Dog:
def make_sound(self):
print("멍멍!")
class Car:
def make_sound(self):
print("빵빵!")
def play_sound(obj):
obj.make_sound()
dog = Dog()
car = Car()
play_sound(dog) # 멍멍!
play_sound(car) # 빵빵!
추상화는 구체적인 구현은 감추고, 필요한 공통 인터페이스나 개념만 표현하는 것.
파이썬에서 추상화는 주로 abc(Abstract Base Classes) 모듈을 통해 구현.
추상 클래스(ABC): 직접 인스턴스를 만들 수 없고, 자식 클래스에서 반드시 구현해야 하는 추상 메서드(abstract method)를 지정할 수 있는 클래스
자식 클래스가 추상 메서드를 구현(override)하지 않으면 에러.
예시
from abc import ABC, abstractmethod
class Animal(ABC):
@abstractmethod
def make_sound(self):
pass
def sleep(self):
print("동물이 잠을 잡니다.")
class Dog(Animal):
def make_sound(self):
print("멍멍!")
dog = Dog()
dog.make_sound() # 멍멍!
dog.sleep() # 동물이 잠을 잡니다.
- class Animal(ABC):
ABC를 상속함으로써 추상 클래스를 만들 수 있음.- @abstractmethod
이 데코레이터가 붙은 메서드는 “반드시 자식 클래스에서 오버라이딩 해야 하는 메서드”.
만약 자식 클래스에서 make_sound()를 구현하지 않으면, TypeError: Can't instantiate abstract class ... 에러가 발생.- 추상 클래스인 Animal 자체로는 인스턴스를 만들 수 없음.
예: animal = Animal() -> 에러 (추상 클래스는 인스턴스화할 수 없음)