python语言:装饰器原理

发布于:2022-10-20 ⋅ 阅读:(351) ⋅ 点赞:(0)

目录

简述装饰器

一. 函数也是对象

二、在函数中定义函数

三、从函数内部返回函数:

四、将一个函数作为参数提供给另一个函数

五、写一个装饰器

5.1. 应用

5.2. 用于权限管理

5.3. 用于日志管理

六. 装饰器参数

6.1.函数装饰器

6.2. 类装饰器


简述装饰器

        装饰器是 Python 的重要组成部分。简而言之:它们是修改其他功能的功能。它们有助于使我们的代码更短、更 Pythonic。大多数初学者不知道在哪里使用它们,所以我将分享一些装饰器可以让你的代码更简洁的地方。这可能是最难掌握的概念之一。我们将一步一步地进行,以便您完全理解它。

一. 函数也是对象

        首先要搞清楚函数的性质:

def hi(name="yasoob"):
    return "hi " + name

print(hi())
# output: 'hi yasoob'

# We can even assign a function to a variable like
greet = hi
# We are not using parentheses here because we are not calling the function hi
# instead we are just putting it into the greet variable. Let's try to run this

print(greet())
# output: 'hi yasoob'

# Let's see what happens if we delete the old hi function!
del hi
print(hi())
#outputs: NameError

print(greet())
#outputs: 'hi yasoob'eer

结论1:函数可以当对象变量用。
 

二、在函数中定义函数

        因此,这些是功能方面的基础知识。让我们把你的知识更进一步。在 Python 中,我们可以在其他函数中定义函数:

def hi(name="yasoob"):
    print("now you are inside the hi() function")

    def greet():
        return "now you are in the greet() function"

    def welcome():
        return "now you are in the welcome() function"

    print(greet())
    print(welcome())
    print("now you are back in the hi() function")

hi()
#output:now you are inside the hi() function
#       now you are in the greet() function
#       now you are in the welcome() function
#       now you are back in the hi() function

# This shows that whenever you call hi(), greet() and welcome()
# are also called. However the greet() and welcome() functions
# are not available outside the hi() function e.g:

greet()
#outputs: NameError: name 'greet' is not defined

        所以现在我们知道我们可以在其他函数中定义函数。换句话说:我们可以制作嵌套函数。现在你需要再学习一件事,函数也可以返回函数。

三、从函数内部返回函数:

        不必在另一个函数中执行一个函数,我们也可以将其作为输出返回:

def hi(name="yasoob"):
    def greet():
        return "now you are in the greet() function"

    def welcome():
        return "now you are in the welcome() function"

    if name == "yasoob":
        return greet
    else:
        return welcome

a = hi()
print(a)
#outputs: <function greet at 0x7f2143c01500>

#This clearly shows that `a` now points to the greet() function in hi()
#Now try this

print(a())
#outputs: now you are in the greet() function

        再看一遍代码。在 if/else 子句中,我们返回的是 greet 和welcome,而不是 greet() 和welcome()。这是为什么?这是因为当你在它后面加上一对括号时,函数就会被执行;而如果你不把括号放在它后面,那么它可以被传递并可以分配给其他变量而不执行它。你明白了吗?让我更详细地解释一下。当我们写 a = hi() 时,hi() 会被执行,因为默认名称是 yasoob,所以会返回函数 greet。如果我们将语句更改为 a = hi(name = "ali") 则将返回欢迎函数。我们还可以执行 print hi()() 输出,现在您在 greet() 函数中。

四、将一个函数作为参数提供给另一个函数

def hi():
    return "hi yasoob!"

def doSomethingBeforeHi(func):
    print("I am doing some boring work before executing hi()")
    print(func())

doSomethingBeforeHi(hi)
#outputs:I am doing some boring work before executing hi()
#        hi yasoob!

        现在,您已经掌握了了解装饰器的真正含义所需的所有知识。装饰器让您可以在函数之前和之后执行代码。

五、写一个装饰器

        在上一个示例中,我们实际上制作了一个装饰器!让我们修改之前的装饰器,制作一个更实用的程序:

def a_new_decorator(a_func):

    def wrapTheFunction():
        print("I am doing some boring work before executing a_func()")

        a_func()

        print("I am doing some boring work after executing a_func()")

    return wrapTheFunction

def a_function_requiring_decoration():
    print("I am the function which needs some decoration to remove my foul smell")

a_function_requiring_decoration()
#outputs: "I am the function which needs some decoration to remove my foul smell"

a_function_requiring_decoration = a_new_decorator(a_function_requiring_decoration)
#now a_function_requiring_decoration is wrapped by wrapTheFunction()

a_function_requiring_decoration()
#outputs:I am doing some boring work before executing a_func()
#        I am the function which needs some decoration to remove my foul smell
#        I am doing some boring work after executing a_func()

        你明白了吗?我们只是应用了之前学到的原理。这正是装饰器在 Python 中所做的!它们包装一个函数并以一种或另一种方式修改其行为。现在您可能想知道为什么我们没有在代码中的任何地方使用@?这只是组成装饰功能的一种简短方法。下面是我们如何使用 @ 运行前面的代码示例。

@a_new_decorator
def a_function_requiring_decoration():
    """Hey you! Decorate me!"""
    print("I am the function which needs some decoration to "
          "remove my foul smell")

a_function_requiring_decoration()
#outputs: I am doing some boring work before executing a_func()
#         I am the function which needs some decoration to remove my foul smell
#         I am doing some boring work after executing a_func()

#the @a_new_decorator is just a short way of saying:
a_function_requiring_decoration = a_new_decorator(a_function_requiring_decoration)

        我希望您现在对装饰器在 Python 中的工作方式有一个基本的了解。现在我们的代码有一个问题。如果我们运行:

print(a_function_requiring_decoration.__name__)
# Output: wrapTheFunction

        这不是我们所期望的!它的名字是“a_function_requiring_decoration”。好吧,我们的函数被 wrapTheFunction 取代了。它覆盖了我们函数的名称和文档字符串。幸运的是,Python 为我们提供了一个简单的函数来解决这个问题,那就是 functools.wraps。让我们修改之前的示例以使用 functools.wraps:

from functools import wraps

def a_new_decorator(a_func):
    @wraps(a_func)
    def wrapTheFunction():
        print("I am doing some boring work before executing a_func()")
        a_func()
        print("I am doing some boring work after executing a_func()")
    return wrapTheFunction

@a_new_decorator
def a_function_requiring_decoration():
    """Hey yo! Decorate me!"""
    print("I am the function which needs some decoration to "
          "remove my foul smell")

print(a_function_requiring_decoration.__name__)
# Output: a_function_requiring_decoration
现在好多了。让我们继续学习装饰器的一些用例。

Blueprint:

from functools import wraps
def decorator_name(f):
    @wraps(f)
    def decorated(*args, **kwargs):
        if not can_run:
            return "Function will not run"
        return f(*args, **kwargs)
    return decorated

@decorator_name
def func():
    return("Function is running")

can_run = True
print(func())
# Output: Function is running

can_run = False
print(func())
# Output: Function will not run

        注意:@wraps 接受一个要装饰的函数,并添加复制函数名称、文档字符串、参数列表等的功能。这允许我们在装饰器中访问预装饰函数的属性。

5.1. 应用

        现在让我们看一下装饰器真正闪耀的领域,以及它们的使用使事情变得非常容易管理。下面看看装饰器的管理应用。

5.2. 用于权限管理

        装饰器可以帮助检查某人是否被授权使用 Web 应用程序中的端点。它们广泛用于 Flask Web 框架和 Django。这是一个使用基于装饰器的身份验证的示例:

Example :

from functools import wraps

def requires_auth(f):
    @wraps(f)
    def decorated(*args, **kwargs):
        auth = request.authorization
        if not auth or not check_auth(auth.username, auth.password):
            authenticate()
        return f(*args, **kwargs)
    return decorated

5.3. 用于日志管理

        日志记录是装饰者大放异彩的另一个领域。这是一个例子:

from functools import wraps

def logit(func):
    @wraps(func)
    def with_logging(*args, **kwargs):
        print(func.__name__ + " was called")
        return func(*args, **kwargs)
    return with_logging

@logit
def addition_func(x):
   """Do some math."""
   return x + x


result = addition_func(4)
# Output: addition_func was called

我相信你已经在考虑装饰器的一些巧妙用途。

六. 装饰器参数

        想一想,@wraps 不也是一个装饰器吗?但是,它需要一个参数,就像任何普通函数一样。那么,为什么我们也不能这样做呢?

        这是因为当您使用 @my_decorator 语法时,您正在应用一个将单个函数作为参数的包装函数。请记住,Python 中的一切都是对象,这包括函数!考虑到这一点,我们可以编写一个返回包装函数的函数。

6.1.函数装饰器

        让我们回到我们的日志示例,并创建一个包装器,让我们指定要输出到的日志文件。

from functools import wraps

def logit(logfile='out.log'):
    def logging_decorator(func):
        @wraps(func)
        def wrapped_function(*args, **kwargs):
            log_string = func.__name__ + " was called"
            print(log_string)
            # Open the logfile and append
            with open(logfile, 'a') as opened_file:
                # Now we log to the specified logfile
                opened_file.write(log_string + '\n')
            return func(*args, **kwargs)
        return wrapped_function
    return logging_decorator

@logit()
def myfunc1():
    pass

myfunc1()
# Output: myfunc1 was called
# A file called out.log now exists, with the above string

@logit(logfile='func2.log')
def myfunc2():
    pass

myfunc2()
# Output: myfunc2 was called
# A file called func2.log now exists, with the above string

6.2. 类装饰器

        现在我们在生产环境中使用了 logit 装饰器,但是当我们的应用程序的某些部分被认为是关键时,故障可能需要立即引起注意。假设有时您只想登录到文件。其他时候,您希望发送电子邮件,以便引起您的注意,并且仍然保留日志以供您自己记录。这是使用继承的一个例子,但到目前为止我们只看到了用于构建装饰器的函数。

class logit(object):

    _logfile = 'out.log'

    def __init__(self, func):
        self.func = func

    def __call__(self, *args):
        log_string = self.func.__name__ + " was called"
        print(log_string)
        # Open the logfile and append
        with open(self._logfile, 'a') as opened_file:
            # Now we log to the specified logfile
            opened_file.write(log_string + '\n')
        # Now, send a notification
        self.notify()

        # return base func
        return self.func(*args)

    def notify(self):
        # logit only logs, no more
        pass

        这个实现还有一个额外的优点,就是比嵌套函数方法更简洁,并且包装函数仍然会使用与以前相同的语法:

logit._logfile = 'out2.log' # if change log file
@logit
def myfunc1():
    pass

myfunc1()
# Output: myfunc1 was called

现在,让我们将 logit 子类化以添加电子邮件功能(尽管此处不会涉及此主题)。

class email_logit(logit):
    '''
    A logit implementation for sending emails to admins
    when the function is called.
    '''
    def __init__(self, email='admin@myproject.com', *args, **kwargs):
        self.email = email
        super(email_logit, self).__init__(*args, **kwargs)

    def notify(self):
        # Send an email to self.email
        # Will not be implemented here
        pass

        从这里开始,@email_logit 的工作方式与 @logit 类似,但除了记录日志外,还会向管理员发送一封电子邮件。

本文含有隐藏内容,请 开通VIP 后查看