일 | 월 | 화 | 수 | 목 | 금 | 토 |
---|---|---|---|---|---|---|
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 |
- 매터
- 월패드
- 애플
- 티스토리챌린지
- 해외주식
- ConnectedHomeIP
- RS-485
- matter
- 국내주식
- 공모주
- esp32
- 미국주식
- Espressif
- 코스피
- SK텔레콤
- 나스닥
- raspberry pi
- Apple
- 힐스테이트 광교산
- cluster
- homebridge
- MQTT
- Bestin
- Home Assistant
- Python
- 홈네트워크
- 현대통신
- 배당
- 파이썬
- 오블완
- Today
- Total
YOGYUI
Python - Decorator in Class 본문
파이썬에서 공통으로 사용되는 구문들은 decorator를 활용해서 코드를 간소화할 수 있다
decorator를 활용한 로깅 예시는 다음과 같다
import logging
def log(func):
"""
Log what function is called
"""
def wrap_log(*args, **kwargs):
name = func.__name__
logger = logging.getLogger(name)
logger.setLevel(logging.INFO)
# add file handler
fh = logging.FileHandler("%s.log" % name)
fmt = '%(asctime)s - %(name)s - %(levelname)s - %(message)s'
formatter = logging.Formatter(fmt)
fh.setFormatter(formatter)
logger.addHandler(fh)
logger.info("Running function: %s" % name)
result = func(*args, **kwargs)
logger.info("Result: %s" % result)
return func
return wrap_log
@log
def double_function(a):
"""
Double the input parameter
"""
return a*2
if __name__ == "__main__":
value = double_function(2)
(출처: https://python101.pythonlibrary.org/chapter25_decorators.html)
클래스 내부 메서드들의 공통 구문들도 decorator를 활용해 코드를 간소화할 수 있는데, 일반적인 decorator 사용법에 조금의 트릭만 가미하면 된다
예를 위해 다음과 같이 클래스 MyClass1을 구현했다
import datetime
class MyClass1:
name = 'YogyuiClass1'
def method1(self):
print('Called Time: ' + datetime.datetime.now().strftime('%Y-%m-%d %H:%M:%S'))
# print(f'{type(self).__name__}::method1 is called')
print(f'{self.name}::method1 is called')
def method2(self, value: int) -> int:
print('Called Time: ' + datetime.datetime.now().strftime('%Y-%m-%d %H:%M:%S'))
# print(f'{type(self).__name__}::method2 is called')
print(f'{self.name}::method2 is called')
return value + 1
def method3(self, value1: int, value2: int) -> float:
print('Called Time: ' + datetime.datetime.now().strftime('%Y-%m-%d %H:%M:%S'))
# print(f'{type(self).__name__}::method3 is called')
print(f'{self.name}::method3 is called')
try:
return value1 / value2
except ZeroDivisionError:
print('ZeroDivisionError Occurred')
method1은 함수 인자 없이 단순 문자열 출력, method2는 정수형 인자를 입력받아서 1 더한 값을 리턴, method2는 정수형 인자 2개를 입력 받아서 나눈 값을 리턴하도록 작성했으며, 모든 메서드들은 호출 시 호출 시점을 출력하도록 구현했다
각 메서드들을 호출해보자
In [1]: test = MyClass1()
In [2]: print(test.method1())
Called Time: 2021-07-05 15:39:33
YogyuiClass1::method1 is called
None
In [3]: print(test.method2(1))
Called Time: 2021-07-05 15:39:37
YogyuiClass1::method2 is called
2
In [4]: print(test.method3(1, 2))
Called Time: 2021-07-05 15:39:42
YogyuiClass1::method3 is called
0.5
In [5]: print(test.method3(1, 0))
Called Time: 2021-07-05 15:39:45
YogyuiClass1::method3 is called
ZeroDivisionError Occurred
None
모든 메서드들에 대해 호출 시점을 출력하는 함수를 추가해야 한다고 생각하면 코드가 길어질 수 밖에 없다
이 때, 다음과 같이 클래스 내부에서 객체를 참조할 수 있는 Inner Class를 구현한 후, decorator로 활용하면 공통 구문을 손쉽게 하나로 합칠 수 있다
MyClass1 메서드들의 공통 구문을 하나로 묶은 MyClass2를 다음과 같이 구현해보았다
class MyClass2:
name = 'YogyuiClass2'
class MyDecorator(object):
@staticmethod
def decorator(func):
def wrapper(self, *args, **kwargs):
print('Called Time: ' + datetime.datetime.now().strftime('%Y-%m-%d %H:%M:%S'))
print(f'{self.name}::{func.__name__} is called')
try:
return func(self, *args, **kwargs)
except ZeroDivisionError:
print('ZeroDivisionError Occurred')
return wrapper
@MyDecorator.decorator
def method1(self):
pass
@MyDecorator.decorator
def method2(self, value: int):
return value + 1
@MyDecorator.decorator
def method3(self, value1: int, value2: int) -> float:
return value1 / value2
In [6]: test = MyClass2()
In [7]: print(test.method1())
Called Time: 2021-07-05 15:47:55
YogyuiClass2::method1 is called
None
In [8]: print(test.method2(1))
Called Time: 2021-07-05 15:47:58
YogyuiClass2::method2 is called
2
In [9]: print(test.method3(1, 2))
Called Time: 2021-07-05 15:48:03
YogyuiClass2::method3 is called
0.5
In [10]: print(test.method3(1, 0))
Called Time: 2021-07-05 15:48:04
YogyuiClass2::method3 is called
ZeroDivisionError Occurred
None
Class 내부에 구현된 inner-Class가 부모(엄밀히 말하면 부모는 아니지만) 객체를 참조하는 약간의 트릭만 추가하면 손쉽게 데코레이터 객체를 만들 수 있고, 메서드들의 공통 구문들을 하나로 묶어서 코드를 간소화할 수 있다 (굳이 functool의 wraps를 사용하지 않아도 직관적으로 구현 가능하다)
개인적인 경험으로는, 디버깅을 위한 logging 구현 시 엄청 편해지고, 위와 같이 try-except문도 decorator단에서 처리할 수 있기 때문에 불필요한 code indent도 방지할 수 있어서 코드가 굉장히 간결해진다 (협업 시 co-worker들은 decorator 구문 신경쓰지 않고 본인의 코드만 구현할 수 있다)
끝~!
[참고]
https://stackoverflow.com/questions/1263451/python-decorators-in-classes
'Software > Python' 카테고리의 다른 글
PyQt5 - Connect pyqtSignal in For loop (lambda problem) (0) | 2021.07.20 |
---|---|
PyQt5 - QTextEdit Tab Width 설정하기 (0) | 2021.07.15 |
PyQt5 - QDoubleSpinbox 더블클릭 시 텍스트 전체선택 (0) | 2021.06.21 |
PyQt5 - QTableWidget Column Header Label 가져오기 (0) | 2021.06.21 |
Python::구조적 패턴 매칭 - 파이썬에서 switch/case문을?! (0) | 2021.03.21 |