클래스는 데이터와 동작을 하나로 묶는다
클래스를 쓰면 어떤 대상의 타입을 정의할 수 있다. 사용자, 은행 계좌, 설정, 요청 같은 것들 말이다. 그리고 그 대상이 할 수 있는 동작까지 함께 담을 수 있다. 이때 실제 대상 하나하나를 클래스의 _인스턴스_라고 부르고, 그 대상이 수행하는 _동작_을 _메서드_라고 한다.
가장 단순한 형태의 클래스를 먼저 보자:
하나씩 뜯어보면:
class User:로 클래스 정의가 시작됩니다. 클래스 이름은 관례적으로 PascalCase로 씁니다.__init__는 초기화 메서드(initializer) 입니다. 새 인스턴스를 만들 때 자동으로 호출되며,self.무언가 = ...형태로 인스턴스 속성을 세팅하는 역할을 합니다.greet는 메서드입니다.self는 메서드가 호출된 바로 그 인스턴스 자신을 가리키는 이름이고요.User("Rosa", "rosa@example.com")은 인스턴스를 생성합니다. 이때 파이썬은__init__를 호출하면서self에 새로 만들어진 인스턴스를 넘기고, 나머지 인자도 함께 전달합니다.user.greet()는 메서드를 호출합니다. 파이썬이 알아서user를self자리에 넘겨주죠.
self는 그냥 첫 번째 매개변수일 뿐
self는 키워드가 아닙니다. 인스턴스 메서드의 첫 번째 매개변수에 파이썬 개발자들이 관례적으로 붙이는 이름일 뿐이에요. 즉, 지금 메서드가 실행되고 있는 대상 인스턴스를 가리키는 변수죠. 원한다면 다른 이름으로도 쓸 수 있지만, 다들 self라고 쓰기 때문에 우리도 self라고 씁니다.
모든 인스턴스 메서드는 첫 번째 매개변수로 self를 받습니다. 그래야 어떤 인스턴스의 데이터를 읽거나 수정해야 하는지 메서드가 알 수 있으니까요.
동작 추가하기
메서드는 상태를 읽을 수도 있고, 바꿀 수도 있습니다:
__repr__도 파이썬에서 자주 쓰는 특별 메서드 중 하나다. 한마디로 "이 객체를 출력할 때 어떻게 보여줄까?"를 정의하는 훅이라고 보면 된다. 제대로 만들어두면 디버깅할 때 두고두고 고마워하게 된다.
클래스 속성과 인스턴스 속성의 차이
클래스 본문에 정의한 속성(즉, __init__ 바깥에 쓴 속성)은 모든 인스턴스가 공유 한다. 반면 self에 할당한 속성은 인스턴스마다 따로 존재한다.
정말로 공유되어야 하는 값, 즉 상수나 카운터, 설정 같은 것들은 클래스 속성으로 두세요. 인스턴스마다 달라지는 데이터는 __init__ 안에 넣으면 됩니다.
상속 (Inheritance)
클래스는 다른 클래스를 _상속_할 수 있습니다. 부모 클래스의 메서드와 속성을 그대로 물려받으면서, 필요한 부분만 새로 추가하거나 덮어쓸 수 있죠.
super()는 부모 클래스에 접근할 수 있게 해주는데, 보통 __init__ 안에서 자식만의 속성을 추가하기 전에 부모의 속성을 먼저 초기화할 때 많이 사용합니다:
상속은 강력하지만 남용하기 쉬운 도구입니다. 파이썬을 막 시작한 개발자들은 합성(composition) — 객체를 속성으로 갖는 방식 — 이 더 명확한 상황에서도 습관적으로 상속부터 꺼내드는 경우가 많습니다. 일단은 평평한 클래스 구조로 시작하고, 진짜 "is-a" 관계가 성립할 때만 상속을 도입하세요.
데이터클래스: 레코드를 다룰 때 쓰기 좋은 기본 선택지
단순히 몇 개의 속성을 담고 동등성 비교나 출력 동작 정도만 필요한 클래스라면, @dataclass를 사용하면 반복되는 보일러플레이트 코드를 크게 줄일 수 있습니다:
__init__, __repr__, __eq__를 일일이 손으로 작성한다고 생각해보세요. 그냥 __init__에서 속성만 대입하는 class Thing 정도라면, 데이터 클래스를 쓰는 게 가장 현명한 선택입니다.
프로퍼티: 계산되는 속성
가끔은 실제로는 계산해서 만들어내는 값인데, 겉으로는 "속성"처럼 보이게 하고 싶을 때가 있습니다. @property를 쓰면 속성 접근처럼 자연스럽게 사용할 수 있죠:
obj.get_something() 식으로 쓰게 될 상황이라면 프로퍼티를 써보세요. 속성처럼 접근할 수 있어서 API가 훨씬 자연스러워집니다. 다만 무거운 연산을 숨기는 용도로는 쓰지 마세요. 속성 접근은 당연히 빠를 거라고 기대하니까요.
관례로 정하는 private 속성
파이썬에는 진짜 의미의 private이 없습니다. 대신 관례적으로, 언더스코어(_)로 시작하는 속성은 private으로 의도된 것이라고 봅니다. 다시 말해, 클래스 바깥에서는 건드리지 않는 게 암묵적인 약속이죠:
class Cache:
def __init__(self):
self._store = {}
def put(self, key, value):
self._store[key] = value
def get(self, key, default=None):
return self._store.get(key, default)
이중 언더스코어로 시작하는 이름(__name)은 네임 맹글링(name mangling) 을 유발하는데, 이 과정에서 속성 이름 앞에 클래스 이름이 붙습니다. 이건 상속 시 이름 충돌을 피하기 위한 장치이지, 보안 기능이 아닙니다.
클래스를 쓰지 않아도 될 때
서로 관련된 데이터 묶음만 필요하다면 dataclass나 그냥 딕셔너리, 혹은 네임드 튜플로도 충분합니다. 함수 하나만 있으면 되는 상황이라면 굳이 클래스로 감쌀 필요가 없죠. 클래스는 상태와 동작이 함께 묶여 있을 때, 특히 같은 종류의 인스턴스를 여러 개 만들어야 할 때 꺼내 쓰는 도구입니다.
마지막 예제
파일처럼 동작하는 간단한 로그 버퍼를 만들어 봅시다:
__len__도 매직 메서드다. 이걸 정의해두면 len(logger)가 동작하는데, 이유는 간단하다. len(x)는 사실 내부적으로 x.__len__()을 호출하는 것이기 때문이다. 파이썬에는 이런 훅(hook)이 수십 개나 있는데, 필요할 때마다 하나씩 익혀나가면 된다.
다음 주제: 제너레이터
클래스는 데이터와 동작을 하나로 묶어준다. 다음 장에서는 조금 다른 결의 추상화, 즉 **반복(iteration)**을 다룬다. 그 시작은 제너레이터다. 제너레이터는 값을 한 번에 하나씩 만들어내며 그 사이에 잠시 멈춰 있는 함수라고 보면 된다. 그 바로 뒤에 이어지는 with 문과 컨텍스트 매니저와도 찰떡궁합이니 기대해도 좋다.
자주 묻는 질문
파이썬에서 클래스란 무엇인가요?
클래스는 데이터(속성)와 동작(메서드)을 하나로 묶어 객체를 만들어내는 설계도입니다. 예를 들어 class User:로 User 클래스를 정의하고, User(...)를 호출하면 인스턴스가 만들어집니다. 상태와 동작을 함께 갖는 대상을 모델링할 때 파이썬에서 가장 많이 쓰는 도구가 바로 클래스예요.
파이썬에서 self는 어떤 의미인가요?
self는 인스턴스 메서드의 첫 번째 매개변수입니다. user.greet()처럼 호출하면, 파이썬이 자동으로 user 인스턴스를 self로 전달해줍니다. 메서드 안에서 self.name이라고 쓰면 해당 인스턴스의 name 속성에 접근할 수 있죠. self는 예약어가 아니라 그냥 관례지만, 파이썬 개발자라면 거의 예외 없이 이 이름을 씁니다.
파이썬은 객체지향 언어인가요?
네, 맞습니다. 파이썬에서는 숫자, 문자열, 함수까지 모든 것이 객체예요. 객체지향이 어울리는 상황에서는 OOP 방식으로 코드를 짤 수 있지만, 파이썬은 이를 강제하지 않습니다. 실제 현업 코드에서도 클래스를 남발하지 않고, 가능하면 일반 함수와 기본 자료구조로 해결하는 경우가 많습니다.