본문 바로가기

Programming👩🏻‍💻/Python

[Python] - decorator 데코레이터 개념 @staticmethod, @classmethod (1)

1. @decorator

데코레이터는 다른 함수를 감싸는 함수로, 기존 코드를 수정하지 않고도 동작을 추가하거나 변경할 수 있습니다.

파이썬에서는 @함수명 문법을 사용해 데코레이터를 적용합니다.

데코레이터는 함수뿐만 아니라 클래스에도 적용할 수 있습니다.

 

1. 1 @decorator 함수

데코레이터는 함수를 매개변수로 받아 내부에서 새로운 함수를 정의하고 반환합니다.

 

def my_decorator(func):
    def wrapper():
        print("Something before the function runs")
        func()  # 원래 함수 호출
        print("Something after the function runs")
    return wrapper
    
    
def say_hello():
    print("Hello!")

아래 예시로 my_decorator, say_hello 두 함수가 있다고 치면 my_decorator함수에서 say_hello 함수가 호출되기 전에 중간에 메세지를 실행하고자 했을때

 

say_hello = my_decorator(say_hello)

say_hello()

 

 

이렇게 함수를 대입하여서 출력을 해주면 되는데, 문법이 조금 번거로운 감이 있습니다.

 

그럴때, 데코레이터를 사용하면 문법이 간결해지는데요,

 

[@데코레이터이름] 을 함수 위에 붙이면 데코레이터가 적용됩니다.

say_hello라는 함수 위에 @my_decorator 데코레이팅을 해주었으므로, 

my_decorator 함수 인자에 say_hello 함수가 파라메터로 전달 되어 my_decorator(say_hello) 로 실행이 되겠네요.

@my_decorator
def say_hello():
    print("Hello!")

say_hello()

그리고 호출할때는 say_hello()로 간단하게 호출이 가능해집니다.

 

정리하면 say_hello = my_decorator(say_hello) 이 문법을 간결하게 해주기 위해서 사용한다라고 생각하면 되겠네요.

 

출력 결과는 wapper 함수 내부에서 print 실행 구문 사이에 func() 함수가 정의되어 있으므로 아래와 같이 되겠습니다.

# 출력
Something before the function runs
Hello!
Something after the function runs

 

 

이 예제만으로는 사실 무슨 역할을 하는지 와닿지 않는데요, -_- 좀 더 실용적인 코드로 설명 해보겠습니다.

 

 

아래는 함수 실행 소요시간을 측정을 위해 정의한 함수입니다.

sample_function 함수에서 전달된 수치 범위 만큼 반복을 실행합니다. 해당 함수가 실행 되기 전에 start time, 반복문을 마친 후에 end time을 계산하는 목적입니다.

def timing_decorator(func):
    def wrapper(*args, **kwargs):
        # 함수 실행 전 시간 측정
        start_time = time.time()
        result = func(*args, **kwargs)
        # 함수 실행 후 시간 측정
        end_time = time.time()
        # 함수 실행 시간 출력
        print(
            f"[알림] 함수명: <{func.__name__}> 실행에 {end_time - start_time:.6f} 초 소요되었습니다."
        )
        return result

    return wrapper

@timing_decorator
def sample_function(n):
    sum = 0
    for i in range(n):
        sum += i
    return sum
    
 
 # 함수 실행 및 시간 측정
 sample_function(1000)
 
 # 출력
 [알림] 함수명: <sample_function> 실행에 0.0000XX 초 소요되었습니다.
499500

@timing_decorator로 정의해주어서 timing_decorator함수 인자에 sample_function이 전달 되게되고,

wapper함수에서 순차적으로 실행됩니다.

 

이렇게 다른 함수에서도 함수 실행 전에 시작,끝 시간을 측정 수행이 필요하다면 @timeing_decorator를 넣어주면 되겠죠?

 

 

 

이제 파이선 내장 기능의 데코레이터 메서드를 개념을 살펴보겠습니다.

2. built-in feature 데코레이터 

2.2 @staticmethod

@staticmethod는 클래스에 소속된 메서드가 아니라 일반 함수를 정의하겠다는 의미로 생각하면 됩니다.

 

일반 메서드는 인스턴스 생성 후, 함수 호출 하지만

class MyClass:
    def instance_method(self):
        print(f"This is an instance method, self = {self}")

obj = MyClass()
obj.instance_method()  # 인스턴스 생성 후 호출

 

 

@staticmethod는 인스턴스 없이 클래스.함수명으로 호출이 가능합니다.

class MyClass:
    @staticmethod
    def static_method():
        print("This is a static method, no instance required")

MyClass.static_method()  # 인스턴스 없이 호출

 

클래스 상태와 무관한 경우, 정적 메서드(@staticmethod)를 사용합니다.

 

2.2.1 클래스 상태

클래스 상태란 클래스 변수와 인스턴스 속성을 통해 접근하거나 수정할 수 있는 데이터를 의미합니다.

 

클래스 변수는 클래스 정의에서 메서드 밖에 존재하는 변수입니다. 해당 클래스를 사용하는 공용 변수입니다.

'클래스명.변수명'으로 액세스 할 수 있습니다.

 

아래 예제의 경우 class_variable가 공용 변수로 MyClass.class_variable로 접근 할 수 있겠습니다.

class MyClass:
    class_variable = "I belong to the class"  # 클래스 변수

    def __init__(self):
        self.instance_variable = "I belong to an instance"  # 인스턴스 변수

# 클래스 상태 접근
print(MyClass.class_variable)  # "I belong to the class"

obj = MyClass()
print(obj.instance_variable)  # "I belong to an instance"

 

 

인스턴스 변수는  각 객체별로 서로 다른 값을 갖는 변수입니다.

클래스 내부에서 인스턴스 변수에 접근하려면 self 키워드를 사용하여 "self.변수명"으로 접근합니다.

class Rectangle:
    def __init__(self, width, height):
        self.width = width  # 인스턴스 변수
        self.height = height  # 인스턴스 변수

    def area(self):
        return self.width * self.height  # self를 통해 접근

 

클래스 외부에서는 객체.인스턴스변수를 사용하여 접근합니다.

rect = Rectangle(10, 5)  # Rectangle 클래스의 인스턴스 생성
print(rect.width)  # 객체명.rect.width로 접근
print(rect.height)  # 객체명.rect.height로 접근

 

 

2.3 @classmethod

@classmethod는 클래스 자체를 매개변수로 전달 받습니다. 관례적으로 cls 키워드를 사용합니다. cls는 호출된 클래스 자체를 뜻합니다.

class Korean:
    country = "korea"  # 클래스 변수

    # 일반 메서드
    def i_change(self, name):
        self.country = name  # 인스턴스 변수로 접근

    # 클래스 메서드
    @classmethod
    def c_change(cls, name):
        cls.country = name  # 클래스 변수로 접근

 

 

i_change 메서드는 self.country 인스턴스 변수로 작동하여 특정 인스턴스 변수만 변경합니다. 클래스 변수 country와는 독립적입니다.

 

# 인스턴스 생성
person1 = Korean()
person2 = Korean()

# 인스턴스 메서드 호출
person1.i_change("US")

# 확인
print(person1.country)  # "US" (person1의 인스턴스 변수만 변경됨)
print(person2.country)  # "korea" (person2의 인스턴스 변수는 변경되지 않음)
print(Korean.country)   # "korea" (클래스 변수는 변경되지 않음)

 

 

c_change 메서드는 cls.country를 통해 클래스 변수에 직접 접근하여 변경합니다.

# 클래스 메서드 호출
Korean.c_change("china")

# 확인
print(Korean.country)   # "china" (클래스 변수 변경됨)
person1 = Korean()
person2 = Korean()
print(person1.country)  # "china" (모든 인스턴스에 영향)
print(person2.country)  # "china"

 

 

정리하면 일반 메서드는 특정 인스턴스 변수를 변경하고 클래스 변수는 영향 받지 않습니다.

@classmethod는 클래스 변수를 변경하고 모든 인스턴스에서 해당 변경 값을 참조합니다.

@staticmethod클래스나 인스턴스에 의존하지 않는 메서드입니다.

'Programming👩🏻‍💻 > Python' 카테고리의 다른 글