본문 바로가기

프로그래밍/Python

[Python] classmethod는 언제 사용해?

728x90
Python @classmethod 실습 가이드
Python · @classmethod

Python @classmethod 실습 중심 가이드

이 글은 Python의 @classmethod를 인스턴스 메서드, 정적 메서드와 비교하면서, 실습 위주로 이해할 수 있도록 정리한 가이드입니다.

1. 메서드 3종류 큰 그림

Python 클래스에서 자주 사용하는 메서드는 세 가지입니다.

  • 인스턴스 메서드
  • @classmethod가 붙은 클래스 메서드
  • @staticmethod가 붙은 정적 메서드
class Demo:
    def instance_method(self):
        print("인스턴스 메서드, self =", self)

    @classmethod
    def class_method(cls):
        print("클래스 메서드, cls =", cls)

    @staticmethod
    def static_method():
        print("정적 메서드, self/cls 없음")

obj = Demo()
obj.instance_method()
Demo.class_method()
Demo.static_method()
  • 인스턴스 메서드: 첫 인자 self, 개별 객체 상태를 다룸
  • 클래스 메서드: 첫 인자 cls, 클래스 자체를 다룸
  • 정적 메서드: 첫 인자 없음, 클래스/인스턴스와 무관한 순수 함수

2. @classmethod 한 줄 정의

@classmethod“첫 번째 인자로 클래스 객체(cls)를 받는 메서드”입니다.

실무에서는 주로 “대체 생성자(Alternative Constructor)”를 만들 때 자주 사용합니다.

3. 기본 예제 – 문자열로부터 인스턴스 생성

class Person:
    def __init__(self, name, age):
        self.name = name
        self.age = age

    @classmethod
    def from_string(cls, text):
        name, age = text.split(",")
        return cls(name, int(age))

p1 = Person("Alice", 30)
p2 = Person.from_string("Bob,25")

print(p1.name, p1.age)
print(p2.name, p2.age)

from_string은 문자열을 파싱해서 cls(...)를 호출하고, 새 인스턴스를 만들어 반환하는 “대체 생성자”입니다.

4. 왜 cls가 중요한가?

단순히 Person(...)를 직접 호출해도 동작은 하지만, 상속을 고려하면 문제가 생길 수 있습니다.

class Person:
    def __init__(self, name, age):
        self.name = name
        self.age = age

    @classmethod
    def from_string(cls, text):
        name, age = text.split(",")
        return cls(name, int(age))

class Employee(Person):
    def __init__(self, name, age, company):
        super().__init__(name, age)
        self.company = company

이제 다음을 호출한다고 가정해봅시다.

e = Employee.from_string("Carol,40")
  • from_stringPerson에 정의되어 있지만,
  • cls에는 실제로 Employee 클래스가 전달됩니다.
  • 따라서 return cls(...)Employee(...)를 호출하게 됩니다.
상속/다형성을 고려하면, Person(...) 대신 cls(...)로 작성하는 것이 훨씬 안전하고 확장성이 좋습니다.

5. @staticmethod와 비교하기

클래스 메서드와 정적 메서드는 특히 헷갈리기 쉬우므로, 예제를 통해 비교해 봅니다.

class Person:
    def __init__(self, name, age):
        self.name = name
        self.age = age

    @staticmethod
    def is_adult(age):
        return age >= 18

    @classmethod
    def from_string(cls, text):
        name, age = text.split(",")
        return cls(name, int(age))

p = Person.from_string("Alice,30")
print(Person.is_adult(p.age))
  • is_adult: 나이 숫자만 보고 판단 → 클래스/인스턴스 상태와 무관 → @staticmethod 적합
  • from_string: 새 인스턴스를 돌려주는 대체 생성자 → cls 필요 → @classmethod 적합

6. 동작 원리 – classmethod() 함수

@classmethod도 데코레이터 문법의 축약형입니다.

class Person:
    @classmethod
    def from_string(cls, text):
        ...

위 코드는 다음과 동등합니다.

class Person:
    def from_string(cls, text):
        ...

    from_string = classmethod(from_string)

classmethod() 빌트인 함수는 해당 함수를 “첫 번째 인자로 인스턴스가 아니라 클래스(cls)를 받는 메서드”로 감싸 줍니다.

7. 실습 1 – 다양한 생성 경로 만들기

class MyDate:
    def __init__(self, year, month, day):
        self.year = year
        self.month = month
        self.day = day

    @classmethod
    def from_string(cls, text):
        # "2025-12-31"
        year, month, day = text.split("-")
        return cls(int(year), int(month), int(day))

    @classmethod
    def today(cls):
        # 예시: 실제로는 datetime 등을 사용할 수 있음
        return cls(2025, 1, 1)

d1 = MyDate(2025, 12, 31)
d2 = MyDate.from_string("2025-12-31")
d3 = MyDate.today()

print(d1.year, d1.month, d1.day)
print(d2.year, d2.month, d2.day)
print(d3.year, d3.month, d3.day)

이렇게 여러 생성 방식(생성자 스타일)을 한 클래스 안에 모아두는 것@classmethod 사용의 대표적인 패턴입니다.

8. 실습 2 – 상속 + @classmethod

class Shape:
    def __init__(self, name):
        self.name = name

    @classmethod
    def from_config(cls, config):
        return cls(config["name"])

class Circle(Shape):
    def __init__(self, name, radius):
        super().__init__(name)
        self.radius = radius

    @classmethod
    def from_config(cls, config):
        obj = super().from_config(config)  # 여기서도 cls는 Circle
        obj.radius = config["radius"]
        return obj

c = Circle.from_config({"name": "my-circle", "radius": 10})
print(c.name, c.radius)

부모 클래스의 @classmethod를 재사용하면서, 자식 클래스에서 필요한 정보를 추가로 설정할 수 있습니다.

9. 언제 @classmethod를 쓰면 좋은가?

  • 문자열, 딕셔너리, 파일, 환경 변수 등 다양한 입력으로 인스턴스를 만들고 싶을 때
  • 상속 구조에서 공통 생성 로직을 부모 클래스에 두고 재사용하고 싶을 때
  • 클래스 단위로 관리되는 설정/리소스를 생성/초기화하는 팩토리를 만들 때
단순 계산이나 판별처럼 클래스와 무관한 로직은 @staticmethod가 더 어울리며, 인스턴스 상태를 다루는 로직은 일반 인스턴스 메서드가 더 적합합니다.

10. 정리 – 세 가지 메서드 비교

  • 인스턴스 메서드: self, 개별 객체 상태를 다룸
  • 클래스 메서드 (@classmethod): cls, 대체 생성자 / 클래스 단위 로직 / 상속 고려
  • 정적 메서드 (@staticmethod): 인스턴스/클래스 모두와 무관한 순수 함수

11. 연습 문제

연습 1 – User.from_string

User 클래스를 만들고 다음을 구현해 보세요.

  • __init__(self, name, age)
  • @classmethod from_string(cls, text)"이름:나이" 형식 문자열로부터 인스턴스 생성

연습 2 – Config.from_dict / Config.default

Config 클래스를 만들고:

  • @classmethod from_dict(cls, d) – 딕셔너리 기반 생성
  • @classmethod default(cls) – 기본값을 가진 설정 인스턴스 생성

나중에 AppConfig 같은 서브클래스를 만들어, 부모의 @classmethod를 재사용해 보는 것도 좋은 연습입니다.