본문 바로가기
프로그래밍/Python

[Python] 데코레이터 (@, Decorator)

by Hwan,. 2022. 3. 21.
728x90
반응형

파이썬의 데코레이터란?

먼저 Decorate는 '장식하다' 라는 의미가 있고, 따라서 직역하면 '장식자'라는 의미가 된다. (구글 번역기에서는 a person who decorates something. 로 정의되어 있다.)

파이썬에서도 원래 뜻과 비슷한 의미로 사용되는데 @ 기호로 표현 할 수 있으며, 클래스나 메소드를 정의할 때 많이 사용되어 진다.

데코레이터는 직접 정의하여 사용하거나 미리 정의된 내용을 가져와 사용이 가능하다.

(정의된 데코레이터는 @property, @classmethod, @staticmethod 등이 있다.)

 

데코레이터를 작성하면 원본 메소드의 내용을 수정하지 않고 여러 기능을 추가할 수 있게 된다.

  • 메소드(함수)의 실행시간 측정
  • 실행과 종료에 로그 기능 추가
  • ...

메소드 데코레이터 정의하기

데코레이터는 크게 클래스 데코레이터메소드 데코레이터로 나눠진다.

메소드 데코레이터는 메소드를 장식하기 위한 데코레이터로 기본적인 형태는 아래 코드를 따르게 되며,

def decorator_1(func):
	def wrapped_func():
		func() 
    return wrapped_func()

 

만들어진 데코레이터의 사용은 아래 코드와 같다.

# 메소드 데코레이터 정의
def decorator_1(func):
	def wrapped_func():
		print("start")
		func() 
		print("end")

	return wrapped_func

# 사용
@ decorator_1
def test_func():
	print("logic")

test_func()

 

 

위 사진은 데코레이터를 사용한 결과인데 내용을 보면 가장 마지막 줄에 test_func()을 호출했지만 decorator_1에 내부에 정의된 wrapped_func의 기능이 수행된 것을 볼 수있다.

 

하지만 데코레이터를 사용하는 함수의 매개변수를 바꾸고 실행하면 TypeError가 발생한다.

정상적으로 메소드에 인수를 받아 사용하려면 데코레이터의 수정이 조금 필요하다.

 

아래 코드를 보면 wrapped_func에 *args가 매개변수로 추가되었다.

*을 사용하여 non-keworded arguments 형식으로 인수를 받을 수 있는데 이렇게 사용하면, 키워드가 없는 가변 인수가 되어 여러개의 인수를 받는 것이 가능해진다. (만약 메소드에 키워드가 필요하다면 **(keworded arguments)를 사용) 

def decorator_1(func):
	def wrapped_func(*args):
		print("start - ", func.__name__)
		func(*args) 
		print("end - ", func.__name__)

	return wrapped_func


@ decorator_1
def test_func(str_var, int_tmp, a, b, c):
	print(str_var,  int_tmp, a, b, c)


test_func("test", 12, 1, 2, 3,)

 

위 코드에서 매개변수를 받아 출력을 했지만, test_func 함수의 결과 값은 받아볼 수 없다.

wrapped_func 내부에서 func의 결과 값을 받아 return 해줬다.

def decorator_1(func):
	def wrapped_func(*args):
		print("start - ", func.__name__)
		ret= func(*args) 
		print("end - ", func.__name__)
		return ret
	return wrapped_func


@ decorator_1
def test_func(str_var, int_tmp, a, b, c):
	print(str_var,  int_tmp, a, b, c)
    return a + b


print(test_func("test", 12, 1, 2, 3,))

클래스 데코레이터

 

위에서 작성한 데코레이터를 클래스 내에 넣어 작성해보았다.

메소드 데코레이터와 동일한 기능을 수행하는 것을 볼 수 있다.

class myClass:
	def myClass_LoggingDecorator(func):
		def wrapped_func(*args):
			print("start - ", func.__name__)
			func(*args) 
			print("end - ", func.__name__)
		return wrapped_func

	@ myClass_LoggingDecorator
	def test_func(str_var, int_tmp, a, b, c):
		print(str_var,  int_tmp, a, b, c)


myClass.test_func("test", 12, 1, 2, 3,)


클래스 데코레이터 - 활용

import time
import datetime

class myClass:
	def myClass_RunningtimeDecorator(func):
		def wrap(*args):
			start_r = time.perf_counter()
			start_p = time.process_time()

			ret = func(*args)

			end_r = time.perf_counter()
			end_p = time.process_time()
            
			elapsed_r = end_r - start_r
			elapsed_p = end_p - start_p

			print(f'{func.__name__} : {elapsed_r:.6f}sec (Perf_Counter) / {elapsed_p:.6f}sec (Process Time)')
			return ret
		return wrap

	def myClass_LoggingDecorator(func):
		def wrapped_func(*args):
			file_name = f"log_{datetime.datetime.today().strftime('%Y-%m-%d')}.txt"

			File = open(file_name, "w")
			File.write(f"start - {func.__name__}, DateTime : {datetime.datetime.now()}\n")

			ret = func(*args) 
			
			File.write(f"end - {func.__name__}, DateTime : {datetime.datetime.now()}\n")
			File.close()

			return ret
		return wrapped_func

	#@ myClass_RunningtimeDecorator
	@ myClass_LoggingDecorator
	def test_sum(str_var, a, b):
		print(str_var, a + b)
		return a + b
        

print ("res2 :", myClass.test_sum("res1 :", 1, 2))

LoggingDecorator / RunningtimeDecorator
log file

 

728x90
반응형

'프로그래밍 > Python' 카테고리의 다른 글

[python] JWT 모듈  (0) 2022.09.13
[Python] Yahoo_fin 모듈  (0) 2022.09.11
[Python] Paramiko 모듈  (0) 2022.07.07
[Python] min, max 함수  (0) 2022.03.10
[python] lambda 표현식  (0) 2022.01.07
[python] 실행 시 필요한 패키지 자동 설치  (0) 2022.01.01

댓글