파이썬의 First-Citizen(일급시민), Closure, Decorator

1. First-Citizen

정의 : First-Citizen은 특정 자료형을 의미하는 것이 아니고 위 3가지 조건을 만족하면 First-Citizen이다.

First-Citizen의 조건
- 변수나 데이터에 할당할 수 있다.
- 객체의 인자로 전달할 수 있다.
- 객체의 리턴값으로 리턴할 수 있다.

function_name / function_name() 에 대한 차이를 먼저 알아야 함.

파이썬에서는 함수도 일급시민이 될 수 있다.

예를들어 아래 코드를 보자.
def some_func(number):
def square():
print(number * number)
return square

some_func(4)를 호출하면 square 함수를 반환한다.
square 함수의 실행 결과값이 아니라 함수를 반환한다.


2. Closure

(먼저 일급시민에 대한 이해가 필요하다)

정의 : 외부 함수의 실행이 종료된 후에도, 내부 함수가 local scope내의 변수(외부함수에서 정해진 변수라던가)를 기억하고 접근할 수 있는 경우를 의미.
* 쉽게 말해 외부함수에 의해 환경이 설정 된 내부 함수를 의미한다.

Closure is an inner function that remembers and has access to variables in the local scope in which it was created even after the outer function has finished executing.
Closure closes over the free variables from their environment.


예를들어, 아래와 같은 함수가 있을 때

def outer_func(msg):
message = msg
def inner_func():
print(message)
return inner_func

hi_func = outer_func("Hi")
hello_func = outer_func("Hello")
라고 하면 각각 message가 Hi로 세팅 된, Hello로 세팅 된 클로져가 생성이 되며 재사용할 수 있다.
• outer_func의 경우엔 message라는 변수를 free variable이라고 함.

3. Decorator

정의 : 데코레이터란 다른 함수를 인자로 받는 함수이다.
다른 함수를 인자로 받고 그 함수의 결과값을 돌려주면 그냥 일반 함수와 다를바 없지만, 데코레이터는 일반 함수의 결과값을 돌려주는 동시에 부가 기능을 "첨가(decorate)"해서 오리지날 함수의 결과값과 추가 결과를 return한다.

Decorator is function that takes another function as an argument and returns another function.
Why do we use? To easily add functionality to existing functions by adding that functionality to our wrapper


데코레이터 예제 함수 ===============================================

def decorator_function(original_function):
def wrapper_function():
print("wrapper executed this before {}".format(original_function.__name__))
return original_function()
return wrapper_function

def display():
print("display function ran")

@decorator_function
def hide():
print("hide function ran")

데코레이터의 활용==============================================

사실 데코레이터는 이것과 같다.
decorated_display = decorator_function(display)
decorated_display()

하지만 이렇게 쓰면 길다. 따라서 hide 함수에는 편하게 @decorator_function을 붙였다.
@decorator_function
def hide():
print("hide function ran")

1. 만약 inner 함수가 인자를 받는 함수라면?

데코레이터의 wrapper 함수와 original 함수를 아래와 같이 어느 인자나 받도록 바꿔주면 된다.

def decorator_function(original_function):
def wrapper_function(*args, **kwargs):
print("wrapper executed this before {}".format(original_function.__name__))
return original_function(*args, **kwargs)
return wrapper_function

2. 클래스 데코레이터

class decorator_class(object):
def __init__(self, original_function):
self.original_function = original_function

def __call__(self, *args, **kwargs):
print("call method executed this before {}".format(self.original_function.__name__))
return self.original_function(*args, **kwargs)

일반 데코레이터 함수와 똑같이 작동한다.


3. 데코레이터 중첩

데코레이터를 중첩한다고 가정하면 아래처럼 그려질 것이다.
@decorator1
@decorator2
def func():
return blah

이 때 decorator1은 decorator2의 wrapper함수가 마치 오리지날 함수인 것처럼 착각을 하게되고 decorator2의 wrapper함수에 데코레이팅을 하게된다.

그런 상황을 방지하려면 functool모듈에서 wraps 함수를 import하고 모든 데코레이터의 wrapper함수 인자로 실행하고자 하는 original function를 인자로 전달하면 된다.

예시)

from functools import wraps

def my_logger(orig_func):
import logging
logging.basicConfig(filename="{}.log".format(orig_func.__name__), level=logging.INFO)
@wraps(orig_func)
def wrapper(*args, **kwargs):
logging.info("Ran with args : {}, and kwargs: {}".format(args, kwargs))
return orig_func(*args, **kwargs)
return wrapper





댓글

이 블로그의 인기 게시물

로컬 Tomcat 서버 실행속도 개선

2019.05.23 - SQLAlchemy 의 객체 상태 관리 (expire, refresh, flush, commit) 에 관한 이해

2020.02.17 Python의 multiprocessing 중 Pool.map(), chunksize에 관한 내용