language/Python
[Python] closure(클로저)와 Decorator(데코레이터), 로깅(logging) 개념과 예제
스파이디웹
2022. 2. 28. 17:27
728x90
1. closure(클로저)란?
내부 함수를 결과로 반환할 때, 그 내부 함수를 closure라고 한다.
사용 되는 곳
- 콜백 함수에 사용
- 지연된 평가
- 데코레이터 함수
def add(x, y):
def do_add():
print('Adding', x, y)
return x + y
return do_add
do_add()함수가 closure함수
클로저는 나중에 함수가 올바로 작동하는 데 필요한 모든 변수의 값을 유지한다.
클로저를 함수와 그 함수가 의존하는 변숫값을 저장하는 환경이 합쳐진 것으로 생각할 수 있다.
closure를 함수로 구현
class Outer:
def __init__(self, num):
self.num = num
def __call__(self):
print(self.num)
- Outer 클래스에 대한 객체를 생성하고 이를 호출
- 클래스에 __call__ 메서드를 정의해두면 '객체( )'와 같이 사용할 때 __call__ 메서드가 호출
- 사실 함수도 클래스의 객체이며 우리가 함수를 호출할 때 '함수이름( )'과 같이 사용할 수 있었던 이유가 바로 __call__ 메서드 덕분
2. decorator(데코레이터)란?
데코레이터를 설명하기전에 logging이라는 것에 대해 잠깐 설명하고 데코레이터를 정리하겠습니다.
logging이란 프로그램을 작성할 때 간단히 함수 호출 상태나 함수의 리턴 값, 함수의 처리시간 등을 확인하기 위해 print를 사용하는 것.
def add(x, y):
return x + y
#로깅 추가
def add(x, y):
print('Calling add')
return x + y
def sub(x, y):
print('Calling sub')
return x - y
위와 같은 함수에 로깅이 추가 되었을 때 코드가 반복되는 일이 발생합니다.
따라서 함수에 로깅을 추가해주는 wrapper함수를 closure함수로 만듭니다.
def logged(func):
def wrapper(*args, **kwargs):
print('Calling', func.__name__)
return func(*args, **kwargs)
return wrapper
def add(x, y):
return x + y
logged_add = logged(add)
print(logged_add(3,4))
#Calling add 출력
#7 출력
wrapper는 다른 함수를 감싸는 함수이다.
데코레이터는 함수 주변에 래퍼를 두는 전용 구문이라고 생각하면 됩니다.
아래의 코드는 위의 코드와 동일하게 작동합니다.
def logged(func):
def wrapper(*args, **kwargs):
print('Calling', func.__name__)
return func(*args, **kwargs)
return wrapper
@logged
def add(x, y):
return x + y
print(add(3,4))
#Calling add 출력
#7 출력
@logged와 같은 문법을 함수를 데코레이트 한다 라고합니다.
logging을 편하게 해주는 logging 모듈
파이썬의 logging 모듈을 사용하면 print 함수를 사용하거나 파일 입출력을 사용하는 것보다 편리하게 로깅할 수 있습니다.
import logging
def hap(a, b):
ret = a + b
logging.info(f"input: {a} {b}, output={ret}")#print 대신에 logging.info()사용
return ret
#logging.info()의 인자는 문자열 타입이어야 함
result = hap(3, 4)
#출력결과는 없다(logging.info가 출력되지 않는다.)
- 로깅 모듈에는 다음과 같이 로깅 레벨이 존재
- 로깅 모듈의 기본 레벨은 WARNING으로 설정
- info 함수는 설정 레벨인 WARNING 보다 높으므로 해당 메세지가 출력되지 않는 것
레벨 | 로깅함수 | 사용할 때 |
DEBUG | debug() | 상세한 정보를 출력 |
INFO | info() | 예상대로 작동하는지를 확인 |
WARNING | warning() | 소프트웨어는 정상 동작하는데 예상치 못한 일이 발생한 것에 대해 표시 |
ERROR | error() | 소프트웨어의 일부가 정상적으로 동작하지 않는 경우에 대해 표시 |
CRITICAL | critical() | 심각한 에러 상황에 대해 표시 |
import logging
logging.basicConfig(level=logging.INFO)
def hap(a, b):
ret = a + b
logging.info(f"input: {a} {b}, output={ret}")
return ret
result = hap(3, 4)
#INFO:root:input: 3 4, output=7 와 같이 출력
다음과 같이 로거 객체를 만들어 사용할 수도 있습니다.
log = logging.getLogger(__name__)
log.critical(message [, args])
log.error(message [, args])
log.warning(message [, args])
log.info(message [, args])
log.debug(message [, args])
3. 데코레이트 된 메소드
메소드 정의와 함께 사용할 수 있는 대표적인 빌트인(미리 정의된) 데코레이션이 몇 가지 있다.
1) 정적 메소드
- @staticmethod는 정적클래스 메소드를 정의하는 데 사용
- 정적 메소드는 클래스에 속한 함수이지만 인스턴스에 대해 연산을 하지 않음
- 클래스를 위한 내부 지원 코드를 구현하는 데 정적 메소드를 종종 사용
- 예) 생성한 인스턴스를 관리하는 코드(메모리 관리, 시스템 자원, 지속성, 잠금 등)
class Foo(object):
@staticmethod
def bar(x):
print('x =', x)
>>> Foo.bar(2)
x=2
2) 클래스 메소드
- @classmethod는 클래스 메소드를 정의하는 데 사용
- 인스턴스 대신 class 객체를 첫 번째 매개변수로 받음
- 생성자(constructor)의 대안으로 클래스 메소드를 종종 사용
- 상속과 같은 문제에 있어서 활용됨
class Foo:
def bar(self):
print(self)
@classmethod
def spam(cls):
print(cls)
>>> f = Foo()
>>> f.bar()
<__main__.Foo object at 0x971690> # 인스턴스 `f`
>>> Foo.spam()
<class '__main__.Foo'> # `Foo` 클래스
############################
class Date:
def __init__(self,year,month,day):
self.year = year
self.month = month
self.day = day
@classmethod
def today(cls):
# 클래스가 어떻게 인자로 전달되는지 보라
tm = time.localtime()
# 새 인스턴스를 생성하는 데 사용됨
return cls(tm.tm_year, tm.tm_mon, tm.tm_mday)
d = Date.today()
3) Property
- @property는 메소드를 마치 속성처럼 사용할 수 있게 해줌
- 함수를 호출하는 의믜인 ()를 붙이지 않고 변수처럼 사용할 수 있게 됨
class Car:
def __init__(self, model):
self.model = model
@property
def get_model(self):
return self.model
c = Car("GV80")
print(c.get_model) # c.get_model()이 아님
#GV80출력
참조:
728x90