Welcome to OGeek Q&A Community for programmer and developer-Open, Learning and Share
Welcome To Ask or Share your Answers For Others

Categories

0 votes
220 views
in Technique[技术] by (71.8m points)

python - 如何制作功能装饰器链?(How to make a chain of function decorators?)

How can I make two decorators in Python that would do the following?

(如何在Python中制作两个装饰器,以完成以下工作?)

@makebold
@makeitalic
def say():
   return "Hello"

...which should return:

(...应返回:)

"<b><i>Hello</i></b>"

I'm not trying to make HTML this way in a real application - just trying to understand how decorators and decorator chaining works.

(我并不是想在真实的应用程序中以这种方式制作HTML ,而是试图了解装饰器和装饰器链接的工作方式。)

  ask by Imran translate from so

与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
Welcome To Ask or Share your Answers For Others

1 Reply

0 votes
by (71.8m points)

If you are not into long explanations, see Paolo Bergantino's answer .

(如果您不做详细解释,请参阅Paolo Bergantino的答案 。)

Decorator Basics (装饰基础)

Python's functions are objects (Python的函数是对象)

To understand decorators, you must first understand that functions are objects in Python.

(要了解装饰器,您必须首先了解函数是Python中的对象。)

This has important consequences.

(这具有重要的后果。)

Let's see why with a simple example :

(让我们来看一个简单的例子:)

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!'

Keep this in mind.

(请记住这一点。)

We'll circle back to it shortly.

(我们将在不久后回头再说。)

Another interesting property of Python functions is they can be defined inside another function!

(Python函数的另一个有趣特性是可以在另一个函数中定义它们!)

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

Functions references (功能参考)

Okay, still here?

(好吧,还在吗?)

Now the fun part...

(现在有趣的部分...)

You've seen that functions are objects.

(您已经看到函数是对象。)

Therefore, functions:

(因此,功能:)

  • can be assigned to a variable

    (可以分配给变量)

  • can be defined in another function

    (可以在另一个函数中定义)

That means that a function can return another function .

(这意味着一个函数可以return另一个函数 。)

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...

There's more!

(还有更多!)

If you can return a function, you can pass one as a parameter:

(如果可以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!

Well, you just have everything needed to understand decorators.

(好吧,您只需具备了解装饰器所需的一切。)

You see, decorators are “wrappers”, which means that they let you execute code before and after the function they decorate without modifying the function itself.

(您会看到,装饰器是“包装器”,这意味着它们使您可以在装饰函数之前和之后执行代码,而无需修改函数本身。)

Handcrafted decorators (手工装饰)

How you'd do it manually:

(您将如何手动进行操作:)

# 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

Now, you probably want that every time you call a_stand_alone_function , a_stand_alone_function_decorated is called instead.

(现在,你可能想,每次你打电话时a_stand_alone_functiona_stand_alone_function_decorated被称为替代。)

That's easy, just overwrite a_stand_alone_function with the function returned by my_shiny_new_decorator :

(这很简单,只需用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!

Decorators demystified (装饰者神秘化)

The previous example, using the decorator syntax:

(上一个使用装饰器语法的示例:)

@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

Yes, that's all, it's that simple.

(是的,仅此而已。)

@decorator is just a shortcut to:

(@decorator只是以下操作的快捷方式:)

another_stand_alone_function = my_shiny_new_decorator(another_stand_alone_function)

Decorators are just a pythonic variant of the decorator design pattern .

(装饰器只是装饰器设计模式的pythonic变体。)

There are several classic design patterns embedded in Python to ease development (like iterators).

(Python中嵌入了几种经典的设计模式来简化开发(例如迭代器)。)

Of course, you can accumulate decorators:

(当然,您可以积累装饰器:)

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~
#<\______/>

Using the Python decorator syntax:

(使用Python装饰器语法:)

@bread
@ingredients
def sandwich(food="--ham--"):
    print(food)

sandwich()
#outputs:
#</''''''>
# #tomatoes#
# --ham--
# ~salad~
#<\______/>

The order you set the decorators MATTERS:

(您设置装饰器重要事项的顺序:)

@ingredients
@bread
def strange_sandwich(food="--ham--"):
    print(food)

strange_sandwich()
#outputs:
##tomatoes#
#</''''''>
# --ham--
#<\______/>
# ~salad~

Now: to answer the question... (现在:回答问题...)

As a conclusion, you can easily see how to answer the question:

(作为结论,您可以轻松地看到如何回答该问题:)

# 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>

You can now just leave happy, or burn your brain a little bit more and see advanced uses of decorators.

(现在,您可以放开心心,或者多动脑筋,看看装饰器的高级用法。)


Taking decorators to the next level (将装饰者提升到一个新的水平)

Passing arguments to the decorated function (将参数传递给装饰函数)

# 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_f

与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
OGeek|极客中国-欢迎来到极客的世界,一个免费开放的程序员编程交流平台!开放,进步,分享!让技术改变生活,让极客改变未来! Welcome to OGeek Q&A Community for programmer and developer-Open, Learning and Share
Click Here to Ask a Question

...