기능 장식가들을 어떻게 만들고 그들을 쇠사슬로 묶는가?
Python에서 다음과 같은 일을 할 두 명의 장식가를 어떻게 만들 수 있을까?
@makebold
@makeitalic
def say():
return "Hello"
...반환해야 할 사항:
"<b><i>Hello</i></b>"
난 만들려는 게 아니야HTML
이런 방식으로 실제 응용을 할 수 있다 - 단지 장식가와 장식가 체인이 어떻게 작용하는지를 이해하려고 할 뿐이다.
만약 당신이 긴 설명을 좋아하지 않는다면, 파올로 베르간티노의 대답을 보라.
장식가 기본사항
Python의 기능은 사물이다.
장식가를 이해하기 위해서는 먼저 기능이 Python의 사물이라는 것을 이해해야 한다.이것은 중요한 결과를 가져온다.간단한 예를 들어 그 이유를 살펴보자.
def shout(word="yes"):
return word.capitalize()+"!"
print(shout())
# outputs : 'Yes!'
# As an object, you can assign the function to a variable like any other object
scream = shout
# Notice we don't use parentheses: we are not calling the function,
# we are putting the function "shout" into the variable "scream".
# It means you can then call "shout" from "scream":
print(scream())
# outputs : 'Yes!'
# More than that, it means you can remove the old name 'shout',
# and the function will still be accessible from 'scream'
del shout
try:
print(shout())
except NameError as e:
print(e)
#outputs: "name 'shout' is not defined"
print(scream())
# outputs: 'Yes!'
이 점을 명심해라.우리는 곧 그것에 대해 되돌아갈 것이다.
파이톤 기능의 또 다른 흥미로운 특성은 그것들이 다른 기능 안에서 정의될 수 있다는 것이다!
def talk():
# You can define a function on the fly in "talk" ...
def whisper(word="yes"):
return word.lower()+"..."
# ... and use it right away!
print(whisper())
# You call "talk", that defines "whisper" EVERY TIME you call it, then
# "whisper" is called in "talk".
talk()
# outputs:
# "yes..."
# But "whisper" DOES NOT EXIST outside "talk":
try:
print(whisper())
except NameError as e:
print(e)
#outputs : "name 'whisper' is not defined"*
#Python's functions are objects
함수 참조
좋아, 아직 여기 있어?이제 재미있는 부분은...
기능이 물건이라는 것을 보셨을 겁니다.따라서 함수:
- 변수에 할당될 수 있다.
- 다른 함수로 정의할 수 있다.
그것은 어떤 기능이 다른 기능을 할 수 있다는 것을 의미한다.
def getTalk(kind="shout"):
# We define functions on the fly
def shout(word="yes"):
return word.capitalize()+"!"
def whisper(word="yes") :
return word.lower()+"..."
# Then we return one of them
if kind == "shout":
# We don't use "()", we are not calling the function,
# we are returning the function object
return shout
else:
return whisper
# How do you use this strange beast?
# Get the function and assign it to a variable
talk = getTalk()
# You can see that "talk" is here a function object:
print(talk)
#outputs : <function shout at 0xb7ea817c>
# The object is the one returned by the function:
print(talk())
#outputs : Yes!
# And you can even use it directly if you feel wild:
print(getTalk("whisper")())
#outputs : yes...
더 있어!
네가 할 수 있으면.return
함수 하나를 매개 변수로 전달할 수 있음:
def doSomethingBefore(func):
print("I do something before then I call the function you gave me")
print(func())
doSomethingBefore(scream)
#outputs:
#I do something before then I call the function you gave me
#Yes!
넌 장식가들을 이해하는 데 필요한 모든 것을 가지고 있어.알다시피, 장식가들은 "wrappers"인데, 이것은 기능 자체를 수정하지 않고 그들이 꾸미는 기능 전후에 코드를 실행할 수 있게 해준다는 것을 의미한다.
수공예 장식가
수동으로 수행하는 방법:
# A decorator is a function that expects ANOTHER function as parameter
def my_shiny_new_decorator(a_function_to_decorate):
# Inside, the decorator defines a function on the fly: the wrapper.
# This function is going to be wrapped around the original function
# so it can execute code before and after it.
def the_wrapper_around_the_original_function():
# Put here the code you want to be executed BEFORE the original function is called
print("Before the function runs")
# Call the function here (using parentheses)
a_function_to_decorate()
# Put here the code you want to be executed AFTER the original function is called
print("After the function runs")
# At this point, "a_function_to_decorate" HAS NEVER BEEN EXECUTED.
# We return the wrapper function we have just created.
# The wrapper contains the function and the code to execute before and after. It’s ready to use!
return the_wrapper_around_the_original_function
# Now imagine you create a function you don't want to ever touch again.
def a_stand_alone_function():
print("I am a stand alone function, don't you dare modify me")
a_stand_alone_function()
#outputs: I am a stand alone function, don't you dare modify me
# Well, you can decorate it to extend its behavior.
# Just pass it to the decorator, it will wrap it dynamically in
# any code you want and return you a new function ready to be used:
a_stand_alone_function_decorated = my_shiny_new_decorator(a_stand_alone_function)
a_stand_alone_function_decorated()
#outputs:
#Before the function runs
#I am a stand alone function, don't you dare modify me
#After the function runs
은 아마 여러분이 call 마르다를 을 이다.a_stand_alone_function
a_stand_alone_function_decorated
대신 불려진다.그건 쉬워, 덮어쓰기만 하면 돼.a_stand_alone_function
에 의해 기능이 반환되어.my_shiny_new_decorator
:
a_stand_alone_function = my_shiny_new_decorator(a_stand_alone_function)
a_stand_alone_function()
#outputs:
#Before the function runs
#I am a stand alone function, don't you dare modify me
#After the function runs
# That’s EXACTLY what decorators do!
장식가들의 평판이 나빠졌다.
앞의 예: 장식자 구문 사용:
@my_shiny_new_decorator
def another_stand_alone_function():
print("Leave me alone")
another_stand_alone_function()
#outputs:
#Before the function runs
#Leave me alone
#After the function runs
그래, 그게 다야, 그렇게 간단해. @decorator
다음을 실현하는 지름길:
another_stand_alone_function = my_shiny_new_decorator(another_stand_alone_function)
장식가들은 단지 장식가 디자인 패턴의 피톤적인 변형일 뿐이다.Python에는 개발을 용이하게 하기 위해 몇 가지 고전적인 디자인 패턴이 내장되어 있다(이더레이터처럼).
물론, 당신은 장식가들을 축적할 수 있다.
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()
#outputs: --ham--
sandwich = bread(ingredients(sandwich))
sandwich()
#outputs:
#</''''''\>
# #tomatoes#
# --ham--
# ~salad~
#<\______/>
Python decorator 구문 사용:
@bread
@ingredients
def sandwich(food="--ham--"):
print(food)
sandwich()
#outputs:
#</''''''\>
# #tomatoes#
# --ham--
# ~salad~
#<\______/>
실내 장식가들의 순서는 다음과 같다.
@ingredients
@bread
def strange_sandwich(food="--ham--"):
print(food)
strange_sandwich()
#outputs:
##tomatoes#
#</''''''\>
# --ham--
#<\______/>
# ~salad~
자, 질문에 대답하자면...
결론적으로 다음과 같은 질문에 어떻게 대답해야 하는지를 쉽게 알 수 있다.
# The decorator to make it bold
def makebold(fn):
# The new function the decorator returns
def wrapper():
# Insertion of some code before and after
return "<b>" + fn() + "</b>"
return wrapper
# The decorator to make it italic
def makeitalic(fn):
# The new function the decorator returns
def wrapper():
# Insertion of some code before and after
return "<i>" + fn() + "</i>"
return wrapper
@makebold
@makeitalic
def say():
return "hello"
print(say())
#outputs: <b><i>hello</i></b>
# This is the exact equivalent to
def say():
return "hello"
say = makebold(makeitalic(say))
print(say())
#outputs: <b><i>hello</i></b>
여러분은 이제 그냥 행복하게 내버려두거나, 뇌를 조금 더 태워버리고, 장식가들의 진보된 사용을 볼 수 있다.
실내 장식가들을 한 단계 끌어올리는 것
인수를 장식된 함수에 전달
# It’s not black magic, you just have to let the wrapper
# pass the argument:
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
# Since when you are calling the function returned by the decorator, you are
# calling the wrapper, passing arguments to the wrapper will let it pass them to
# the decorated function
@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")
# outputs:
#I got args! Look: Peter Venkman
#My name is Peter Venkman
장식 방법
파이썬의 한가지 중요한 점은 방법과 기능이 정말 같다는 것이다.유일한 차이점은 방법들이 그들의 첫 번째 인수가 현재 객체에 대한 참조라고 예상한다는 것이다.self
).
그것은 여러분이 같은 방법으로 방법들을 위한 장식가를 만들 수 있다는 것을 의미해!그냥 잊지 말고 가져가.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)
#outputs: I am 26, what did you think?
만약 당신이 범용 장식가를 만든다면--그것은 어떤 기능이나 방법에도 적용될 것이고, 그것의 논쟁에 상관없이--그냥 사용해라.*args, **kwargs
:
def a_decorator_passing_arbitrary_arguments(function_to_decorate):
# The wrapper accepts any arguments
def a_wrapper_accepting_arbitrary_arguments(*args, **kwargs):
print("Do I have args?:")
print(args)
print(kwargs)
# Then you unpack the arguments, here *args, **kwargs
# If you are not familiar with unpacking, check:
# http://www.saltycrane.com/blog/2008/01/how-to-use-args-and-kwargs-in-python/
function_to_decorate(*args, **kwargs)
return a_wrapper_accepting_arbitrary_arguments
@a_decorator_passing_arbitrary_arguments
def function_with_no_argument():
print("Python is cool, no argument here.")
function_with_no_argument()
#outputs
#Do I have args?:
#()
#{}
#Python is cool, no argument here.
@a_decorator_passing_arbitrary_arguments
def function_with_arguments(a, b, c):
print(a, b, c)
function_with_arguments(1,2,3)
#outputs
#Do I have args?:
#(1, 2, 3)
#{}
#1 2 3
@a_decorator_passing_arbitrary_arguments
def function_with_named_arguments(a, b, c, platypus="Why not ?"):
print("Do {0}, {1} and {2} like platypus? {3}".format(a, b, c, platypus))
function_with_named_arguments("Bill", "Linus", "Steve", platypus="Indeed!")
#outputs
#Do I have args ? :
#('Bill', 'Linus', 'Steve')
#{'platypus': 'Indeed!'}
#Do Bill, Linus and Steve like platypus? Indeed!
class Mary(object):
def __init__(self):
self.age = 31
@a_decorator_passing_arbitrary_arguments
def sayYourAge(self, lie=-3): # You can now add a default value
print("I am {0}, what did you think?".format(self.age + lie))
m = Mary()
m.sayYourAge()
#outputs
# Do I have args?:
#(<__main__.Mary object at 0xb7d303ac>,)
#{}
#I am 28, what did you think?
실내 장식가에게 논쟁 전달
좋아, 이제 논쟁 자체를 장식가에게 전달하는 것에 대해 어떻게 생각하겠어?
장식가는 기능을 논쟁으로 받아들여야 하기 때문에 이것은 다소 뒤틀릴 수 있다.따라서 장식된 기능의 주장을 장식자에게 직접 전달할 수는 없다.
서둘러 해결책으로 가기 전에 다음과 같은 간단한 리마인더를 작성해 봅시다.
# Decorators are ORDINARY functions
def my_decorator(func):
print("I am an ordinary function")
def wrapper():
print("I am function returned by the decorator")
func()
return wrapper
# Therefore, you can call it without any "@"
def lazy_function():
print("zzzzzzzz")
decorated_function = my_decorator(lazy_function)
#outputs: I am an ordinary function
# It outputs "I am an ordinary function", because that’s just what you do:
# calling a function. Nothing magic.
@my_decorator
def lazy_function():
print("zzzzzzzz")
#outputs: I am an ordinary function
정확히 똑같다."my_decorator
"라고 한다.그래서 당신이@my_decorator
, 당신은 Python에게 그 함수를 '변수에 의해 라벨이 붙여진'이라고 부르라고 말하고 있다.my_decorator
"'.
이건 중요해!당신이 준 라벨은 장식가에게 직접 가리킬 수도 있고 그렇지 않을 수도 있다.
악을 쓰자.☺
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
# Let’s create a decorator. It’s just a new function after all.
new_decorator = decorator_maker()
#outputs:
#I make decorators! I am executed only once: when you make me create a decorator.
#As a decorator maker, I return a decorator
# Then we decorate the function
def decorated_function():
print("I am the decorated function.")
decorated_function = new_decorator(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.
여기선 놀랄 일이 아니다.
정확히 같은 방법으로 하되 성가신 중간 변수는 모두 생략합시다.
def decorated_function():
print("I am the decorated function.")
decorated_function = decorator_maker()(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.
# Finally:
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()
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.
야, 저거 어어?우리는 "과 함께 함수 호출을 이용했다.@
" 구문! :-)
자, 다시 논쟁과 함께 장식가들에게 돌아가 봅시다.만약 우리가 기능들을 사용하여 즉석에서 장식가를 만들 수 있다면, 우리는 그 기능에 논쟁을 전달할 수 있을 것이다, 그렇지 않은가?
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):
# The ability to pass arguments here is a gift from closures.
# If you are not comfortable with closures, you can assume it’s ok,
# or read: https://stackoverflow.com/questions/13857/can-you-explain-closures-as-they-relate-to-python
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))
decorated_function_with_arguments("Rajesh", "Howard")
#outputs:
#I make decorators! And I accept arguments: Leonard Sheldon
#I am the decorator. Somehow you passed me arguments: Leonard Sheldon
#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
여기 있다: 논쟁이 있는 장식가.변수를 변수로 설정할 수 있음:
c1 = "Penny"
c2 = "Leslie"
@decorator_maker_with_arguments("Leonard", c1)
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))
decorated_function_with_arguments(c2, "Howard")
#outputs:
#I make decorators! And I accept arguments: Leonard Penny
#I am the decorator. Somehow you passed me arguments: Leonard Penny
#I am the wrapper around the decorated function.
#I can access all the variables
# - from the decorator: Leonard Penny
# - from the function call: Leslie Howard
#Then I can pass them to the decorated function
#I am the decorated function and only know about my arguments: Leslie Howard
보다시피, 당신은 이 속임수를 사용하여 어떤 기능처럼 장식가에게 주장을 전달할 수 있다.심지어 사용할 수도 있다.*args, **kwargs
네가 원한다면.하지만 장식가들은 단 한번만 불려진다는 것을 기억하라.파이썬이 대본을 가져올 때.나중에 동적으로 인수를 설정할 수 없다."x 가져오기"를 하면 기능이 이미 꾸며져 있어서 아무것도 바꿀 수 없다.
연습하자: 장식가를 꾸미는 것
좋아, 보너스로 장식가라면 어떤 논쟁이든 일반적으로 받아들이도록 해줄게.결국, 논쟁을 받아들이기 위해서, 우리는 다른 기능을 사용하여 우리의 장식가를 만들었다.
우리는 장식가를 감쌌다.
최근에 본 그 포장 기능 말고 다른 건 없어?
예, 장식가들!
즐거운 시간을 보내고 장식가들을 위한 장식가를 쓰자.
def decorator_with_args(decorator_to_enhance):
"""
This function is supposed to be used as a decorator.
It must decorate an other function, that is intended to be used as a decorator.
Take a cup of coffee.
It will allow any decorator to accept an arbitrary number of arguments,
saving you the headache to remember how to do that every time.
"""
# We use the same trick we did to pass arguments
def decorator_maker(*args, **kwargs):
# We create on the fly a decorator that accepts only a function
# but keeps the passed arguments from the maker.
def decorator_wrapper(func):
# We return the result of the original decorator, which, after all,
# IS JUST AN ORDINARY FUNCTION (which returns a function).
# Only pitfall: the decorator must have this specific signature or it won't work:
return decorator_to_enhance(func, *args, **kwargs)
return decorator_wrapper
return decorator_maker
다음과 같이 사용할 수 있다.
# You create the function you will use as a decorator. And stick a decorator on it :-)
# Don't forget, the signature is "decorator(func, *args, **kwargs)"
@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
# Then you decorate the functions you wish with your brand new decorated decorator.
@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
# Whoooot!
알아, 네가 마지막으로 이런 감정을 느낀 것은, 한 남자가 "재귀를 이해하기 전에, 재귀를 먼저 이해해야 한다"고 말하는 것을 듣고 난 후였어.하지만 이제, 이것을 마스터하는 것에 대해 기분이 좋지 않니?
모범 사례: 장식가
- Python 2.4에 실내 장식가가 도입되었으므로 >= 2.4에서 코드를 실행하도록 하십시오.
- 장식가들은 기능 호출을 느리게 한다.명심하세요.
- 함수를 결정 해제할 수는 없다.(제거할 수 있는 장식가를 만들기 위한 해킹이 있지만 아무도 사용하지 않는다.)그래서 한번 기능이 꾸며지면 모든 코드에 맞게 꾸며진다.
- 장식가들은 디버깅을 어렵게 만들 수 있는 기능을 포장한다.(이것은 Python >= 2.5; 아래 참조)
그functools
모듈은 파이썬 2.5에서 도입되었다.그것은 기능을 포함한다.functools.wraps()
, 장식된 함수의 이름, 모듈 및 문서 문자열을 포장지에 복사한다.
(재미있는 사실:functools.wraps()
실내 장식가!☺)
# For debugging, the stacktrace prints you the function __name__
def foo():
print("foo")
print(foo.__name__)
#outputs: foo
# With a decorator, it gets messy
def bar(func):
def wrapper():
print("bar")
return func()
return wrapper
@bar
def foo():
print("foo")
print(foo.__name__)
#outputs: wrapper
# "functools" can help for that
import functools
def bar(func):
# We say that "wrapper", is wrapping "func"
# and the magic begins
@functools.wraps(func)
def wrapper():
print("bar")
return func()
return wrapper
@bar
def foo():
print("foo")
print(foo.__name__)
#outputs: foo
장식가들이 어떻게 유용할 수 있을까?
이제 중요한 질문은:내가 뭘 위해 장식가를 쓸 수 있을까?
멋지고 강력해 보이지만, 실제적인 예가 좋을 것이다.글쎄, 1000개의 가능성이 있어.일반적으로 기능 동작을 외부 lib에서 확장(수정할 수 없음)하거나 디버깅(일시적이므로 수정하지 않음)하는 것이 사용된다.
이러한 기능을 사용하여 다음과 같이 DRY 방식으로 여러 기능을 확장할 수 있다.
def benchmark(func):
"""
A decorator that prints the time a function takes
to execute.
"""
import time
def wrapper(*args, **kwargs):
t = time.clock()
res = func(*args, **kwargs)
print("{0} {1}".format(func.__name__, time.clock()-t))
return res
return wrapper
def logging(func):
"""
A decorator that logs the activity of the script.
(it actually just prints it, but it could be logging!)
"""
def wrapper(*args, **kwargs):
res = func(*args, **kwargs)
print("{0} {1} {2}".format(func.__name__, args, kwargs))
return res
return wrapper
def counter(func):
"""
A decorator that counts and prints the number of times a function has been executed
"""
def wrapper(*args, **kwargs):
wrapper.count = wrapper.count + 1
res = func(*args, **kwargs)
print("{0} has been used: {1}x".format(func.__name__, wrapper.count))
return res
wrapper.count = 0
return wrapper
@counter
@benchmark
@logging
def reverse_string(string):
return str(reversed(string))
print(reverse_string("Able was I ere I saw Elba"))
print(reverse_string("A man, a plan, a canoe, pasta, heros, rajahs, a coloratura, maps, snipe, percale, macaroni, a gag, a banana bag, a tan, a tag, a banana bag again (or a camel), a crepe, pins, Spam, a rut, a Rolo, cash, a jar, sore hats, a peon, a canal: Panama!"))
#outputs:
#reverse_string ('Able was I ere I saw Elba',) {}
#wrapper 0.0
#wrapper has been used: 1x
#ablE was I ere I saw elbA
#reverse_string ('A man, a plan, a canoe, pasta, heros, rajahs, a coloratura, maps, snipe, percale, macaroni, a gag, a banana bag, a tan, a tag, a banana bag again (or a camel), a crepe, pins, Spam, a rut, a Rolo, cash, a jar, sore hats, a peon, a canal: Panama!',) {}
#wrapper 0.0
#wrapper has been used: 2x
#!amanaP :lanac a ,noep a ,stah eros ,raj a ,hsac ,oloR a ,tur a ,mapS ,snip ,eperc a ,)lemac a ro( niaga gab ananab a ,gat a ,nat a ,gab ananab a ,gag a ,inoracam ,elacrep ,epins ,spam ,arutaroloc a ,shajar ,soreh ,atsap ,eonac a ,nalp a ,nam A
물론 장식가의 좋은 점은 거의 모든 것에 다시 쓰지 않고도 바로 사용할 수 있다는 것이다.드라이, 나는 이렇게 말했다.
@counter
@benchmark
@logging
def get_random_futurama_quote():
from urllib import urlopen
result = urlopen("http://subfusion.net/cgi-bin/quote.pl?quote=futurama").read()
try:
value = result.split("<br><b><hr><br>")[1].split("<br><br><hr>")[0]
return value.strip()
except:
return "No, I'm ... doesn't!"
print(get_random_futurama_quote())
print(get_random_futurama_quote())
#outputs:
#get_random_futurama_quote () {}
#wrapper 0.02
#wrapper has been used: 1x
#The laws of science be a harsh mistress.
#get_random_futurama_quote () {}
#wrapper 0.01
#wrapper has been used: 2x
#Curse you, merciful Poseidon!
파이톤은 그 자체로 몇 가지 장식가를 제공한다.property
staticmethod
등
- 장고는 장식가를 사용하여 캐싱을 관리하고 권한을 본다.
- 가짜 인라이닝 비동기 함수 호출로 꼬임.
여긴 정말 큰 놀이터야.
장식가의 작동 방식을 보려면 설명서를 참조하십시오.요청하신 내용은 다음과 같다.
from functools import wraps
def makebold(fn):
@wraps(fn)
def wrapper(*args, **kwargs):
return "<b>" + fn(*args, **kwargs) + "</b>"
return wrapper
def makeitalic(fn):
@wraps(fn)
def wrapper(*args, **kwargs):
return "<i>" + fn(*args, **kwargs) + "</i>"
return wrapper
@makebold
@makeitalic
def hello():
return "hello world"
@makebold
@makeitalic
def log(s):
return s
print hello() # returns "<b><i>hello world</i></b>"
print hello.__name__ # with functools.wraps() this returns "hello"
print log('hello') # returns "<b><i>hello</i></b>"
또는 장식된 기능의 반환 값을 공장 기능에 전달된 태그로 감싼 장식가를 반환하는 공장 기능을 작성할 수 있다.예를 들면 다음과 같다.
from functools import wraps
def wrap_in_tag(tag):
def factory(func):
@wraps(func)
def decorator():
return '<%(tag)s>%(rv)s</%(tag)s>' % (
{'tag': tag, 'rv': func()})
return decorator
return factory
이렇게 하면 다음과 같은 내용을 쓸 수 있다.
@wrap_in_tag('b')
@wrap_in_tag('i')
def say():
return 'hello'
또는
makebold = wrap_in_tag('b')
makeitalic = wrap_in_tag('i')
@makebold
@makeitalic
def say():
return 'hello'
개인적으로 나는 장식가를 다소 다르게 썼을 것이다.
from functools import wraps
def wrap_in_tag(tag):
def factory(func):
@wraps(func)
def decorator(val):
return func('<%(tag)s>%(val)s</%(tag)s>' %
{'tag': tag, 'val': val})
return decorator
return factory
다음과 같은 결과를 얻을 수 있다.
@wrap_in_tag('b')
@wrap_in_tag('i')
def say(val):
return val
say('hello')
장식자 구문이 속기라는 구조를 잊지 마십시오.
say = wrap_in_tag('b')(wrap_in_tag('i')(say)))
다른 사람들이 이미 너에게 그 문제를 해결하는 방법을 알려준 것 같아.나는 이것이 네가 장식가들이 무엇인지 이해하는 데 도움이 되기를 바란다.
장식가들은 그저 통속적인 설탕일 뿐이다.
이것
@decorator
def func():
...
로 확대하다.
def func():
...
func = decorator(func)
그리고 물론 당신은 장식가 기능에서 람다도 반납할 수 있다.
def makebold(f):
return lambda: "<b>" + f() + "</b>"
def makeitalic(f):
return lambda: "<i>" + f() + "</i>"
@makebold
@makeitalic
def say():
return "Hello"
print say()
Python 장식자는 다른 기능에 추가 기능 추가
이탤릭체 장식가는 마치
def makeitalic(fn):
def newFunc():
return "<i>" + fn() + "</i>"
return newFunc
함수는 함수 내부에서 정의된다는 점에 유의하십시오.그것이 기본적으로 하는 일은 함수를 새로 정의한 것으로 대체하는 것이다.예를 들어, 나는 이 수업이 있다.
class foo:
def bar(self):
print "hi"
def foobar(self):
print "hi again"
자, 이제 두 기능 모두 "---"를 인쇄한 후에, 그리고 완성하기 전에 인쇄해 주었으면 한다.나는 각 인쇄 문장의 앞뒤에 인쇄물 "---"를 추가할 수 있다.하지만 나는 반복하는 것을 좋아하지 않기 때문에, 나는 장식가를 만들 것이다.
def addDashes(fn): # notice it takes a function as an argument
def newFunction(self): # define a new function
print "---"
fn(self) # call the original function
print "---"
return newFunction
# Return the newly defined function - it will "replace" the original
그래서 이제 나는 내 수업을 다음으로 바꿀 수 있다.
class foo:
@addDashes
def bar(self):
print "hi"
@addDashes
def foobar(self):
print "hi again"
실내 장식가에 대한 자세한 내용은 http://www.ibm.com/developerworks/linux/library/l-cpdecor.html을 참조하십시오.
당신은 당신이 원하는 것을 할 수 있는 두 개의 분리된 장식가를 바로 아래 그림과 같이 만들 수 있다.의 사용에 유의하십시오.*args, **kwargs
…의 wrapped()
여러 하는 기능( 예로는 꼭 say()
기능(일반성을 위해 포함됨).
《한국》(韓國)은functools.wraps
데코레이터는 포장된 기능의 메타 속성을 장식된 기능의 메타 속성으로 바꾸는 데 사용된다.이렇게 하면 오류 메시지와 내장 기능 설명서가 생성된다.func.__doc__
)는 대신 장식된 기능의 그것이다.wrapped()
의
from functools import wraps
def makebold(fn):
@wraps(fn)
def wrapped(*args, **kwargs):
return "<b>" + fn(*args, **kwargs) + "</b>"
return wrapped
def makeitalic(fn):
@wraps(fn)
def wrapped(*args, **kwargs):
return "<i>" + fn(*args, **kwargs) + "</i>"
return wrapped
@makebold
@makeitalic
def say():
return 'Hello'
print(say()) # -> <b><i>Hello</i></b>
정제
보시다시피 이 두 가지 장식가에는 많은 중복된 코드가 있다.이러한 유사성을 감안한다면, 당신이 대신 실제로 장식가 공장인 일반 공장(즉, 다른 장식가를 만드는 장식가 기능)을 만드는 것이 좋을 것이다.그렇게 하면 코드 반복이 줄어들 것이고, DRAY 원칙을 따를 수 있을 것이다.
def html_deco(tag):
def decorator(fn):
@wraps(fn)
def wrapped(*args, **kwargs):
return '<%s>' % tag + fn(*args, **kwargs) + '</%s>' % tag
return wrapped
return decorator
@html_deco('b')
@html_deco('i')
def greet(whom=''):
return 'Hello' + (' ' + whom) if whom else ''
print(greet('world')) # -> <b><i>Hello world</i></b>
코드를 더 쉽게 읽을 수 있도록 공장에서 생성한 장식가에게 더 자세한 이름을 지정하십시오.
makebold = html_deco('b')
makeitalic = html_deco('i')
@makebold
@makeitalic
def greet(whom=''):
return 'Hello' + (' ' + whom) if whom else ''
print(greet('world')) # -> <b><i>Hello world</i></b>
또는 다음과 같이 결합할 수도 있다.
makebolditalic = lambda fn: makebold(makeitalic(fn))
@makebolditalic
def greet(whom=''):
return 'Hello' + (' ' + whom) if whom else ''
print(greet('world')) # -> <b><i>Hello world</i></b>
효율성
위의 예제가 모든 작업을 수행하지만, 생성된 코드는 여러 장식가를 한 번에 적용할 때 관련 없는 함수 호출의 형태로 상당한 오버헤드를 수반한다.정확한 사용법(예: I/O-bound일 수 있음)에 따라 이 문제는 문제가 되지 않을 수 있다.
장식 기능의 속도가 중요한 경우, 모든 태그를 한 번에 추가하는 약간 다른 장식기 공장 기능을 작성하여 오버헤드를 하나의 추가 기능 호출로 유지할 수 있으므로, 각 태그에 별도의 장식기를 사용하여 발생하는 부가 기능 호출을 피하는 코드를 생성할 수 있다.
이것은 장식가 자체에서 더 많은 코드를 필요로 하지만, 이것은 기능 정의에 적용될 때만 실행되며, 나중에 그것들 자체가 불려질 때는 실행되지 않는다.이것은 또한 다음을 사용하여 더 읽기 쉬운 이름을 만들 때도 적용된다.lambda
앞에서 설명한 기능샘플:
def multi_html_deco(*tags):
start_tags, end_tags = [], []
for tag in tags:
start_tags.append('<%s>' % tag)
end_tags.append('</%s>' % tag)
start_tags = ''.join(start_tags)
end_tags = ''.join(reversed(end_tags))
def decorator(fn):
@wraps(fn)
def wrapped(*args, **kwargs):
return start_tags + fn(*args, **kwargs) + end_tags
return wrapped
return decorator
makebolditalic = multi_html_deco('b', 'i')
@makebolditalic
def greet(whom=''):
return 'Hello' + (' ' + whom) if whom else ''
print(greet('world')) # -> <b><i>Hello world</i></b>
같은 일을 하는 또 다른 방법:
class bol(object):
def __init__(self, f):
self.f = f
def __call__(self):
return "<b>{}</b>".format(self.f())
class ita(object):
def __init__(self, f):
self.f = f
def __call__(self):
return "<i>{}</i>".format(self.f())
@bol
@ita
def sayhi():
return 'hi'
또는 보다 유연하게:
class sty(object):
def __init__(self, tag):
self.tag = tag
def __call__(self, f):
def newf():
return "<{tag}>{res}</{tag}>".format(res=f(), tag=self.tag)
return newf
@sty('b')
@sty('i')
def sayhi():
return 'hi'
Python에서 다음과 같은 일을 할 두 명의 장식가를 어떻게 만들 수 있을까?
호출될 때 다음 기능을 원하는 경우:
@makebold @makeitalic def say(): return "Hello"
돌아가려면:
<b><i>Hello</i></b>
심플 솔루션
이를 위해 가장 간단하게 기능(폐쇄) 위에 닫힌 람다(익명 함수)를 반환하는 장식가를 만들어 다음과 같이 부른다.
def makeitalic(fn):
return lambda: '<i>' + fn() + '</i>'
def makebold(fn):
return lambda: '<b>' + fn() + '</b>'
이제 원하는 대로 사용하십시오.
@makebold
@makeitalic
def say():
return 'Hello'
그리고 지금:
>>> say()
'<b><i>Hello</i></b>'
단순 솔루션 문제
그러나 우리는 본래의 기능을 거의 상실한 것 같다.
>>> say
<function <lambda> at 0x4ACFA070>
그것을 찾으려면, 각 람다의 폐쇄를 파헤쳐야 하는데, 그 중 하나는 다른 하나에 묻혀 있다.
>>> say.__closure__[0].cell_contents
<function <lambda> at 0x4ACFA030>
>>> say.__closure__[0].cell_contents.__closure__[0].cell_contents
<function say at 0x4ACFA730>
그래서 만약 우리가 이 기능에 대한 문서를 넣거나, 혹은 둘 이상의 논쟁을 필요로 하는 기능을 장식할 수 있기를 원하거나, 디버깅 세션에서 우리가 보고 있는 기능이 무엇인지 알고 싶다면, 우리는 우리의 포장지를 가지고 조금 더 많은 것을 할 필요가 있다.
완벽한 솔루션 - 이러한 대부분의 문제를 해결
우리는 실내 장식가가 있다.wraps
처음부터functools
표준 라이브러리의 모듈!
from functools import wraps
def makeitalic(fn):
# must assign/update attributes from wrapped function to wrapper
# __module__, __name__, __doc__, and __dict__ by default
@wraps(fn) # explicitly give function whose attributes it is applying
def wrapped(*args, **kwargs):
return '<i>' + fn(*args, **kwargs) + '</i>'
return wrapped
def makebold(fn):
@wraps(fn)
def wrapped(*args, **kwargs):
return '<b>' + fn(*args, **kwargs) + '</b>'
return wrapped
아직 보일러가 남아 있는 것은 유감스럽지만, 이것은 우리가 만들 수 있는 가장 간단한 것이다.
파이톤 3에서 당신은 또한__qualname__
그리고__annotations__
디폴트로 배정된
자, 이제:
@makebold
@makeitalic
def say():
"""This function returns a bolded, italicized 'hello'"""
return 'Hello'
그리고 이제:
>>> say
<function say at 0x14BB8F70>
>>> help(say)
Help on function say in module __main__:
say(*args, **kwargs)
This function returns a bolded, italicized 'hello'
결론
그래서 우리는 그것을 본다.wraps
포장기능을 거의 모든 것을 하게 한다. 단, 함수가 논쟁으로 무엇을 하는지 우리에게 정확히 말해주는 것을 제외한다.
이 문제를 해결하려고 시도할 수 있는 다른 모듈들이 있지만, 해결책은 아직 표준 라이브러리에 있지 않다.
장식가는 기능 정의를 취하여 이 기능을 실행하고 결과를 변형시키는 새로운 기능을 만든다.
@deco
def do():
...
다음 항목과 동일하다:
do = deco(do)
예:
def deco(func):
def inner(letter):
return func(letter).upper() #upper
return inner
이것
@deco
def do(number):
return chr(number) # number to letter
이것과 같다
def do2(number):
return chr(number)
do2 = deco(do2)
65 <=> 'a'
print(do(65))
print(do2(65))
>>> B
>>> B
장식가를 이해하기 위해서는, 그 장식가가 기능을 실행하고 결과를 변형시키는 내적인 기능을 하는 새로운 기능을 창조했다는 것을 알아두는 것이 중요하다.
이 답은 오래전부터 답해 왔지만, 새로운 장식가 작문을 쉽고 압축적으로 만드는 데코레이터 수업을 공유해야겠다고 생각했다.
from abc import ABCMeta, abstractclassmethod
class Decorator(metaclass=ABCMeta):
""" Acts as a base class for all decorators """
def __init__(self):
self.method = None
def __call__(self, method):
self.method = method
return self.call
@abstractclassmethod
def call(self, *args, **kwargs):
return self.method(*args, **kwargs)
우선, 나는 이것이 장식가들의 행동을 매우 명확하게 한다고 생각하지만, 또한 새로운 장식가를 매우 간결하게 정의하는 것을 쉽게 만든다.위에 나열된 예제의 경우 다음과 같이 해결할 수 있다.
class MakeBold(Decorator):
def call():
return "<b>" + self.method() + "</b>"
class MakeItalic(Decorator):
def call():
return "<i>" + self.method() + "</i>"
@MakeBold()
@MakeItalic()
def say():
return "Hello"
또한 이 도구를 사용하여 보다 복잡한 작업을 수행할 수도 있다. 예를 들어, 기능을 자동으로 반복적으로 반복적으로 반복적으로 반복적으로 반복해서 반복적으로 적용하도록 하는 장식가.
class ApplyRecursive(Decorator):
def __init__(self, *types):
super().__init__()
if not len(types):
types = (dict, list, tuple, set)
self._types = types
def call(self, arg):
if dict in self._types and isinstance(arg, dict):
return {key: self.call(value) for key, value in arg.items()}
if set in self._types and isinstance(arg, set):
return set(self.call(value) for value in arg)
if tuple in self._types and isinstance(arg, tuple):
return tuple(self.call(value) for value in arg)
if list in self._types and isinstance(arg, list):
return list(self.call(value) for value in arg)
return self.method(arg)
@ApplyRecursive(tuple, set, dict)
def double(arg):
return 2*arg
print(double(1))
print(double({'a': 1, 'b': 2}))
print(double({1, 2, 3}))
print(double((1, 2, 3, 4)))
print(double([1, 2, 3, 4, 5]))
인쇄 대상:
2
{'a': 2, 'b': 4}
{2, 4, 6}
(2, 4, 6, 8)
[1, 2, 3, 4, 5, 1, 2, 3, 4, 5]
이 예에는 다음 항목이 포함되지 않았음을 확인하십시오.list
장식가의 즉석화를 타이핑하여 최종 인쇄문에 메소드가 리스트의 요소가 아닌 리스트 자체에 적용되도록 한다.
#decorator.py
def makeHtmlTag(tag, *args, **kwds):
def real_decorator(fn):
css_class = " class='{0}'".format(kwds["css_class"]) \
if "css_class" in kwds else ""
def wrapped(*args, **kwds):
return "<"+tag+css_class+">" + fn(*args, **kwds) + "</"+tag+">"
return wrapped
# return decorator dont call it
return real_decorator
@makeHtmlTag(tag="b", css_class="bold_css")
@makeHtmlTag(tag="i", css_class="italic_css")
def hello():
return "hello world"
print hello()
당신은 또한 수업시간에 장식가를 쓸 수 있다.
#class.py
class makeHtmlTagClass(object):
def __init__(self, tag, css_class=""):
self._tag = tag
self._css_class = " class='{0}'".format(css_class) \
if css_class != "" else ""
def __call__(self, fn):
def wrapped(*args, **kwargs):
return "<" + self._tag + self._css_class+">" \
+ fn(*args, **kwargs) + "</" + self._tag + ">"
return wrapped
@makeHtmlTagClass(tag="b", css_class="bold_css")
@makeHtmlTagClass(tag="i", css_class="italic_css")
def hello(name):
return "Hello, {}".format(name)
print hello("Your name")
여기 장식가들을 체인으로 하는 간단한 예가 있다.마지막 줄에 유의하십시오. 커버 아래에 무슨 일이 일어나고 있는지 표시하십시오.
############################################################
#
# decorators
#
############################################################
def bold(fn):
def decorate():
# surround with bold tags before calling original function
return "<b>" + fn() + "</b>"
return decorate
def uk(fn):
def decorate():
# swap month and day
fields = fn().split('/')
date = fields[1] + "/" + fields[0] + "/" + fields[2]
return date
return decorate
import datetime
def getDate():
now = datetime.datetime.now()
return "%d/%d/%d" % (now.day, now.month, now.year)
@bold
def getBoldDate():
return getDate()
@uk
def getUkDate():
return getDate()
@bold
@uk
def getBoldUkDate():
return getDate()
print getDate()
print getBoldDate()
print getUkDate()
print getBoldUkDate()
# what is happening under the covers
print bold(uk(getDate))()
출력은 다음과 같다.
17/6/2013
<b>17/6/2013</b>
6/17/2013
<b>6/17/2013</b>
<b>6/17/2013</b>
카운터 예에 대해 말하자면 - 위에서 설명한 대로, 카운터는 실내 장식기를 사용하는 모든 기능 간에 공유된다.
def counter(func):
def wrapped(*args, **kws):
print 'Called #%i' % wrapped.count
wrapped.count += 1
return func(*args, **kws)
wrapped.count = 0
return wrapped
이렇게 하면, 당신의 장식자는 다른 기능에 재사용될 수 있다(또는 동일한 기능을 여러 번 장식하는데 사용된다).func_counter1 = counter(func); func_counter2 = counter(func)
() 그러면 카운터 변수는 각각에 대해 비공개로 유지된다.
다른 수의 인수로 함수 장식:
def frame_tests(fn):
def wrapper(*args):
print "\nStart: %s" %(fn.__name__)
fn(*args)
print "End: %s\n" %(fn.__name__)
return wrapper
@frame_tests
def test_fn1():
print "This is only a test!"
@frame_tests
def test_fn2(s1):
print "This is only a test! %s" %(s1)
@frame_tests
def test_fn3(s1, s2):
print "This is only a test! %s %s" %(s1, s2)
if __name__ == "__main__":
test_fn1()
test_fn2('OK!')
test_fn3('OK!', 'Just a test!')
결과:
Start: test_fn1
This is only a test!
End: test_fn1
Start: test_fn2
This is only a test! OK!
End: test_fn2
Start: test_fn3
This is only a test! OK! Just a test!
End: test_fn3
파올로 베르간티노의 대답은 stdlib만 사용할 수 있다는 큰 장점을 가지고 있으며, 장식자 논쟁이나 장식된 함수 논쟁은 없는 이 간단한 예에 효과가 있다.
그러나 더 일반적인 경우를 다루려면 다음과 같은 3가지 주요 제한이 있다.
- 이미 여러 답변에서 언급한 바와 같이 선택적 장식자 인수를 추가하기 위해 코드를 쉽게 수정할 수 없다.예를 들어, a 생성
makestyle(style='bold')
실내 장식가는 비예절적이다. - 다가, 으로 만든 포장지는
@functools.wraps
서명을 보존하지 않기 때문에 잘못된 인수가 제공되면 실행이 시작되고 일반적인 오류와는 다른 유형의 오류가 발생할 수 있음TypeError
. - 마지막으로, 로 만들어진 포장지가 꽤 어렵다.
@functools.wraps
인수의 이름에 따라 인수에 액세스하십시오.과연 그 주장은 에 나타날 수 있다.*args
에**kwargs
또는 전혀 나타나지 않을 수 있다(선택 사항인 경우).
첫 번째 문제를 해결하기 위해 글을 썼고, 나머지 두 가지를 해결하기 위해 글을 썼다.참고:makefun
유명한 리브와 같은 수법을 사용했어
이렇게 하면 진정으로 서명을 보존하는 포장지를 반환하고 논쟁과 함께 장식가를 만들 수 있다.
from decopatch import function_decorator, DECORATED
from makefun import wraps
@function_decorator
def makestyle(st='b', fn=DECORATED):
open_tag = "<%s>" % st
close_tag = "</%s>" % st
@wraps(fn)
def wrapped(*args, **kwargs):
return open_tag + fn(*args, **kwargs) + close_tag
return wrapped
decopatch
당신의 선호도에 따라 다양한 파이선 개념을 숨기거나 보여주는 두 가지 다른 개발 스타일을 당신에게 제공한다.가장 콤팩트한 스타일은 다음과 같다.
from decopatch import function_decorator, WRAPPED, F_ARGS, F_KWARGS
@function_decorator
def makestyle(st='b', fn=WRAPPED, f_args=F_ARGS, f_kwargs=F_KWARGS):
open_tag = "<%s>" % st
close_tag = "</%s>" % st
return open_tag + fn(*f_args, **f_kwargs) + close_tag
두 경우 모두 실내 장식가가 예상대로 작동하는지 확인할 수 있다.
@makestyle
@makestyle('i')
def hello(who):
return "hello %s" % who
assert hello('world') == '<b><i>hello world</i></b>'
자세한 내용은 설명서를 참조하십시오.
나는 당신이 장식가에서 사용자 정의 매개변수를 추가하고 최종 기능에 전달하여 작업해야 할 경우를 추가한다.
장식가들:
def jwt_or_redirect(fn):
@wraps(fn)
def decorator(*args, **kwargs):
...
return fn(*args, **kwargs)
return decorator
def jwt_refresh(fn):
@wraps(fn)
def decorator(*args, **kwargs):
...
new_kwargs = {'refreshed_jwt': 'xxxxx-xxxxxx'}
new_kwargs.update(kwargs)
return fn(*args, **new_kwargs)
return decorator
그리고 최종 기능:
@app.route('/')
@jwt_or_redirect
@jwt_refresh
def home_page(*args, **kwargs):
return kwargs['refreched_jwt']
참조URL: https://stackoverflow.com/questions/739654/how-to-make-function-decorators-and-chain-them-together
'IT이야기' 카테고리의 다른 글
"vue-discript-theplate"를 typecript base로 변환하는 데 문제가 있으십니까? (0) | 2022.03.07 |
---|---|
Vuex-변이로 여러 변수를 전달합니다. (0) | 2022.03.07 |
Vuex에서 정의되지 않은 속성 'displatch'를 읽을 수 없음 (0) | 2022.03.07 |
계산 목록 차이 (0) | 2022.03.07 |
TS6133 무시: "(가져오기) 선언되었지만 절대 사용되지 않음"? (0) | 2022.03.07 |