SW개발/Python

[Python]staticmethod vs classmethod (feat. 언제 사용할까?)

최근에 개발을 하다가 파이썬의 정적 메소드와 클래스 메소드를 언제 사용해야 하는지에 대한 기준이 궁금해졌습니다. 단순하게 눈에 보이는 차이로는 self 와 cls의 차이였는데요. 이 둘을 주로 어떤 상황에서, 어떤 용도로 나누어 사용하는지 알아보려고 합니다.

 

이와 관련한 유명 아티클이 있어 해당 글의 일부를 차용하였고, 개인적으로 이해한 것을 바탕으로 정리했습니다.

https://julien.danjou.info/guide-python-static-class-abstract-methods/

 

The definitive guide on how to use static, class or abstract methods in Python

Doing code reviews is a great way to discover things that people might struggle to comprehend. While proof-reading OpenStack patches recently, I spotted that people were not using correctly the various decorators Python provides for methods. So here's my a

julien.danjou.info

 

먼저 클래스에서 사용되는 각 메소드 종류에 대해서 간단하게 알아보도록 하겠습니다.

 

instance method 란?

인스턴스 메소드는 가장 기본이 되는 클래스의 메소드입니다. 인스턴스 메소드의 첫번째 인자로는 항상 self를 적고, 해당 클래스의 인스턴스 값이 넘어오게 됩니다.

class Blog:
    def __init__(self, name="leffe's blog"):
        self.name = name

	# 인스턴스 메소드
    def print_name(self):
        print(self.name)
        
blog = Blog()
print(blog.print_name())
# leffe's blog

 

staticmethod 란?

클래스의 인스턴스 메소드에 @staticmethod 데코레이터를 사용하면 정적 메소드가 됩니다. 인스턴스 메소드와는 달리 self 같은 인자가 필요하지 않습니다. 또한, 인스턴스의 속성에도 접근할 수 없습니다.

class Blog:
    def __init__(self, name="leffe's blog"):
        self.name = name

    # 정적 메소드
    @staticmehtod
    def to_upper(text: str):
        print(text.upper()
        
blog = Blog()
print(blog.name)
# leffe's blog
print(Blog.to_upper(blog.name))
# LEFFE'S BLOG

 

staticmethod 의 사용

정적 메소드의 경우에는 주로 유틸리티성 함수를 위한 용도로서 사용됩니다.

클래스의 어떠한 속성에도 변화를 일으키지 않는 함수이며 입력이 들어오면 항상 같은 출력을 반환하는 순수함수들이 이에 해당합니다.

즉, 개발자는 인스턴스에 어떠한 변화도 일으키지 않는구나 라는 의미를 내포하고 있는 것으로 해석하고 이용할 수 있습니다.

 

그러나 여기서 한가지 의문점이 들 수 있습니다.

유틸리티 함수의 경우 클래스 내부에 위치하지 않고 그냥 모듈로 따로 모아두면 되지 않나요?
그러면 staticmethod가 필요 없는 것 아닌가요?

정답은 없습니다. 이 부분은 실제 개발자의 취향차이라고 생각이 드는데요, 유틸리티성 함수임에도 클래스와 연관이 높은 함수라면 @staticmethod 를 활용해 클래스 내에 위치시켜 연관이 깊다고 의미를 부여하는 방법으로 사용할 수 있습니다.

물론, 속한 조직이나 개인의 취향에 따라서 유틸리티성 함수를 모듈로 빼는 것도 좋은 방법입니다.

 

classmethod 란?

클래스의 인스턴스 메소드에 @classmethod 데코레이터를 사용하면 클래스 메소드가 됩니다. 함수의 첫번째 인자로서 cls를 사용하고, 클래스 자체가 넘어오게 됩니다. 따라서 클래스의 속성에 접근할 수 있습니다.

class Blog:
    platform: str = "tistory"

    def __init__(self, name="leffe's blog"):
        self.name = name

    # 클래스 메소드
    @classmethod
    def change_platform(cls, platform: str):
        cls.platform = platform
        
    @classmethood
    def from_dict(cls, dic: dict):
        return cls(dic["name"])
        
blog = Blog()
print(blog.platform)
# tistory
Blog.change_platform("medium")
print(blog.platform)
# medium

blog2 = Blog.from_dict({"name": "leffe's coding blog"})
print(blog2.name)
# leffe's coding blog

 

classmethod의 사용

클래스 메소드의 경우 주로 아래와 같은 패턴에서 사용됩니다.

  1. 팩토리 메서드를 작성할 때 사용합니다.
  2. 클래스 속성에 접근하려는 경우 사용합니다.

 

팩토리 메서드를 사용하는 경우

특정한 입력 형식으로부터 인스턴스를 생성하려는 경우 classmethod는 유용합니다. 위에서 예시로서 적은 from_dict() 함수의 경우도, 딕셔너리로부터 값을 받아와 인스턴스를 생성하기 위해서 생성자를 wrapping 하는 용도로 사용되었습니다.

 

클래스 속성에 접근하려는 경우

클래스 속성에 접근하거나, 변경하려는 경우에도 classmethod가 사용됩니다. change_platform() 함수의 경우 클래스의 속성인 platform의 값을 접근하고 변경하는 방식으로 이용되었습니다.

 

마무리

실제로 오픈소스 라이브러리 코드에서도 위와 같은 기준을 가지고 작성 되는지 궁금하여 django 코드에서 검색을 많이 해보았습니다. staticmethod 같은 경우에는 대부분 순수함수로 이루어져 있는 공통점을 확인할 수 있었습니다. 또한, staticmethod는 인스턴스화 할 경우 bound 되지 않기 때문에 메모리 사용량에 이점이 있습니다. (부차적인 이유)

 

classmethod 같은 경우는 장고에서는 테스트 클래스를 제외하고는 많이 사용되지 않아, strawberry 라이브러리에서 검색해보았는데 주로 클래스의 생성자를 wrapping하는 용도로 사용되었습니다. (아닌 부분들도 존재합니다)

 

프로그래밍에 정답은 없습니다. 이번 포스팅에서는 통용적으로 사용되는 기준에 대해서 설명을 드렸습니다. 본인의 취향과 속한 조직의 컨벤션에 따라서 이 둘을 나누어 사용하는 기준은 언제든 바뀔 수 있습니다.

 

 

오늘도 읽어주셔서 감사합니다 :)

 

728x90