13-Python-装饰器

本文详细介绍了Python装饰器的概念、实现原理及应用技巧,包括无参装饰器、带参数装饰器、多个装饰器装饰一个函数等内容,并提供了丰富的代码示例。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

1、装饰器的定义

装饰器的本质就是函数,用来装饰其它函数,就是为其它函数添加附加功能。

装饰器原则如下:

  • 不能修改被装饰的函数的源代码
  • 不能修改被装饰的函数的调用方式

2、实现装饰器知识储备

  • 函数即变量
 1 def bar():
 2     print("in the bar")
 3 def foo():
 4     print("in the foo")
 5     bar()
 6 
 7 foo()
 8 
 9 print("----------分割线-----------")
10 
11 def foo1():
12     print("in the foo")
13     bar1()
14 def bar1():
15     print("in the bar")
16 
17 foo1()
18 
19 print("----------分割线-----------")
20 # 这样会报错
21 # def foo2():
22 #     print("in the foo")
23 #     bar2()
24 # foo2()
25 # def bar2():
26 #     print("in the bar")

 

  • 高阶函数
  • 把一个函数名当作实参传递给另外一个函数(在不修改被装饰函数源代码的情况下为其添加功能)
 1 import time
 2 
 3 def bar1():
 4     time.sleep(3)
 5     print("in the bar")
 6 
 7 def test2(func):
 8     start_time = time.time()
 9     func()  # 相当于运行bar1()
10     stop_time = time.time()
11     print("total run time %s" %(stop_time - start_time))
12 
13 test2(bar1)  

 

  • 返回值中包含函数名(不能修改函数的调用方式)
 1 def bar2():
 2     time.sleep(3)
 3     print("in the bar2")
 4 
 5 def test3(func):
 6     print(func)
 7     return func
 8 
 9 print(test3(bar2))  # 获取的是内存地址
10 
11 res = test3(bar2)
12 res()

 

  • 嵌套函数
1 def foo():
2     print("in the foo")
3     def bar():
4         print("in the bar")
5     bar()  # 局部变量只能在其作用域内调用
6 
7 foo()
 1 x = 0
 2 def grandpa():
 3     x = 1
 4     def dad():
 5         x = 2
 6         def son():
 7             x = 3
 8             print(x)  # 最终打印结果为3
 9         son()
10     dad()
11 grandpa()

 

  • 高阶函数 + 嵌套函数 --》装饰器
 1 import time
 2 
 3 
 4 def timer(func):
 5     def deco():
 6         start_time = time.time()
 7         func()
 8         stop_time = time.time()
 9         print("total time is %s" % (stop_time - start_time))
10     return deco  # 返回deco()的内存地址
11 
12 
13 def test1():
14     time.sleep(3)
15     print("in the test1")
16 
17 
18 def test2():
19     time.sleep(3)
20     print("in the test2")
21 
22 #以下可直接用装饰器语法代替
23 timer(test1)  # test1的内存地址赋值给func,返回deco()的内存地址
24 print(timer(test1))  # 返回deco()的内存地址
25 test1 = timer(test1)  # 内存地址赋值给test1
26 test1()  # 相当于执行deco()
27 
28 timer(test2)
29 test2 = timer(test2)
30 test2()
31 
32 print("---------我是分隔符---------")
33 
34 
35 # 装饰器语法如下。(和上面引用的效果一样)
36 @timer  # 相当于test1 = timer(test1)
37 def test1():
38     time.sleep(3)
39     print("in the test1")
40 
41 
42 @timer  # 相当于test1 = timer(test1)
43 def test2():
44     time.sleep(3)
45     print("in the test2")
46 
47 
48 test1()
49 test2()

 

3、动态参数装饰器

def timer(bar):
    def inner(*args, **kwargs):
        start_time = time.time()
        foo_ret = bar(*args, **kwargs)
        end_time = time.time()
        used_time = end_time - start_time
        print(used_time)

        return foo_ret

    return inner


@timer
def foo(*args, **kwargs):
    time.sleep(1)
    print("我的参数:", args, kwargs)
    print("我的运行时间:")

    return "我的返回值"


ret = foo("动态参数装饰器", (1, 2), name="Druid", age=18)
print(ret)

  输出结果如下:

  

 

4、装饰器原理图解

 4.1 被装饰函数没有返回值

  

 

 4.2 被装饰函数有返回值

  

   注意:第二步仅为过程分析量,不作为真实的执行顺序。

5、装饰器固定格式

  装饰器的固定格式如下例所示:

def wrapper(func):
    """
    该函数为装饰器函数
    :param func: 这里的func参数实质是指向被装饰函数的内存地址
    :return:
    """
    def inner(*args, **kwargs):
        """
        该函数为装饰器函数内部函数
        :param args: 实质接收的是被装饰函数的位置参数
        :param kwargs: 实质接收的是被装饰函数的关键字参数
        :return: 返回的是被装饰函数的返回值
        """
        print("这里放被装饰函数执行之前要做的事")
        func_ret = func(*args, **kwargs)  # 被装饰的函数
        print("这里放被装饰函数执行之后要做的事")
        
        return func_ret

    return inner


@wrapper  # 等价于my_func = wrapper(my_func)
def my_func(*args, **kwargs):
    """
    该函数为被装饰函数
    :param args: 接收位置参数
    :param kwargs: 接收关键字参数
    :return: 返回值
    """
    print(*args, **kwargs)

    return ret


ret = my_func()  # 执行原函数,实质是执行inner()。函数返回值保存在变量ret中。

 

6、装饰器修复

  当我们使用装饰器去装饰某个函数时,我们想要引用被装饰函数原私有属性,如__name__、__doc__时,就有问题了,因为我们虽然仍然在执行被装饰函数,但其实执行的是闭包,看下例。

def wrapper(func):
    """
    该函数为装饰器函数
    :param func: 这里的func参数实质是指向被装饰函数的内存地址
    :return:
    """
    def inner(*args, **kwargs):
        """
        该函数为闭包(装饰器函数内部函数)
        :param args: 实质接收的是被装饰函数的位置参数
        :param kwargs: 实质接收的是被装饰函数的关键字参数
        :return: 返回的是被装饰函数的返回值
        """

        func_ret = func(*args, **kwargs)  # 被装饰的函数

        return func_ret

    return inner


@wrapper  # 等价于my_func = wrapper(my_func)
def my_func(*args, **kwargs):
    """
    该函数为被装饰函数
    :param args: 接收位置参数
    :param kwargs: 接收关键字参数
    :return: 返回值
    """
    print(*args, **kwargs)

    return "返回值"


my_func("装饰器没被修复前,被装饰函数原函数的私有属性如__name__、__doc__是获取不到的,如下:")  # 执行原函数,实质是执行inner()
print(my_func.__name__)  # 打印函数的名字
print(my_func.__doc__)  # 打印函数的注释文档

  输出结果如下:

  

  如果仍想使用被装饰函数的原私有属性,那么就可以用装饰器修复:

from functools import wraps


def wrapper(func):
    """
    该函数为装饰器函数
    :param func: 这里的func参数实质是指向被装饰函数的内存地址
    :return:
    """

    @wraps(func)
    def inner(*args, **kwargs):
        """
        该函数为闭包(装饰器函数内部函数)
        :param args: 实质接收的是被装饰函数的位置参数
        :param kwargs: 实质接收的是被装饰函数的关键字参数
        :return: 返回的是被装饰函数的返回值
        """

        func_ret = func(*args, **kwargs)  # 被装饰的函数

        print("装饰器修复不会改变原装饰器的作用")

        return func_ret

    return inner


@wrapper  # 等价于my_func = wrapper(my_func)
def my_func(*args, **kwargs):
    """
    该函数为被装饰函数
    :param args: 接收位置参数
    :param kwargs: 接收关键字参数
    :return: 返回值
    """
    print(*args, **kwargs)

    return "返回值"


my_func("装饰器被修复后,被装饰函数原函数的私有属性如__name__、__doc__就可以正常获取了,如下:")  # 执行原函数,实质是执行inner()
print(my_func.__name__)  # 打印函数的名字
print(my_func.__doc__)  # 打印函数的注释文档

  输出结果如下:

  

 

7、带参数的装饰器 

  需求:很多函数共用一个装饰器,要求随时可以关闭装饰器功能,且尽可能的减少代码修改。 该需求可以用标记位来实现,如下:

import time


FLAG = True


def timmer_out(flag):
    def timmer(func):
        def inner(*args, **kwargs):
            if flag:
                start_time = time.time()
                ret_func = func(*args, **kwargs)
                end_time = time.time()
                used_time = end_time - start_time
                print("函数{name}执行时间:{time}".format(name=func.__name__, time=used_time))
                # print("函数{name}执行时间:{time}".format_map({"name": func.__name__, "time": used_time}))
            else:
                ret_func = func(*args, **kwargs)

            return ret_func

        return inner

    return timmer


@timmer_out(FLAG)  # 第一步,先执行timmer_out(FLAG),得到返回值timmer。第二步执行@timmer,即 my_func1 = timmer(my_func1)
def my_func1():
    time.sleep(1)
    print("my_func1")


@timmer_out(FLAG)
def my_func2():
    time.sleep(1)
    print("my_func2")


@timmer_out(FLAG)
def my_func3():
    time.sleep(1)
    print("my_func3")


my_func1()
my_func2()
my_func3()

  当FLAG置为True时,装饰器功能生效,输出结果如下图所示:

  

  当FLAG置为False时,装饰器功能关闭,输出结果如下图所示:

  

 

8、多个装饰器装饰一个函数

 

def wrapper1(func):
    def inner1():
        print("wrapper1, before func")
        func()
        print("wrapper1, after func")

    return inner1


def wrapper2(func):
    def inner2():
        print("wrapper2, before func")
        func()
        print("wrapper2, after func")

    return inner2


@wrapper2
@wrapper1
def my_func():
    print("function is my func")


my_func()

  注意输出结果:

  

  为什么结果是这样?请看如下分析:

  

  为什么是先执行@wrapper1而不是@wrapeer2呢?因为装饰器在找到被装饰函数会优先执行。

 

转载于:https://www.cnblogs.com/Druidchen/p/7906705.html

窗口数目任你调   安装运行AcerGrid,此时,系统托盘区会出现一个显示器状图标,右击它,选择“Grid Configuration→Display 1”,在出现的级联菜单中可看到不同的分割方式,每种方式前都以直观的图示显示出窗格效果,你可以根据需要选择合适的方式(最多支持分成四个区域)。 窗口显示快速达   设置好窗口数后,咱们便可快速地使窗口显示到指定窗格中。有两种方法可快速来完成。   1.一拖而就   缩小要拖入的窗口(不是最小化哦),然后指指针移到其标题栏,按下鼠标左键进行拖动,便可显示窗格,此时将其拖曳到相应的区域中即可。程序会自动调整窗口,使其占满所在的窗格。   小提示:一定不能使窗口处于最大化状态,否则无法进行拖曳操作。 2.快速设置   上面的方法在窗口最大化时不能进行操作,给操作带来一些不方便。在实际操作中,还有一种更为便捷的方法,可将当前运行的窗口快速添加进窗格中,自由性更大。   AcerGrid安装完成后,会在以后打开的窗口右上侧添加三个相应的按钮。需要将当前窗口添加到栅格中时,只要单击“Acer GridVista Extensions”按钮,选择“Send Window to Grid”项,然后选择好要发送的窗格即可,很方便吧? 如果要让该窗口解除栅格状态,除了可以直接单击窗口中的“向下还原”按钮令其还原外,咱们还可单击“Lock to Grid”按钮来解除当前状态。另外,如果你想让当前窗口出现类似于Vista的透明效果的话,可以单击“Acer Vista Extensions”按钮,然后选择“Transparent”即可。 display1菜单中,normal是正常显示模式,double(vertical)窗口分成左右两个,triple窗口分成三个,quad,窗口分成四个,triplequad左边的小图标就是分屏后的样式。
### 如何使用笔记本电脑接口连接外接显示器 #### 连接方法 为了使笔记本电脑与外接显示器成功相连,需确认所使用的接口类型。常见的接口有 HDMI、VGA DisplayPort (DP)[^2]。 对于较新的笔记本型号,通常会配备 HDMI 或 DP 接口;而对于一些老款设备,则可能仅提供 VGA 接口[^3]。如果遇到特定情况,比如某些高性能图形处理需求下,可能会涉及到独立显卡直连的情况,这时应选择支持独显输出的接口如 DP 来获得更好的性能表现[^4]。 完成物理连接后,在大多数情况下,当正确接入电缆并开启两台装置电源时,Windows 操作系统应当自动检测新屏幕的存在,并启动扩展桌面模式[^1]。 #### 设置调整 尽管有时无需手动配置即可正常使用多屏环境,但还是建议进入控制面板中的「显示」选项来进一步优化布局: - 右键点击桌面上任意空白处,选取“显示设置” - 在打开窗口内找到关于多个显示屏的部分 - 用户可以根据实际摆放位置拖拽图标重新排列各监视器顺序 - 调整分辨率其他参数以匹配个人偏好或工作流程的要求 ```python import os def open_display_settings(): """模拟调用系统的显示设置界面""" command = "control /name Microsoft.Display" os.system(command) open_display_settings() ``` 此段 Python 代码可用于快速访问 Windows显示属性页面,方便用户进行上述提到的各项设定操作。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值