[파이썬] 정적(static) 메서드와 클래스(class) 메서드
이번 포스팅에서는 파이썬의 정적(static) 메서드와 클래스(class) 메서드에 대해서 알아보겠습니다.
인스턴스(instance) 메서드
정적(static) 메서드와 클래스(class) 메서드에 대해서 살펴보기 전에 가장 기본이 되는 인스턴스(instance) 메서드에 대해서 먼저 짚고 넘어가는 게 좋을 것 같습니다.
클래스에 아무 데코레이터(decorator) 없이 메서드를 선언하면 해당 메서드는 인스턴스 메서드로 취급이 되며, 첫번째 매개 변수로 클래스의 인스턴스가 넘어오게 됩니다.
이 첫번째 매개 변수의 이름은 보통 관행적으로 self
라고 하며, 인스턴수 메서드는 이 self
를 통해 인스턴스 속성(attribute)에 접근하거나 다른 인스턴스 메서드를 호출할 수 있습니다.
뿐만 아니라 self
를 통해, 클래스 속성에 접근하거나 클래스 메서드를 호출할 수도 있습니다.
class Counter:
def __init__(self, value = 0):
self.value = value
def increment(self, delta = 1):
self.value += delta
def decrement(self, delta = 1):
self.value -= delta
예를 들어, 위의 Counter
클래스는 3개의 인스턴스 메서드로 이루어져 있으며, 각 인스턴스 메서드는 self
라는 첫번째 매개 변수를 가지고 있습니다.
생성자인 __init__
매서드는, 인자로 넘어온 value
값을 객체의 value
속성에 할당해주며, increment
와 decrement
메서드는 객체의 value
속성값을 변경해줍니다.
인스턴스 메서드는 앞으로 소개해드릴 클래스 메서드나 정적 메서드와 달리 반드시 먼저 인스턴스를 생성 한 후에 해당 인스턴스를 대상으로 호출해야 합니다. 인스턴스 메서드의 첫번째 매개 변수는 자동으로 할당되기 때문에, 호출할 때 넘기는 첫번째 인자는 실제 인스턴스 메서드에서는 두번째 매개 변수에 할당되는 효과가 나타납니다.
>>> counter = Counter()
>>> counter.value
0
>>> counter.increment(3)
>>> counter.value
3
>>> counter.decrement(2)
>>> counter.value
1
클래스(class) 메서드
@classmethod
데코레이터를 사용해서 클래스에 메서드를 선언하면 해당 메서드는 클래스(class) 메서드가 되며, 첫번째 매개 변수로 클래스 인스턴스가 아닌 클래스 자체가 넘어오게 됩니다.
이 첫번째 매개 변수의 이름은 보통 관행적으로 cls
라고 하며, 클래스 메서드는 이 cls
를 통해 클래스 속성(attribute)에 접근하거나, 클래스 메서드를 호출할 수 있습니다.
하지만, 인스턴스 메서드와 달리 인스턴스 속성에 접근하거나 다른 인스턴스 메서드를 호출하는 것은 불가능합니다.
class User:
def __init__(self, email, password):
self.email = email
self.password = password
@classmethod
def fromTuple(cls, tup):
return cls(tup[0], tup[1])
@classmethod
def fromDictionary(cls, dic):
return cls(dic["email"], dic["password"])
파이썬에서는 생성자 오버로딩(overloading)을 지원하기 때문에, 클래스 메서드는 특히 팩토리 메서드를 작성할 때 유용하게 사용됩니다.
예를 들어, 위의 User
클래스는 생성자인 1개의 인스턴스 메서드와 @classmethod
어노테이션이 달린 2개의 클래스 메서드로 이루어져 있습니다.
fromTuple
와 fromDictionary
메서드의 첫번째 매개 변수인 cls
에는 클래스가 할당되어 있으며, 이 클래스를 호출하면 생성자가 호출되고 인스턴스가 생성되게 됩니다.
fromTuple
메서드는 튜플(tuple) 타입의 인자를 받고, fromDictionary
메서드는 사전(dictionary) 타입의 인자를 받아서 User
클래스의 객체를 생성해줍니다.
따라서 다음과 같이 팩토리 메서드를 이용해서 다양한 포맷의 데이터로 부터 User
객체를 생성할 수 있습니다.
- 기본 생성자로 객체 생성
>>> user = User("user@test.com", "1234")
>>> user.email, user.password
('user@test.com', '1234')
- 클래스 메서드로 튜플로 부터 객체 생성
>>> user = User.fromTuple(("user@test.com", "1234"))
>>> user.email, user.password
('user@test.com', '1234')
- 클래스 메서드로 사전으로 부터 객체 생성
>>> user = User.fromDictionary({"email": "user@test.com", "password": "1234"})
>>> user.email, user.password
('user@test.com', '1234')
정적(static) 메서드
@staticmethod
데코레이터를 사용해서 클래스에 메서드를 선언하면 해당 메서드는 정적(static) 메서드가 되며, 정적 메서드는 인스턴스 메서드나 클래스 메서드와 달리 첫번째 매개 변수가 할당되지 않습니다.
따라서 정적 메서드 내에서는 인스턴스/클래스 속성에 접근하거나, 인스턴스/클래스 메서드를 호출하는 것이 불가능합니다.
class StringUtils:
@staticmethod
def toCamelcase(text):
words = iter(text.split("_"))
return next(words) + "".join(i.title() for i in words)
@staticmethod
def toSnakecase(text):
letters = ["_" + i.lower() if i.isupper() else i for i in text]
return "".join(letters).lstrip("_")
정적 메서드는 유틸리티 메서드를 구현할 때 많이 사용됩니다.
예를 들어, 위의 StringUtils
클래스는 2개의 정적 메서드로 이루어져 있습니다. toCamelcase
메서드는 뱀 스타일(snake_case)의 문자열을 낙타 스타일로(CamelCase)로 변환해주며, toSnakecase
는 그 역방향의 변환을 해줍니다.
이 두 개의 메서드는 매개 변수로 넘어온 문자열에만 의존하는 순수한(pure) 함수이기 때문에 굳이 클래스의 일부로 선언할 필요는 없지만, 이렇게 비슷한 류의 여러 유틸리티 메서드를 하나의 클래스의 묶어두고 싶을 때 정적 메서드로 선언할 수 있습니다.
>>> StringUtils.toCamelcase("last_modified_date")
'lastModifiedDate'
>>> StringUtils.toSnakecase("lastModifiedDate")
'last_modified_date'
클래스(class) 메서드 vs. 정적(static) 메서드
클래스 메서드와 정적 메서드는 별도 인스턴스 생성없이 클래스를 대상으로 클래스 이름 뒤에 바로 .
오퍼레이터를 붙여서 호출할 수 있다는 점에서 동일합니다.
차이점은 클래스 메서드를 호출할 때, 첫번째 인자로 클래스 자체가 넘어오기 때문에, 클래스 속성에 접근하거나 다른 클래스 함수를 호출할 수 있습니다. 반면에 정적 메서드를 호출할 때는, 첫번째 인자로 아무것도 넘어오지 않기 때문에, 명시적으로 넘긴 다른 인자만 접근할 수 있습니다.
마치면서
이상으로 파이썬에서 @classmethod
데코레이터와 @staticmethod
데코레이터로 선언이 가능한 클래스 메서드와 정적 메서드에 대해서 알아보았습니다.
자바에서는 이 두 가지 개념이 동일하게 사용하기 때문에 자바에서 건너온 분들이 많이 햇갈려하는 부분이기도 한데요.
자바에서는 이 두 가지 개념을 굳이 구분할 이유가 없습니다. 왜냐하면 정적 메서드에서도 클래스 이름을 통해 자유롭게 클래스에 접근할 수 있기 때문입니다.
하지만 파이썬에서는 클래스에 접근이 필요할 때는 클래스 메서드, 접근이 필요없을 때는 정적 메서드를 사용하도록 되어 있습니다.