learn

Python @staticmethod

Python @staticmethod

@staticmethod의 목적은 "외부에서 호출 가능하게" 하는 것이 아니다. 외부 호출은 인스턴스를 만들면 어떤 메서드든 가능하기 때문.

핵심

"이 함수는 self도, cls도 필요 없다"는 선언.

Python 클래스 안에 함수를 정의하면 기본적으로 첫 번째 인자로 인스턴스(self)가 암묵적으로 바인딩된다. @staticmethod는 그 바인딩을 제거한다.

class Converter:
    @staticmethod
    def celsius_to_fahrenheit(c):
        return c * 9/5 + 32

# 두 가지 모두 동작
Converter.celsius_to_fahrenheit(100)
Converter().celsius_to_fahrenheit(100)

왜 모듈 함수로 안 빼고 클래스 안에 두나?

기능적으로는 동일하다. 차이는 의미와 네임스페이스 — "이 함수는 이 클래스의 책임 범위 안에 있고, 인스턴스 상태는 필요 없다"는 것을 코드로 명시하는 것.

세 가지 메서드 비교

| | 첫 인자 | 접근 가능한 것 | |---|---|---| | 일반 메서드 | self (인스턴스) | 인스턴스 상태, 클래스 | | @classmethod | cls (클래스) | 클래스 변수, 클래스 메서드 | | @staticmethod | 없음 | 인자로 받은 것만 |

@classmethod는 팩토리 메서드 패턴에 자주 쓰인다 — cls를 통해 서브클래스를 인식할 수 있기 때문. @staticmethod는 그마저도 필요 없는 순수 유틸리티 함수.

왜 클래스 밖에 안 두나?

기능적으로는 모듈 레벨 함수와 완전히 동일하게 동작한다. 차이는 의미적 소속뿐.

"이메일 검증은 User의 책임" → User 클래스 안에 두는 것이 구조적으로 명확하다. C++로 치면 관련 없는 free function을 namespace 안에 넣는 감각과 같다.

사용 방법

class Temperature:
    def __init__(self, celsius):
        self.celsius = celsius

    def to_fahrenheit(self):               # 인스턴스 메서드 — self.celsius 씀
        return Temperature.c_to_f(self.celsius)

    @staticmethod
    def c_to_f(c):                         # 변환 공식 자체는 인스턴스 상태 불필요
        return c * 9/5 + 32

# 클래스 이름으로 직접 호출
Temperature.c_to_f(0)      # → 32.0

# 인스턴스를 통해서도 호출 가능 (근데 어색해서 잘 안 씀)
t = Temperature(100)
t.c_to_f(0)                # → 32.0
t.to_fahrenheit()          # → 212.0  (내부에서 staticmethod 호출)

한 줄 기준

"인스턴스 없이도 쓸 수 있고, 이 클래스와 의미적으로 관련 있다" → @staticmethod

성능 vs 가독성

@staticmethod는 호출 시 bound method 객체 생성을 건너뛰어서 미세하게 빠르다. 하지만 수십 나노초 수준 — 병목이 될 수 없다.

진짜 이유는:

  • 의도 표현: 함수 내부를 읽지 않아도 "인스턴스 상태 안 건드림"을 즉시 알 수 있다
  • 구조적 가이드레일: self가 없으니 실수로 인스턴스 상태를 건드리는 코드가 들어갈 수 없다

C++에서 멤버 함수에 const를 붙이는 감각과 같다 — 성능보다 "이 함수는 상태를 안 바꾼다"는 계약을 코드에 새기는 것.