首页 > 其他分享 >3.1、装饰器基础

3.1、装饰器基础

时间:2024-03-23 09:01:37浏览次数:12  
标签:基础 --- test func 3.1 print 装饰 def

1、装饰器基本介绍

(1)装饰器原理

  • 装饰器在不改变原函数的调用方式的前提下,为原函数增加功能。
def w1(func):
    def inner():
        print("---正在验证权限----")
        func()
    return inner

def f1():
    print("---f1---")

def f2():
    print("---f2---")

# jy: 为增加原函数(f1)的功能, 此处改变了原函数的调用方式
innerFunc = w1(f1)
innerFunc()
"""
---正在验证权限----
---f1---
"""

# jy: 没有改变原函数的调用方式, 调用方式仍然为: f1()
f1 = w1(f1)
f1()
"""
---正在验证权限----
---f1---
"""


# jy: 不经过装饰, 原样输出: "---f2---"
f2()

(2)装饰器语法糖(@)

def w1(func):
    def inner():
        print("---正在验证权限----")
        func()
    return inner

#jy: 此处的 @w1 等价于: f1 = w1(f1)
@w1
def f1():
    print("---f1---")

#jy: 此处的 @w1 等价于: f2 = w1(f2)
@w1
def f2():
    print("---f2---")


f1()
"""
---正在验证权限----
---f1---
"""

f2()
"""
---正在验证权限----
---f2---
"""

2、多个装饰器装饰函数

# 装饰器-1
def makeBold(fn):
    def wrapped():
        print("----1---")
        return "<b>" + fn() + "</b>"
    return wrapped

# 装饰器-2
def makeItalic(fn):
    def wrapped():
        print("----2---")
        return "<i>" + fn() + "</i>"
    return wrapped

# jy: 两个装饰器装饰, 等价于如下代码逻辑(越接近被装饰函数的装
#     饰器会越优先对其进行打包装饰, 直到真正执行函数时, 最外层
#     的包的逻辑先执行, 随后再执行内层的):
"""
# 传入 makeItalic 的 test3 是最原始的 test3
test3 = makeItalic(test3) 
# 传入 makeBold 的 test3 是以上经过装饰后的 test3
test3 = makeBold(test3)     
"""
@makeBold
@makeItalic
def test3():
    print("----3---")
    return "hello world-3"

ret = test3()
print(ret)
"""
----1---
----2---
----3---
<b><i>hello world-3</i></b>
"""

3、装饰器中代码的执行逻辑

  • 装饰器什么时候开始进行装饰(装饰器中的代码什么时候执行)?
def w1(func):
    print("---正在装饰1----")
    def inner():
        print("---正在验证权限1----")
        func()
    return inner

def w2(func):
    print("---正在装饰2----")
    def inner():
        print("---正在验证权限2----")
        func()
    return inner

# jy: 两个装饰器装饰, 等价于(越接近被装饰函数的装饰器会越优先
#     对其进行打包装饰, 真正执行被装饰函数时, 最外层的包的逻
#     辑先执行, 随后再执行内层的):
"""
f1 = w2(f1)  # 传入 w2 的 f1 是最原始的 f1
f1 = w1(f1)  # 传入 w1 的 f1 是以上经过装饰后的 f1
"""
@w1
@w2
def f1():
    print("---f1---")

# f1 经过装饰后(还没执行 f1 函数), 就已输出以下内容:
"""
---正在装饰2----
---正在装饰1----
"""

# 调用 f1 后, 输出结果如下:
"""
---正在验证权限1----
---正在验证权限2----
"""
f1()

4、装饰无参函数

def func(functionName):
    print("---func---1---")
    def func_in():
        print("---func_in---1---")
        functionName()
        print("---func_in---2---")

    print("---func---2---")
    return func_in

# jy: 等价于: test = func(test), 因此在还没执
#     行 test() 时, 就已经会输出如下:
"""
---func---1---
---func---2---
"""
@func
def test():
    print("----test----")

# jy: 执行 test 时输出结果如下:
"""
---func_in---1---
----test----
---func_in---2---
"""
test()

5、装饰有参函数

(1)定长参数

  • 当原被装饰函数有参数时,需要在装饰器里的闭包函数的定义中也定义相应数量的参数(为了确保被装饰器装饰后调用方式不变,使得调用返回的闭包时传入的参数被传入到原函数中)
  • 因此,当装饰有参函数时,以下代码中的第 6、11、27 行中的函数参数个数必须保持一致。
def func(functionName):
    print("---func---1---")
	# jy: 如果缺失 a, b 参数, 会导致 test(11, 22) 的调用失败;
    #     因为经过装饰器装饰后, 调用 test(11, 22) 相当于是调用
    #     装饰器返回的闭包, 即此处的 func_in
    def func_in(a, b):     
        print("---func_in---1---")
		# jy: 如果缺失 a, b 参数传入, 会导致调用 test(11, 22)
        #     函数失败; 因为 functionName 代表原函数, 其调用方
        #     式必须与被装饰前的函数, 即 test(a, b) 保持统一
        functionName(a, b) 
        print("---func_in---2---")
        
    print("---func---2---")
    # jy: 由于返回的函数句柄带两个参数, 故后续调用时也需要传入两个
    #     参数; 且该函数句柄会将这两个参数直接传入原函数, 使得不改
    #     变原函数的调用方式; 因此, 当原被装饰函数有参数时, 为了
    #     确保被装饰器装饰后调用方式不变, 需要在装饰器里的闭包函数
    #     的定义中也定义相应数量的参数, 然后传入原函数中
    return func_in

# jy: 等价于: test = func(test), 此时被装饰后会输出如下:
"""
---func---1---
---func---2---
"""
@func
def test(a, b):
    print("---- test- a=%d, b=%d ---"%(a, b))


# jy: 调用函数时, 输出如下:
"""
---func_in---1---
---- test- a=11, b=22 ---
---func_in---2---
"""
test(11, 22)

(2)不定长参数

def func(functionName):
    print("---func---1---")
	# jy: 采用不定长参数的方式满足所有函数的传参情况
    def func_in(*args, **kwargs):
        print("---func_in---1---")
		# jy: 此处 args 前要带 *, kwargs 前要带 **; 如果不
        #     带, 则 args 为元祖, kwargs 为字典, 与后续被装
        #     饰函数的参数不一定一致
        functionName(*args, **kwargs) 
        print("---func_in---2---")
        
    print("---func---2---")
    return func_in

# jy: 装饰 test 函数时, 输出如下:
"""
---func---1---
---func---2---
"""
@func
def test(a, b, c):
    print("----test- a=%d, b=%d, c=%d ---"%(a, b, c))

# jy: 装饰 test2 时, 输出如下:
"""
---func---1---
---func---2---
"""
@func
def test2(a, b, c, d):
    print("----test- a=%d, b=%d, c=%d, d=%d ---"%(a, b, c, d))

# jy: 调用 test 时, 输出如下:
"""
---func_in---1---
----test- a=11, b=22, c=33 ---
---func_in---2---
"""
test(11, 22, 33)


# jy: 调用 test2 时, 输出如下:
"""
---func_in---1---
----test- a=44, b=55, c=66, d=77 ---
---func_in---2---
"""
test2(44, 55, 66, 77)

6、装饰带返回值的函数

def func(functionName):
    print("---func---1---")
    def func_in():
        print("---func_in---1---")
		# jy: 保存返回值
        ret = functionName() 
        print("---func_in---2---")
		# jy: 把返回值返回到函数调用中
        return ret 

    print("---func---2---")
    return func_in

# jy: 装饰 test 函数时, 输出如下:
"""
---func---1---
---func---2---
"""
@func
def test():
    print("----test----")
    return "haha"

# jy: 执行被装饰函数时, 输出如下:
"""
---func_in---1---
----test----
---func_in---2---
"""
ret = test()
# jy: test return value is haha
print("test return value is %s" % ret)

7、通用装饰器(装饰各种函数)

def func(functionName):
    def func_in(*args, **kwargs):
        print("-----记录日志-----")
        ret = functionName(*args, **kwargs)
        return ret

    return func_in

# 1) 装饰带返回值函数 ------------------------------------------------
@func
def test():
    print("----test----")
    return "haha"

# jy: 执行被装饰函数时, 输出如下:
"""
-----记录日志-----
----test----
"""
ret = test()
# jy: test return value is haha
print("test return value is %s" % ret)



# 2) 装饰无参函数 ---------------------------------------------------
@func
def test2():
    print("----test2---")

# jy: 执行被装饰函数时, 输出如下:
"""
-----记录日志-----
----test2---
"""
a = test2()
# jy: test2 return value is None
print("test2 return value is %s" % a)



# 3) 装饰有参函数 --------------------------------------------------
@func
def test3(a):
    print("-----test3-- a=%d --" % a)

# jy: 执行被装饰函数时, 输出如下:
"""
-----记录日志-----
-----test3-- a=11 --
"""    
test3(11)

8、带参装饰器

(1)普通带参装饰器

def func_arg(arg):
    def func(functionName):
        def func_in():
            print("---记录日志- arg=%s --" % arg)
            if arg == "heihei":
                functionName()
                functionName()
            else:
                functionName()
        return func_in
    return func

# jy: 带参装饰器的执行流程如下:
#     1) 先执行 func_arg("heihei") 函数, 该函数的返回结果
#        是 func 函数句柄(即普通不带参装饰器)
#     2) @func, 即普通不带参装饰器逻辑, 区别仅在于装饰器中的闭
#        包的 arg 对应为 "heihei";
#     3) 使用 @func 对 test 进行装饰, 当执行 test() 时会执
#        行相应的逻辑
@func_arg("heihei")
def test():
    print("--test--")

# jy: 同理如上; 可见, 带参装饰器能在运行时, 基于传入装饰器中的
#     参数的不同选择执行不同的功能
@func_arg("haha")
def test2():
    print("--test2--")

# jy: 执行被装饰函数 test 时, 输出结果如下:
"""
---记录日志- arg=heihei --
--test--
--test--
"""
test()

# jy: 执行被装饰函数 test2 时, 输出结果如下:
"""
---记录日志- arg=haha --
--test2--
"""
test2()

(2)通用带参装饰器(装饰函数可带参数、可有返回值)

def func_arg(arg):
    def func(functionName):
        def func_in(*args, **kwargs):
            print("---记录日志- arg=%s --" % arg)
            if arg == "heihei":
                print("==== 传入装饰器的参数为 heihei ====")
                ret = functionName(*args, **kwargs)
            else:
                print("==== 传入装饰器的参数为其它 ====")
                ret = functionName(*args, **kwargs)
            return ret
        return func_in
    return func


@func_arg("heihei")
def test1():
    print("--test1--")

# jy: 执行被装饰函数时, 输出结果如下:
"""
---记录日志- arg=heihei --
==== 传入装饰器的参数为 heihei ====
--test1--
"""
test1()
    
    
    
@func_arg("heihei")
def test2(a, b):
    print("--test2--")
    return a + b

# jy: 执行被装饰函数时, 输出结果如下:
"""
---记录日志- arg=heihei --
==== 传入装饰器的参数为 heihei ====
--test2--
"""
res = test2(2, 3)
# jy: 5
print(res)



@func_arg("haha")
def test3():
    print("--test3--")

# jy: 执行被装饰函数时, 输出结果如下:
"""
---记录日志- arg=haha --
==== 传入装饰器的参数为其它 ====
--test3--
"""
test3()

9、使用装饰器的注意事项

  • 装饰器的实现过程中,被装饰后的函数其实已经是另外一个函数(函数名等函数属性会发生改变)
  • functools包中的wraps装饰器可消除这样的副作用:实现装饰器时,最好在装饰器中的闭包定义之前加上@functools.wrap(func)(即对装饰器中的闭包进行进一步装饰,传入的参数为函数句柄),使得可以保留原函数func的函数属性(如函数名等)。

(1)不加@functools.wrap(func),被装饰函数的属性发生改变

def my_decorator(func):
    def wrapper(*args, **kwargs):
        '''decorator'''
        print('Calling decorated function...')
        return func(*args, **kwargs)
    return wrapper

@my_decorator
def example():
    """Docstring"""
    print('Called example function')

# jy: 输出: "wrapper decorator", 因为 example 经过装
#     饰后, 函数属性发生了改变;
print(example.__name__, example.__doc__)

(2)加@functools.wrap(func),被装饰函数保留原有属性

import functools

def my_decorator(func):
    # jy: 对装饰器中的闭包进行进一步装饰(装饰时传入的参数为函
    #     数句柄 func);
    @functools.wraps(func)
    def wrapper(*args, **kwargs):
        '''decorator'''
        print('Calling decorated function...')
        return func(*args, **kwargs)
    return wrapper

@my_decorator
def example():
    """Docstring"""
    print('Called example function')

# jy: 输出: "example Docstring"    
print(example.__name__, example.__doc__)

标签:基础,---,test,func,3.1,print,装饰,def
From: https://blog.csdn.net/m0_66491750/article/details/136862391

相关文章

  • 图论基础|417. 太平洋大西洋水流问题、827.最大人工岛、127. 单词接龙
    目录417.太平洋大西洋水流问题827.最大人工岛127.单词接龙417.太平洋大西洋水流问题题目链接(opensnewwindow)有一个m×n的矩形岛屿,与太平洋和大西洋相邻。“太平洋”处于大陆的左边界和上边界,而“大西洋”处于大陆的右边界和下边界。这个岛被分割......
  • Java-Java基础学习(5)-注解和反射以及类的加载过程分析
    4.1注解的理解Annotation是从JDK5.0开始引入的新技术Annotation的作用不是程序本身,可以对程序作出解释(这点和注释comment没什么区别);可以被其他程序(比如:编译器等)读取;Annotation的格式注解是以“@注释名”在代码中存在的,还可以添加一些参数值,例如:@SuppressWarnings(v......
  • 深入理解 C++ 语法:从基础知识到高级应用
    C++语法让我们将以下代码分解以更好地理解它:示例#include<iostream>usingnamespacestd;intmain(){cout<<"HelloWorld!";return0;}示例解释第1行:#include<iostream>是一个头文件库,它让我们可以使用输入和输出对象,比如cout(在第5行使用)。头文件为......
  • 显卡基础知识及元器件原理分析
    显卡应该算是是目前最为火热的研发方向了,其中的明星公司当属英伟达。当地时间8月23日,英伟达发布截至7月30日的2024财年第二财季财报,营收和利润成倍增长,均超市场预期。财报显示,第二财季英伟达营收为135.07亿美元,同比增长101%,环比增长88%。美国通用会计准则(GAAP)下,净利润为6......
  • java基础50道面试题
    java基础50道面试题一、java基础1.equals与==区别在Java中,"=="是一个比较操作符,用于比较两个变量的值是否相等。而"equals()"是Object类中定义的方法,用于比较两个对象是否相等。具体区别如下:1."=="用于比较基本数据类型和引用类型变量的地址值是否相等。对于基本数据类型,比......
  • 时序分析:基础知识整理(三)差分转单端的约束等
    之后的都只有我个人能看,想看的请支持单刀大佬。主时钟约束主时钟约束,就是我们对主时钟(PrimaryClock)的时钟周期进行约束(告诉综合工具布局布线的标准),这个约束是我们用的最多的约束了,也是最重要的约束。主时钟必须与一个网表对象相连,该对象代表了所有时钟边沿的开始点,并且在时钟......
  • Java基础面试题(一)
    1.解释下什么是面向对象?面向对象和面向过程的区别?面向对象(Object-Oriented,简称OO)是一种程序设计范式或编程范式,也是一种程序开发的方法。它将对象作为程序的基本单元,将程序和数据封装在对象中,以提高软件的可重用性、灵活性和扩展性。在面向对象编程中,有以下几个核心概念:......
  • Bootloader/IAP零基础入门(0) —— Bootloader/IAP的前置知识
    前言(1)如果有嵌入式企业需要招聘湖南区域日常实习生,任何区域的暑假Linux驱动/单片机/RTOS的实习岗位,可C站直接私聊,或者邮件:[email protected],此消息至2025年1月1日前均有效(2)本章节主要是进行一些基础科普,对这部分了解的,可自行跳到后面章节。Bootloader/IAP的前置知......
  • Java基础面试题(四)
    1.深克隆和浅克隆的区别?深克隆和浅克隆的主要区别在于它们处理对象中的引用类型字段的方式不同,这导致它们在复制对象时的行为有所不同。浅克隆(ShallowClone)在复制对象时,对于非基本类型(即引用类型)的属性,只复制其引用地址,而不复制引用的对象本身。这意味着,原始对象和克隆......
  • 时序分析:基础知识整理(二)
    搬运自:孤独的单刀;大佬后面的是付费项目,所以涉及付费项目的我不会公开,本博客纯方便自己看做笔记。输出延时时间Tco由clk触发到输出数据有效之间最大延迟时间,对应图1的Tco(clockoutputdelay)组合逻辑与时序逻辑组合逻辑电路数字电路根据逻辑功能的不同特点,可以分成两......