zoukankan      html  css  js  c++  java
  • 测开之函数进阶· 第7篇《装饰器装饰类,通用装饰器,有啥区别呢?》

    坚持原创输出,点击蓝字关注我吧

    作者:清菡
    博客:oschina、云+社区、知乎等各大平台都有。

    由于微信公众号推送改为了信息流的形式,防止走丢,请给加个星标 ⭐,你就可以第一时间接收到本公众号的推送!

    目录

    • 一、什么是装饰器
      • 1.开放封闭原则(面向对象原则的核心)
      • 2.装饰器的作用
    • 二、实现一个装饰器
      • 1.不带参数的装饰器
      • 2.装饰器的原理
      • 3.组装方便,拆卸也方便
      • 4.带参数的装饰器
    • 三、通用装饰器
    • 四、装饰器装饰类
      • 1.不带参数的
      • 2.带参数的
    • 五、装饰器的应用场景
    • 六、补充
      • 1.*号的作用
      • 2.装饰器装饰类和装饰函数的不同点

    一、什么是装饰器

    1.开放封闭原则(面向对象原则的核心)

    对已经实现的功能(项目已经上线了),在这个基础上增加新功能,也可以在它的基础上进行拓展,这个就是开放。如果你要去再修改它内部的代码,这个时候是不允许的,对内部的修改是封闭的。

    就是你实现的功能可以拓展,但是你不要去修改它内部的代码。

    比如index()是接口,返回是“这个是网站的首页”,只要调用这个接口就会返回一个“这个是网站的首页。”

    突然有个需求,在进入网站之前需要先登录校验一下。这个时候需要拓展,如何拓展?就需要用装饰器了。

    def index():
        print("这个是网站的首页")
    

    这个已经实现的接口,不能去修改的。

    2.装饰器的作用

    装饰器可在不更改这个函数里面任何代码的基础上,给它添加新的功能。

    二、实现一个装饰器

    1.不带参数的装饰器

    装饰器其实就是一种闭包的应用。要使用装饰器,可以先定义个闭包函数。把登录校验的功能写在了闭包函数的内部。

    把闭包函数当成装饰器来用的话,外面接收的参数需要传一个函数,你要装饰哪个函数,你就传哪个函数。

    
    # 开放封闭原则
    def login(func):
        def fun():
            # 简单的校验
            username = 'python01'
            password = 'qinghan'
            user = input("请输入账号:")
            pw = input("请输入密码:")
            # 判断下账号密码对不对
            if username == user and pw == password:
                func()  # 登录得账号密码都正确的情况下,调用这个函数
            else:
                print("账号或密码错误")
        return fun
    
    
    @login  # 艾特一下这个装饰器
    def index():
        print("这个是网站的首页")
    
    
    index()
    
    

    2.装饰器的原理

    将被装饰的函数当作一个参数传到装饰器中,并且让被装饰的函数名指向装饰器内部的函数,在装饰器的内部函数中用接收到的参数再调用被装饰的函数。

    @login是 Python 中的一个语法糖。它的作用是:index=login(index)。传入index,然后被index接收。

    如何做到通过func()调用原函数?

    @login等于index=login(index)

    自动将index当作参数传入login这个函数里面,去执行login(func)这个函数,检测到这个fun()函数,将这段代码:

     def fun():
            # 简单的校验
            username = 'python01'
            password = 'qinghan'
            user = input("请输入账号:")
            pw = input("请输入密码:")
            # 判断下账号密码对不对
            if username == user and pw == password:
                func()  # 登录得账号密码都正确的情况下,调用这个函数
            else:
                print("账号或密码错误")
    

    直接跳过。

    return fun直接将结果返回出来。结果返回出来又给index()接收,调用index()的时候实际上是调用fun()函数。

    执行fun()函数里面的代码。通过func()调用原函数,怎么做到的?

    fun()函数是放在index.__closure__这个属性里面。

    然后在下面调用func()的时候,就是去index.__closure__这个属性里面找到对应存储的那块代码。

    存储的代码就是这个:

    def index():
        print("这个是网站的首页")
    

    以上,就是装饰器装饰的流程。

    3.组装方便,拆卸也方便

    我想改成不用登录也可以访问,直接去掉@login这个装饰器就可以了。

    
    # 开放封闭原则
    def login(func):
        def fun():
            # 简单的校验
            username = 'python01'
            password = 'qinghan'
            user = input("请输入账号:")
            pw = input("请输入密码:")
            # 判断下账号密码对不对
            if username == user and pw == password:
                func()  # 登录得账号密码都正确的情况下,调用这个函数
            else:
                print("账号或密码错误")
        return fun
    
    
    # @login  # 艾特一下这个装饰器
    def index():
        print("这个是网站的首页")
    
    # index.__closure__
    index()
    
    

    这样操作,不会对原来有什么影响。

    4.带参数的装饰器

    实现两个数相加后,又有新的需求,需要可以相乘、相除。

    def add(func):
        def fun(a, b):
            print("相乘", a * b)
            print("相除", a / b)
            func(a, b)
    
        return fun
    
    
    @add
    def add_num(a, b):
        # 打印两个数相加
        print("相加:", a + b)
    
    
    add_num(11, 22)
    
    

    参数传递的过程

    三、通用装饰器

    如果同一个装饰器既要装饰有参数的函数,又要装饰无参数的函数。

    那么我们在传参的时候就设置成不定长参数,这样不管被装饰的函数有没有参数都能用。

    # 通用装饰器
    def add(func):
        def fun(*args, **kwargs):
            print("装饰器的功能代码:登录")
            func(*args,**kwargs)
        return fun
    
    
    @add
    def index():
        print("这个是网站的首页")
    
    @add
    def good_list(num):
        print("这个是商品列表第{}页".format(num))
    
    index()
    print("------------")
    good_list(9)
    

    四、装饰器装饰类

    1.不带参数的

    #装饰器装饰类
    
    def add(func):
        def fun(*args, **kwargs):
            print("装饰器的功能代码:登录")
            return func(*args,**kwargs)
        return fun
    
    
    @add  # MyClass=add(MyClass)
    class MyClass:
        def __init__(self):
            pass
    
    m = MyClass()
    print("m的值:",m)
    

    装饰器装饰类的原理

    把类当作一个参数传到装饰器里面。return fun返回的是funMyClass接收到的是fun

    MyClass()调用的是fun

    执行代码:

     def fun(*args, **kwargs):
            print("装饰器的功能代码:登录")
            return func(*args,**kwargs)
    

    这里面的功能。

    先执行装饰器的功能,return func(*args,**kwargs)func()来自def add(func)

    调用MyClass这个类,return func(*args,**kwargs)创建了个对象,MyClass()调用完了接收,m 就能接收这个对象了。

    这个就是装饰器装饰类的一个原理。

    2.带参数的

    #装饰器装饰类
    
    def add(func):
        def fun(*args, **kwargs):
            print("装饰器的功能代码:登录")
            return func(*args,**kwargs)
        return fun
    
    
    @add  # MyClass=add(MyClass)
    class MyClass:
        def __init__(self,name,age):
          self.name=name
          self.age=age
    
    m = MyClass("qinghan","18")
    print("m的值:",m)
    

    这里用的是不定行参数,所以不管你装饰的类是有参数的还是没参数的,都可以。

    五、装饰器的应用场景

    1.登录校验。(在装饰器里面判断下你有没有登录)

    2.函数运行时间统计。

    3.执行函数之前做准备工作。

    4.执行函数后清理功能。

    六、补充

    1.*号的作用

    *是进行拆包作用的。把每个元素拿出来,当作参数进行传递。

    一个*是对元组形式的位置参数进行拆包,两个**对关键字参数进行拆包。

    2.装饰器装饰类和装饰函数的不同点

    类需要把对象返回出来。


    公众号清菡软件测试首发,更多原创文章:清菡软件测试 116+原创文章,欢迎关注、交流,禁止第三方擅自转载。

  • 相关阅读:
    用SQL查询方式显示GROUP BY中的TOP解决方法[转]
    三大UML建模工具Visio、Rational Rose、PowerDesign的区别
    Eclipse HTML Editor
    [转]跨平台开发:PhoneGap移动开发框架初探
    取消开机显示登陆页面
    PhoneGap开发环境搭建
    显示器不能全屏及开机慢解决方案
    调用webservice超时问题的解决[转]
    最简单的JAVA解析XML字符串方法
    适用于vue项目的打印插件(转载)
  • 原文地址:https://www.cnblogs.com/qinghan123/p/14235763.html
Copyright © 2011-2022 走看看