파이썬 클래스
파이썬 클래스
프로그램을 짜다 보면 관련 있는 데이터와 그 데이터를 다루는 함수를 한 덩어리로 묶고 싶을 때가 있어요. 클래스가 바로 그 역할을 하죠. 클래스를 만들면 새로운 타입이 생기고, 그 타입의 인스턴스를 만들어 원하는 상태(state)를 담을 수 있어요. 파이썬의 클래스는 다른 언어보다 문법이 단순하면서도 상속·메서드 오버라이드 같은 객체 지향 기능을 모두 갖추고 있어요. 이름이 결정되는 규칙인 스코프와 네임스페이스부터 인스턴스, 상속, 반복자까지 순서대로 풀어볼게요.
스코프와 네임스페이스
클래스를 이해하려면 먼저 이름이 어디서 정의되고 어떻게 찾아지는지를 알아야 해요. 네임스페이스는 이름과 객체를 연결해 주는 공간으로, 내장 이름(예: abs(), 내장 예외들), 모듈의 전역 이름, 함수 호출의 지역 이름이 각각 별도 네임스페이스예요. 서로 다른 네임스페이스의 이름끼리는 전혀 간섭하지 않아서, 두 모듈이 같은 이름의 함수를 가져도 문제가 없어요.
이름을 찾는 순서는 지역(local) → 전역(global) → 내장(built-in) 이에요. 스코프는 텍스트 기준으로 정해지는데, 함수가 어느 모듈에서 정의됐는지에 따라 그 함수의 전역 스코프가 결정돼요. 실제 이름 탐색은 실행 시점에 동적으로 일어난다는 점이 특징이에요.
클래스 객체와 인스턴스
클래스 정의는 곧 클래스 객체를 만들어요. 클래스 객체를 '호출'하면 빈 인스턴스가 생기는데, 보통은 초기 상태를 지정하고 싶죠. 그때 특별한 메서드 __init__을 정의해요.
class Dog:
def __init__(self, name):
self.name = name
def speak(self):
return f"{self.name} says woof"
메서드 정의에서 첫 번째 인자 self는 인스턴스 자신을 가리켜요. 인스턴스 변수는 self.이름으로 만들고, 메서드는 instance.method() 형태로 호출해요. __init__처럼 앞뒤에 밑줄이 붙은 '던더(dunder)' 메서드는 파이썬이 특별한 의미로 처리해요.
상속(inheritance)
클래스는 다른 클래스를 상속받아 기능을 확장할 수 있어요. 파생 클래스는 기반 클래스의 메서드를 그대로 쓰면서, 필요한 메서드만 다시 정의(오버라이드)하면 돼요.
class Puppy(Dog):
def speak(self):
return f"{self.name} yips"
상속받는 클래스 이름을 괄호 안에 적으면 되고, 여러 기반 클래스를 쉼표로 나열하는 다중 상속도 지원해요. 기반 클래스의 같은 이름 메서드를 호출하려면 super()를 쓰면 돼요. 사물(class)이 '이다(is-a)' 관계일 때 상속이 자연스럽답니다.
데이터 클래스(dataclass)
이름과 타입을 쌍으로 묶어 간단히 담고 싶은 데이터 타입이 필요할 때가 있어요. C의 구조체(struct)처럼 쓰고 싶다면, 관용적으로 dataclasses 데코레이터를 많이 사용해요.
from dataclasses import dataclass
@dataclass
class Employee:
name: str
dept: str
salary: int
데코레이터만 붙이면 초기화·출력·비교 같은 반복적인 보일러플레이트를 파이썬이 자동으로 만들어 줘요. 타입 힌트를 적어 두면 정적 도구가 오류를 잡는 데도 도움이 돼요.
반복자와 제너레이터
for 루프가 동작하는 배경에는 반복자(iterator) 프로토콜이 있어요. __iter__()는 반복자 자신을 돌려주고 __next__()는 다음 값을 하나씩 돌려주는 객체를 만들면 for에서 그대로 쓸 수 있어요. 항목을 소진하면 StopIteration을 일으키면 되죠.
class Reverse:
"""시퀀스를 뒤에서부터 순회하는 반복자."""
def __init__(self, data):
self.data = data
self.index = len(data)
def __iter__(self):
return self
def __next__(self):
if self.index == 0:
raise StopIteration
self.index -= 1
return self.data[self.index]
반복자를 만들 일이 많아지면 제너레이터(generator) 와 제너레이터 표현식이 훨씬 편해요. yield 키워드로 값을 하나씩 던지면 __iter__/__next__를 직접 구현하지 않아도 반복자 프로토콜을 만족시킬 수 있답니다.