파이썬 함수는 객체다!
def shout(word="yes"):
return word.capitalize()+"!"
print(shout()) #'Yes!'
scream = shout # 함수 할당
print(scream()) # 'Yes!'
del shout # 함수 지움
try:
print(shout())
except NameError as e:
print(e)
#outputs: "name 'shout' is not defined"
print(scream()) # 'Yes!' 접근 가능!
def doSomethingBefore(func):
print("I do something before then I call the function you gave me")
def inner():
print('innner')
print(func())
return inner
doSomethingBefore(scream)()
#I do something before then I call the function you gave me
# Yes!
# inner
데코레이터를 이해하기 전에 알아둘 내용
return
값이 될 수 있다.위 코드에 다 나와있다.
- 데코레이터란?
decorators are “wrappers”, which means that they let you execute code before and after the function they decorate without modifying the function itself.
즉 데코레이터란 함수 시작 전, 후에 코드를 실행해주는wrapper
라고 생각해주면 된다.
손으로 만드는 데코레이터와 실제 데코레이터를 살펴보자
def my_new_decorator(a_function_to_decorate):
def the_wrapper_around_the_original_function():
print("Before the function runs")
a_function_to_decorate()
print("After the function runs")
return the_wrapper_around_the_original_function
def a_stand_alone_function():
print("I am a stand alone function, don't you dare modify me")
a_stand_alone_function_decorated = my_new_decorator(a_stand_alone_function)
a_stand_alone_function_decorated()
#Before the function runs
#I am a stand alone function, don't you dare modify me
#After the function runs
@my_new_decorator # 데코레이터! 위에서 함수에 함수를 넣어준 코드와 동일한 역할을 한다!
def another_stand_alone_function():
print("Leave me alone")
another_stand_alone_function()
#Before the function runs
#Leave me alone
#After the function runs
@func
을 하게 되면 코드를 감싸는 것과 동일해진다! 함수에 함수를 넣어준다고 생각하면 된다!
데코레이터는 여러개 못 사용할까? 사용할 수 있다! 함수를 파라미터로 받은 함수를 다시 파라미터로 받은 함수를 생각해보자
def bread(func):
def wrapper():
print("</''''''\>")
func()
print("<\______/>")
return wrapper
def ingredients(func):
def wrapper():
print("#tomatoes#")
func()
print("~salad~")
return wrapper
def sandwich(food="--ham--"):
print(food)
sandwich = bread(ingredients(sandwich))
sandwich()
#</''''''\>
# #tomatoes#
# --ham--
# ~salad~
#<\______/>
이제 위 코드를 간단하게 바꿔보자
# sandwich = bread(ingredients(sandwich))
@bread
@ingredients
def sandwich(food="--ham--"):
print(food)
bread
에 ingredient
를 넣고 ingredient
에 sandwich
를 넣어주었다.
천천히 가보자. 데코레이팅 당하는 함수에 파라미터를 넘겨 주는 것 부터 보자!
def a_decorator_passing_arguments(function_to_decorate):
def a_wrapper_accepting_arguments(arg1, arg2):
print("I got args! Look: {0}, {1}".format(arg1, arg2))
function_to_decorate(arg1, arg2)
return a_wrapper_accepting_arguments
@a_decorator_passing_arguments
def print_full_name(first_name, last_name):
print("My name is {0} {1}".format(first_name, last_name))
print_full_name("Peter", "Venkman") # -> a_wrapper_accepting_arguments("Peter", "Venkman")이렇게 전달 된다.
#I got args! Look: Peter Venkman
#My name is Peter Venkman
#데코레이터가 없다는 가정하에
a = a_decorator_passing_arguments(print_full_name) # return a_wrapper_accepting_arguments
print_full_name
== 데코레이터 없는 a
이라고할 수 있다. a
는 a_wrapper_accepting_arguments
를 return하기 때문에 print_full_name
에 파라미터를 넣어주기 위해서는 a_wrapper_accepting_arguments
에 인자를 넣어줘야 한다!
메소드에서도 사용할 수 있다. 처음 파라미터가 self
라는 것만 기억하자! 이제서야 말하는데 데코레이터된 함수를 디버깅 해보면 상당히 재미있다. 이해도 더 쉬우니 해보는 것을 추천한다. 아래 코드는 이해하는데 조금 헷갈렸다.
def method_friendly_decorator(method_to_decorate):
def wrapper(self, lie):
lie = lie - 3 # very friendly, decrease age even more :-)
return method_to_decorate(self, lie)
return wrapper
class Lucy(object):
def __init__(self):
self.age = 32
@method_friendly_decorator
def sayYourAge(self, lie):
print("I am {0}, what did you think?".format(self.age + lie))
l = Lucy()
l.sayYourAge(-3)
#I am 26, what did you think?
데코레이터가 없고 method_friendly_decorator(sayYourAge)
이렇게 있다고 생각해보자. wrapper
에서 return
이 없으면 어떻게 될가? sayYourAge
가 실행되지 않을 것이다.
이제 데코레이터에 파라미터를 넘기러 가보자! 아래 코드의 과정을 하나하나 따라가다보면 데코레이터에 인자를 넘겨주는 방법을 이해할 수 있다.
def decorator_maker():
print("I make decorators! I am executed only once: "
"when you make me create a decorator.")
def my_decorator(func):
print("I am a decorator! I am executed only when you decorate a function.")
def wrapped():
print("I am the wrapper around the decorated function. "
"I am called when you call the decorated function. "
"As the wrapper, I return the RESULT of the decorated function.")
return func()
print("As the decorator, I return the wrapped function.")
return wrapped
print("As a decorator maker, I return a decorator")
return my_decorator
new_decorator = decorator_maker() # -> my_decorator
#I make decorators! I am executed only once: when you make me create a decorator.
#As a decorator maker, I return a decorator
def decorated_function():
print("I am the decorated function.")
decorated_function = new_decorator(decorated_function) # -> wrapped(decorated_function)
#outputs:
#I am a decorator! I am executed only when you decorate a function.
#As the decorator, I return the wrapped function
# Let’s call the function:
decorated_function()
#outputs:
#I am the wrapper around the decorated function. I am called when you call the decorated function.
#As the wrapper, I return the RESULT of the decorated function.
#I am the decorated function.
위 과정이 아래 코드와 동일하다.
@decorator_maker() # decorateor_maker를 실행했다고 생각하면 편하다.
def decorated_function():
print("I am the decorated function.")
#outputs:
#I make decorators! I am executed only once: when you make me create a decorator.
#As a decorator maker, I return a decorator
#I am a decorator! I am executed only when you decorate a function.
#As the decorator, I return the wrapped function.
#Eventually:
decorated_function()
#outputs:
#I am the wrapper around the decorated function. I am called when you call the decorated function.
#As the wrapper, I return the RESULT of the decorated function.
#I am the decorated function.
이제 실제 파라미터를 넣어주자. 최상위 함수에서 파라미터를 받기 때문에 어디서나 인자를 사용할 수 있다!
wrapped
, decorator_maker_with_argumets
둘 다 파라미터를 받았다는 사실을 염두하자! wrapped
는 데코레이터를 받는 함수에 대한 파라미터이고, decorator_maker_with_argumets
는 데코레이터에 대한 파라미터다.
decorator_arg1
, decorator_arg2
대신 *args
, **kwargs
를 사용해도 된다.
def decorator_maker_with_arguments(decorator_arg1, decorator_arg2):
print("I make decorators! And I accept arguments: {0}, {1}".format(decorator_arg1, decorator_arg2))
def my_decorator(func):
print("I am the decorator. Somehow you passed me arguments: {0}, {1}".format(decorator_arg1, decorator_arg2))
# Don't confuse decorator arguments and function arguments!
def wrapped(function_arg1, function_arg2) :
print("I am the wrapper around the decorated function.\n"
"I can access all the variables\n"
"\t- from the decorator: {0} {1}\n"
"\t- from the function call: {2} {3}\n"
"Then I can pass them to the decorated function"
.format(decorator_arg1, decorator_arg2,
function_arg1, function_arg2))
return func(function_arg1, function_arg2)
return wrapped
return my_decorator
@decorator_maker_with_arguments("Leonard", "Sheldon")
def decorated_function_with_arguments(function_arg1, function_arg2):
print("I am the decorated function and only knows about my arguments: {0}"
" {1}".format(function_arg1, function_arg2))
# I make decorators! And I accept arguments: Leonard, Sheldon
#I am the decorator. Somehow you passed me arguments: Leonard, Sheldon
decorated_function_with_arguments("Rajesh", "Howard")
#I am the wrapper around the decorated function.
#I can access all the variables
# - from the decorator: Leonard Sheldon
# - from the function call: Rajesh Howard
#Then I can pass them to the decorated function
#I am the decorated function and only knows about my arguments: Rajesh Howard
데코레이터를 데코레이터로 꾸며보자!
주의! 머리가 아플 수 있음
def decorator_with_args(decorator_to_enhance):
def decorator_maker(*args, **kwargs):
def decorator_wrapper(func):
return decorator_to_enhance(func, *args, **kwargs)
return decorator_wrapper
return decorator_maker
@decorator_with_args
def decorated_decorator(func, *args, **kwargs):
def wrapper(function_arg1, function_arg2):
print("Decorated with {0} {1}".format(args, kwargs))
return func(function_arg1, function_arg2)
return wrapper
@decorated_decorator(42, 404, 1024)
def decorated_function(function_arg1, function_arg2):
print("Hello {0} {1}".format(function_arg1, function_arg2))
decorated_function("Universe and", "everything")
#outputs:
#Decorated with (42, 404, 1024) {}
#Hello Universe and everything
이해하기까지 꽤나 시간이 걸렸다. 코드를 한줄 한 줄 따라가며 생각해보자. @decorator_with_args
가 제일 먼저 호출되며decorator_to_enhance
여기에 decorated_decorator
가 들어가고 decorator_maker
를 반환 받을 것이다. 그 다음에는 @decorated_decorator(42, 404, 1024)
를 호출하며 decorator_maker
에 (42,404,1024)를 파라미터로 넣으며 함수를 싱행 시킨다. 그 결과decorator_wrapper
가 실행되고 decorator_wrapper
의 func
파라미터에 decorated_function
이 들어간다. 그리고 decorator_to_enhance(func, *args, **kwargs)
를 호출하게 되니 이는 wrapper
를 반환한다.
decorated_function("Universe and", "everything")
를 싱행하게 되면 wrapper
가 호출 되면서 print
를 하고 func
를 호출하여 decorated_funcion
을 호출한다.
아래는 같은 코드에 주석을 달아보았다.
def decorator_with_args(decorator_to_enhance):
def decorator_maker(*args, **kwargs):
print('@decorated_decorator(42, 404, 1024) 실행하면 (42,404,1024) 파라미터 여기서 받음')
def decorator_wrapper(func):
print('func', func)
print("@decorated_decorator(42, 404, 1024)는 이 함수 실행까지 아래 return도 실행함 ")
return decorator_to_enhance(func, *args, **kwargs)
return decorator_wrapper
print("first_start, @decorator_with_args 실행때 여기 까지")
return decorator_maker
@decorator_with_args
def decorated_decorator(func, *args, **kwargs):
def wrapper(function_arg1, function_arg2):
print("decorated_function(Universe and, everything) 실행하면 여기 들어와서 나머지 모두 실행" )
print("Decorated with {0} {1}".format(args, kwargs))
return func(function_arg1, function_arg2) # decorated_function 실행함
print("@decorated_decorator(42, 404, 1024) 실행 중 아래 wrapper를 return 값으로 가지고 있음!")
return wrapper
@decorated_decorator(42, 404, 1024)
def decorated_function(function_arg1, function_arg2):
print("Hello {0} {1}".format(function_arg1, function_arg2))
decorated_function("Universe and", "everything")
그럼 이제 아래 코드를 이해할 수 있다.
from typing import Union
from fastapi import FastAPI
app = FastAPI()
@app.get("/", response_class=HTMLResponse)
async def main(request: Request,message:Union[None, str] = None):
return templates.TemplateResponse('main.html',{"request" : request,"message" :message})
FastAPI 코드를 자세히 보면 알 수 있겠지만 아마 아래와 같은 코드가 아닐까?
def get(*param, **kwargs):
# do somthing with param,kwargs
print(param,kwargs)
def decorator(func):
def wrapper(*args):
return func(args)
return wrapper
return decorator
이를 실행해봄 코드
@get('/') # ('/',) {}
def test(*args):
print(args)
test(33) # ((33,),)
이해가 되었기를 바라며!
사실 아래 질문과 답변에 모든 것이 나와있다
코드출처 및 참고링크