Classmethod and Staticmethod

Python의 클래스에서 사용되는 두 가지 다른 종류의 메서드 데코레이터

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
class MyClass:
    class_var = 0  # 클래스 변수

    def __init__(self):
        self.instance_var = 0  # 인스턴스 변수

    # 일반 인스턴스 메서드
    def instance_method(self):
        return f"instance method: {self.instance_var}"

    # 클래스 메서드
    @classmethod
    def class_method(cls):
        return f"class method: {cls.class_var}"

    # 정적 메서드
    @staticmethod
    def static_method():
        return "static method"

Classmethod

특징

  • @classmethod 데코레이터 사용
  • 첫 번째 매개변수로 클래스 자신(cls)을 자동으로 받음
  • 클래스 변수에 접근 가능
  • 상속 시 cls는 현재 클래스를 참조

주요 사용 사례

대체 생성자(Alternative Constructor) 구현
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
class Date:
    def __init__(self, year, month, day):
        self.year = year
        self.month = month
        self.day = day

    @classmethod
    def from_string(cls, date_string):
        year, month, day = map(int, date_string.split('-'))
        return cls(year, month, day)

    @classmethod
    def from_timestamp(cls, timestamp):
        import datetime
        date = datetime.datetime.fromtimestamp(timestamp)
        return cls(date.year, date.month, date.day)

# 사용 예시
date1 = Date.from_string('2024-03-20')
date2 = Date.from_timestamp(1710915600)  # 2024-03-20의 타임스탬프
클래스 상태 관리
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
class Student:
    total_students = 0  # 클래스 변수

    def __init__(self, name):
        self.name = name
        Student.total_students += 1

    @classmethod
    def get_total_students(cls):
        return cls.total_students

    @classmethod
    def reset_total_students(cls):
        cls.total_students = 0

# 사용 예시
student1 = Student("John")
student2 = Student("Jane")
print(Student.get_total_students())  # 출력: 2
Student.reset_total_students()
print(Student.get_total_students())  # 출력: 0

Staticmethod

특징

  • @staticmethod 데코레이터 사용
  • 첫 번째 매개변수로 아무것도 자동으로 받지 않음
  • 클래스/인스턴스 변수에 직접 접근 불가
  • 유틸리티 함수처럼 독립적으로 동작

주요 사용 사례

유틸리티 함수 구현
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
class MathOperations:
    @staticmethod
    def is_even(number):
        return number % 2 == 0

    @staticmethod
    def is_prime(number):
        if number < 2:
            return False
        for i in range(2, int(number ** 0.5) + 1):
            if number % i == 0:
                return False
        return True

    @staticmethod
    def get_factors(number):
        return [i for i in range(1, number + 1) if number % i == 0]

# 사용 예시
print(MathOperations.is_even(4))     # 출력: True
print(MathOperations.is_prime(7))    # 출력: True
print(MathOperations.get_factors(12))  # 출력: [1, 2, 3, 4, 6, 12]
헬퍼 메서드 구현
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
class FileProcessor:
    def __init__(self, filename):
        self.filename = filename

    @staticmethod
    def is_valid_file_format(filename):
        return filename.endswith(('.txt', '.csv', '.json'))

    @staticmethod
    def get_file_extension(filename):
        return filename.split('.')[-1] if '.' in filename else ''

    def process_file(self):
        if not self.is_valid_file_format(self.filename):
            raise ValueError("Invalid file format")
        # 파일 처리 로직…

# 사용 예시
print(FileProcessor.is_valid_file_format('data.txt'))  # 출력: True
print(FileProcessor.get_file_extension('data.csv'))    # 출력: csv

Classmethod와 Staticmethod 비교

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
class Example:
    class_var = 10

    def __init__(self):
        self.instance_var = 5

    # 인스턴스 메서드
    def instance_method(self):
        return self.instance_var, self.class_var

    # 클래스 메서드
    @classmethod
    def class_method(cls):
        return cls.class_var  # 클래스 변수 접근 가능
        # return self.instance_var  # 인스턴스 변수 접근 불가

    # 정적 메서드
    @staticmethod
    def static_method():
        # 클래스/인스턴스 변수 직접 접근 불가
        return "I am static"

사용 시 고려사항

Classmethod 사용 시기

  • 클래스 상태를 수정하거나 접근해야 할 때
  • 대체 생성자가 필요할 때
  • 상속 시 다형성이 필요할 때

Staticmethod 사용 시기

  • 클래스/인스턴스 상태와 무관한 유틸리티 함수가 필요할 때
  • 네임스페이스 조직화가 필요할 때
  • 순수 함수가 필요할 때
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
class DataProcessor:
    data_format = 'json'  # 클래스 변수

    def __init__(self, data):
        self.data = data

    # classmethod 사용이 적절한 경우
    @classmethod
    def change_data_format(cls, new_format):
        if cls.is_valid_format(new_format):  # staticmethod 호출
            cls.data_format = new_format
            return True
        return False

    # staticmethod 사용이 적절한 경우
    @staticmethod
    def is_valid_format(format_str):
        return format_str.lower() in ['json', 'xml', 'yaml']

    # 일반 인스턴스 메서드
    def process(self):
        if not self.is_valid_format(self.data_format):
            raise ValueError("Invalid format")
        # 데이터 처리 로직…

# 사용 예시
print(DataProcessor.is_valid_format('json'))  # 출력: True
DataProcessor.change_data_format('xml')  # 데이터 형식 변경

Self와 Cls

기본 개념

self: 인스턴스 메서드에서 인스턴스 자신을 참조
cls: 클래스 메서드에서 클래스 자신을 참조

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
class ExampleClass:
    class_variable = 0  # 클래스 변수
    
    def __init__(self):
        self.instance_variable = 0  # 인스턴스 변수
    
    # 인스턴스 메서드
    def instance_method(self):
        return self.instance_variable
    
    # 클래스 메서드
    @classmethod
    def class_method(cls):
        return cls.class_variable
    
    # 정적 메서드
    @staticmethod
    def static_method():
        return "I am static"

메서드 종류별 특징과 사용법

인스턴스 메서드 (self 사용)
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
class User:
    def __init__(self, name):
        self.name = name  # 인스턴스 변수 설정
    
    def say_hello(self):  # 인스턴스 메서드
        return f"Hello, I'm {self.name}"
    
    def change_name(self, new_name):  # 인스턴스 변수 수정
        self.name = new_name

# 사용 예시
user = User("John")
print(user.say_hello())  # 출력: Hello, I'm John
user.change_name("Mike")
print(user.say_hello())  # 출력: Hello, I'm Mike
클래스 메서드 (cls 사용)
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
class DatabaseConnection:
    connection_count = 0  # 클래스 변수
    
    def __init__(self):
        DatabaseConnection.connection_count += 1
    
    @classmethod
    def get_connection_count(cls):  # 클래스 메서드
        return cls.connection_count
    
    @classmethod
    def create_connection(cls):  # 팩토리 메서드 패턴
        return cls()

# 사용 예시
conn1 = DatabaseConnection()
conn2 = DatabaseConnection()
print(DatabaseConnection.get_connection_count())  # 출력: 2
정적 메서드 (self나 Cls 사용하지 않음)
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
class MathOperations:
    @staticmethod
    def add(x, y):  # 정적 메서드
        return x + y
    
    @staticmethod
    def is_positive(number):  # 정적 메서드
        return number > 0

# 사용 예시
print(MathOperations.add(5, 3))  # 출력: 8
print(MathOperations.is_positive(-1))  # 출력: False

Self와 Cls를 사용하는 상황 구분

메서드 종류데코레이터첫 번째 매개변수사용하는 경우
인스턴스 메서드없음self인스턴스 상태를 수정하거나 접근할 때
클래스 메서드@classmethodcls클래스 상태를 수정하거나 접근할 때
정적 메서드@staticmethod없음클래스/인스턴스 상태와 무관한 기능 구현 시

실제 사용 예시

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
class Student:
    school_name = "Python High School"  # 클래스 변수
    student_count = 0  # 클래스 변수
    
    def __init__(self, name, age):
        self.name = name  # 인스턴스 변수
        self.age = age    # 인스턴스 변수
        Student.student_count += 1
    
    # 인스턴스 메서드
    def introduce(self):
        return f"Hi, I'm {self.name}, {self.age} years old"
    
    # 클래스 메서드
    @classmethod
    def change_school_name(cls, new_name):
        cls.school_name = new_name
    
    # 클래스 메서드 - 대체 생성자
    @classmethod
    def from_string(cls, string):
        name, age = string.split(',')
        return cls(name, int(age))
    
    # 정적 메서드
    @staticmethod
    def is_adult(age):
        return age >= 18

# 사용 예시
# 1. 인스턴스 메서드 사용
student = Student("John", 20)
print(student.introduce())  # 출력: Hi, I'm John, 20 years old

# 2. 클래스 메서드 사용
Student.change_school_name("Python University")
print(Student.school_name)  # 출력: Python University

# 3. 대체 생성자 사용
new_student = Student.from_string("Jane")
print(new_student.introduce())  # 출력: Hi, I'm Jane, 19 years old

# 4. 정적 메서드 사용
print(Student.is_adult(20))  # 출력: True

주의사항

  1. self와 cls는 관례적인 이름이지만, 다른 이름을 사용할 수도 있다 (하지만 권장하지 않음)
  2. 인스턴스 메서드는 self를 통해 클래스 변수에도 접근할 수 있다
  3. 클래스 메서드는 인스턴스 변수에 접근할 수 없다
  4. 정적 메서드는 클래스/인스턴스 변수 모두에 접근할 수 없다

참고 및 출처