• 装饰器--初识


    函数调用的顺序

    和其他语言类似。python函数在未经声明之前,不允许对其引用和调用。

     1 1、未声明之前,调用或者引用会报错。
     2 def t1():
     3     print('This is t1...')
     4     t2()
     5 
     6 t1()
     7 
     8 def t2():
     9     print('This is t2...')
    10 
    11     
    12 显示结果:
    13 This is t1...
    14 Traceback (most recent call last):
    15   File "D:/软件/pychar/data/s13/practise1.py", line 37, in <module>
    16     t1()
    17   File "D:/软件/pychar/data/s13/practise1.py", line 35, in t1
    18     t2()
    19 NameError: name 't2' is not defined
    20 
    21 
    22 
    23 2、正确的调用顺序:(先声明函数,在引用或者调用)
    24 def t1():
    25     print('This is t1...')
    26     t2()
    27 
    28 def t2():
    29     print('This is t2...')
    30 
    31 t1()
    32 
    33 显示结果:
    34 This is t1...
    35 This is t2...

    函数的功能与作用

    需要添加一个打印日志功能。
    在没学函数的时候,只能用print显示打印,例如:

    1 def t1():
    2     pass
    3     print('logging')
    4    
    5 def t2():
    6     pass
    7     print('logging')

    学了函数以后可以定义一个函数,例如:

    def logger():
        print('logging')
    
    def t1():
        pass
       logger()   #直接调用函数
       
    def t2():
        pass
        logger()

    装饰器的功能与特点

    对已经上线的代码需要添加功能:

    问题:
    1、代码行数很多,查找很麻烦,而且要挨个找出,把新功能函数加进去。
    2、代码已经上线,对源代码修改可能会导致未知故障发生。(新增功能是不能修改源代码的)
    3、代码已经上线,不光源代码不能随便修改,就连函数调用方式也不能随意修改。

    解决方法:使用装饰器进行代码修饰。

    装饰器的概述

    一、定义:

    装饰器本身就是一个函数,遵循函数的声明,引用,调用,作用域等相关规则。

    二、作用:

    装饰器:目的是(装饰其他函数)就是为其他函数添加附加功能。

    三、用途:

    装饰器:最常见的用途主要体现在用户登录与权限认证。

    四、原则:

    1、不能修改被装饰的函数的源代码。
    2、不能修改被装饰的函数的调用方式。
    3、装饰器对被装饰的函数时完全透明的。(源代码感知不到装饰器函数的存在,但装饰器函数确实影响着源代码)
     
     
    五、调用方法:
    @+装饰器的函数名;
    一个原函数体可以调用多个装饰器,执行顺序如下:

     装饰器的知识储备

    1、函数即"变量"
    2、高阶函数。
    3、嵌套函数。
    最终:高阶函数+嵌套函数 =》装饰器函数

     知识点一:函数即“变量”:

    定义一个x或者y是装在内存中声明一个内存块,并通通过存地址指定给x或者y,当调用x或者y时可以加载在内存块中进行引用或调用。

    定义一个test的函数的原理同上。

    定义一个无名子的函数体原理也同上。

    注意:
    解释器会先回收无名子的函数体所占的内存块(即lambda的函数模块)
    del 并不是删除内存块,而是直接删除内存定义的名字,通过解析器在清除内存块。

    知识点二、高阶函数:

    满足下面两个条件之一就是高阶函数
    a:把一个函数名当做实参传给另外一个函数,即某一函数当做参数传入另一个函数中(在不修改被装饰函数源代码的情况下添加功能)
    b:返回值中包含函数名,即函数的返回值包含n个函数,n>0(不修改函数的调用方式)

    练习a:

     1 #!/usr/bin/env python
     2 # -*- coding:utf8 -*-
     3 # Author:Dong Ye
     4 
     5 import time
     6 
     7 #定义一个源代码。要求不允许改变源代码和调用方式的情况下进行装饰:
     8 def bar():   #定义个变量,模拟线上函数体
     9     time.sleep(3)
    10     print("in the bar")
    11 
    12 
    13 #用高阶函数装饰bar代码
    14 def test1(func):    #定义一个形参,用于接收调用test1函数的实参,如果传过来的是函数名,则该函数已经被应用。
    15     start_time =  time.time()
    16     func()         #调用bar函数,类似于x=1,y=x  显示y。而func = bar,直接调用func
    17     stop_time = time.time()
    18     print("the func run time is %s (non bar run time)" %(stop_time-start_time))
    19 
    20 test1(bar)  #调用仿装饰器传实参给test1(模拟线上环境的函数)
    某一函数当做参数传入另一个函数中
    重点:结合装饰器的重要的两个特点,做个练习1:
    1、不能修改被装饰的函数的源代码。
    2、不能修改被装饰的函数的调用方式。
     
    问题:
    1、装饰器并没有修改源代码bar
    2、由于test1(bar)改变了调用函数的方式,因此不符合装饰器的条件。
     
    注释:
    test1(bar)与test1(bar())的区别:
    test1(bar):是把内存地址通过实参方式传给test1函数作为形参。
    test1(bar()):是把内存中的函数体通过实参方式传给test1函数作为形参。

     练习b:返回值中包含函数名。(不修改函数的调用方式)

     1 #!/usr/bin/env python
     2 # -*- coding:utf8 -*-
     3 # Author:Dong Ye
     4 '''
     5 import time
     6 
     7 #定义一个源代码。要求不允许改变源代码和调用方式的情况下进行装饰:
     8 def bar():   #定义个变量,模拟线上函数体
     9     time.sleep(3)
    10     print("in the bar")
    11 
    12 
    13 #用高阶函数装饰bar代码
    14 def test1(func):    #定义一个形参,用于接收调用test1函数的实参,如果传过来的是函数名,则该函数已经被应用。
    15     start_time =  time.time()
    16     func()         #调用bar函数,类似于x=1,y=x  显示y。而func = bar,直接调用func
    17     stop_time = time.time()
    18     print("the func run time is %s (non bar run time)" %(stop_time-start_time))
    19 
    20 test1(bar)  #调用仿装饰器传实参给test1(模拟线上环境的函数)
    21 
    22 '''
    23 
    24 import time
    25 
    26 #定义模拟线上代码
    27 def bar():
    28     time.sleep(3)
    29     print('in the bar')
    30 
    31 
    32 #用高阶函数做装饰器
    33 def test2(func):
    34     print(func) #打印bar的内存地址
    35     return func  #返回test2的运行结果地址
    36 
    37 #调用方式一:展示返回值
    38 print(test2(bar))
    39 
    40 #调用方式二:获取返回值
    41 t = test2(bar)
    42 print(t)
    43 #注释:
    44 #首先、test2函数把bar传到test2函数里,然后打印bar函数的内存地址。
    45 #其次、将bar的内存地址return返回给t。
    46 #最后、打印t接收的bar地址。
    47 
    48 #调用方式三:变量名覆盖
    49 bar = test2(bar) #将装饰器返回bar()的内存地址,覆盖掉原函数bar
    50 bar()  #原函数调用方式
    51 
    52 #注释:
    53 #由于bar在源代码中已经定义了。
    54 #为了不影响源代码函数的调用方式,只提取了bar一开始传实参的内存地址
    55 #当装饰器函数的功能执行完毕后,在将bar的内存地址返回,重新赋值给变量
    56 #最后、从原函数的被调用的角度来看并没有改变bar()的调用方式,
    57 # 而是在原基础上利用装饰器返回的bar的内存值进行了第二次赋值。
    58 #这样装饰器功能不仅能够与源代码完美结合,又能满足不修改源代码,不修改调用函数的方法。
    View Code

     知识点三、嵌套函数:

     在一个函数体内创建另外一个函数,这种函数就叫内嵌函数(基于python支持静态嵌套域)

     1 #嵌套函数:
     2 #概念:在一个函数的函数体内,用def去声明一个子函数
     3 #作用:高阶函数+嵌套函数=装饰器
     4 def foo():
     5     print('in the foo')
     6     def bar():
     7         print('in the bar')
     8 
     9     #在调用bar函数的时候,就跟调用局部变量一样,只能在内部调用
    10     bar()
    11 foo()
    12 
    13 
    14 
    15 #局部作用域和全局作用域的访问顺序:
    16 x = 0
    17 def grandpa():
    18     #x = 1
    19     def dad(): #相当于定义了一个变量,如果不调用就相当于什么也没做。
    20         x = 2
    21         def son():
    22             x = 3
    23             print(x)  #这个值应该是3,因为只定义在son这个函数体里
    24 
    25         son()  #如果不调用就不会显示3,因为嵌套函数时一层一层的找。
    26     dad()  #上面的定义如果不在这里调用,就等于什么也没做。
    27 grandpa()  
    嵌套函数练习
  • 相关阅读:
    企业微信小程序用getImageInfo保存图片提示fail download image fail
    post提交定义传输数据格式-设置请求头content-type
    微信小程序-wxml模板页面逻辑运算
    微信小程序-页面间传参
    微信小程序-wxml标签绑定data值传参给js方法(事件传参)
    微信小程序-子组件调用父组件
    微信小程序-调用组件内方法(父组件调用子组件)
    input中number类型去掉上下箭头
    js slice 截取数组或字符串
    vue组件库小练习——目录
  • 原文地址:https://www.cnblogs.com/abobo/p/8058878.html
Copyright © 2020-2023  润新知