• python 装饰器,进阶与运用


    小 tips

    写递归函数的章节提到过,当函数不断调用自身,直到被pycharm发现抛出异常。实际上是因为栈溢出。

    什么是栈溢出呢?

    python中只要调用一个函数,函数中自己调用自己,每次调用都会放到内存中反复递归,但是当原函数没结束时(就是说没有设置递归结束标示),递归就不会结束,直到内存被用完,栈溢出。

    另外递归的性能是特别不好的,所以一般是不使用递归函数去编写功能。

    闭包函数

    在进入装饰器之前,先讲解闭包函数的概念。

    函数中调用函数本身,那么在函数中可不可以定义一个函数呢?

    需求,如何函数外部调用内部定义的函数

    问题引入,什么是闭包?

    闭包的概念:

      1. 函数中嵌套一个函数
      2. 外层函数返回内层函数的变量名
      3. 内层函数对外部作用域有一个非全局变量的引用

    来一个最简单的闭包案例

    def login():
        print("登录"num = 100  # 这是个全局变量
              
    def func():
        print("-----func被调用")
        num =101  # 非全局变量,因为命名空间在外层函数中(相较于内层被嵌套函数而言)
       def count_book():
              print(num)  # 内部函数对外部作用域有一个非全局的引用
              print("函数内部嵌套的函数")
       return count_book  # 外层函数返回的是内层函数的变量名
              
      # 进行调用
      res = func()  # 通过return返回函数内部的函数,然后使用
      res()
      # 或者
     func()()  # 这样的方式也可以访问到内部函数

    函数里面定义的函数,相较于外部而言是一个局部变量,外部是无法访问函数内部的函数的。

    闭包函数的作用

    闭包函数会将传入的局部变量存储在__closure__中,记录的是非全局变量,且从外部函数的__clourse__是访问不到的。

    #闭包有什么作用?
    def func(num,b):
       def count_book():
          print(num)
          print(b)
          print("这个是闭包函数")
       return count_book
    res = func([1,2,3],"python")
    print(res.__closure__)  # 闭包函数会将传入的局部变量存储在__closure__中
    # 记录的是非全局变量,且从外部函数的__closure__是访问不到的
    print(func.__closure__)
    # 达成数据锁定 从而不管外部环境发生任何变化,内部的变量都不会发生变化

    装饰器

    接下来开始今天的硬菜,装饰器的学习。

    开放封闭原则

    所谓开放封闭原则,是一种程序设计规范,软件实体应该是可扩展,而不可修改的,也就是说,对扩展是开放的,而对内部原码的修改是封闭的。

    装饰器的作用

    装饰器则是这一理念的实现,即在不修改原方法的前提下进行功能的扩展

    装饰器的应用场景

    登录验证,函数运行时间统计,执行函数之前进行的操作,执行函数之后做的清理操作(有点类似unittest,pytest的前置后置方法)

    来看一个实例

    # 实现一个装饰器
    username ="python"
    pwd = "123"
    
    def login(func):  # 将闭包函数改造成装饰器,需要往函数内传入一个函数 装饰那个函数就传入那个函数
        def fun():
            user = input("请输入账号")
            password = input("请输入密码")
            if username == user and pwd==password:
                func()  # 账号和密码都正确的时候才调用被装饰的函数
            else:
                print("账号或者密码错误")
        return fun
    # 装饰器中的形参func 就相当于被装饰的函数对象,
    # 希望给被装饰的函数添加什么功能(或者什么时候想要调用被装饰函数,
    @login
    def index():
        print("这个是网站的首页")
    
    # index()  # 加入需求是登入之前先进行验证账号,要求不能修改登录原函数,添加一个验证
    # closure__ = index.__closure__
    # 装饰器其实就是一个闭包

    调用原理:

    @login 添加在index()方法上--->login(index) 即将方法作为一个参数传入了login方法中

    然后进入login函数中的内部函数执行,首先进行验证,验证通过则调用index函数,即func形参代表的传入的index方法。

    不通过则输出对应字符串,最后将内部函数返回,以供调用者使用

     

    有参装饰器

    需求:打印两个数相加,然后打印相乘,相除的结果,要求不能修改原方法代码

    def add_num(a,b): # 原函数
       print('相加:',a+b)
    def add(func):
       def fun(a, b):  # 传参先进入这里
          print('相乘',a*b)
          print('相除',a/b)
          func(a,b)  # 此处为被修饰的原函数的调用 如果忘记此处,原函数将不会被运行
       return fun 
    @add
    add_num(1,2)

    调用原理:

    @add 加在add_num头上等价于---->add(add_num(1,2))--->进入add装饰器方法中的内部函数

    然后执行内部函数中的逻辑,通过形参func去调用传进来的实参add_num去执行原函数。

    但是原函数有参,所以func(a,b)传入a,b形参最后返回内部函数给调用者,完成整个调用。

    clipboard.png

    再进阶,可传参和不可传参我全都要

    clipboard1.png

    再再进阶,装饰一个类

    clipboard2.png

  • 相关阅读:
    VLBERT: PRETRAINING OF GENERIC VISUALLINGUISTIC REPRESENTATIONS AHU
    psql客户端连接PG报错“psql error expected authentication request from server, but received v”
    C++ bitset的使用
    利用拓扑排序求 DAG 最短路
    AcWing 1124. 骑马修栅栏
    AcWing 164. 可达性统计
    AcWing 1184. 欧拉回路
    P1339 [USACO09OCT]Heat Wave G
    AcWing 1192. 奖金 [一题三解]
    AcWing 1185. 单词游戏
  • 原文地址:https://www.cnblogs.com/addicated/p/13167349.html
Copyright © 2020-2023  润新知