python中装饰器之真面目一

写代码要遵循开发封闭原则,规定已经实现的功能代码不允许被修改,但可以被扩展,通俗来说,不要瞎改改~~

1,关于函数的说明两点

(1):先说一下函数,到底什么是函数:函数即变量,函数存在内存里面,函数名就像门牌号码(内存地址),寻找对应的函数(变量),这里变量test根据内存地址,找到函数test(),变量x根据内存地址找到变量3

(2):所以函数par_b跟par_b()是不同的概念,先看运行结果,可以看到par_b()返回的是函数的运行结果,而par_b返回的内存地址

def par_b(a):
    '''返回传入的参数值a '''
    return a

print(par_b(2)) # 表示执行函数
print(par_b) #表示是函数,返回的是函数的对象,即函数的内存地址
'''
运行返回值:
2   
<function par_b at 0x00000157BB3AC268>
'''

2,装饰器:装饰器本身就是函数,用函数去装饰另外一个函数,即为其他函数附加功能

装饰器不能违背的两个规则:

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

3,装饰器=高阶函数+嵌套函数

  3.1:高阶函数:满足下面其一均成为高阶函数

(1)把一个函数名作为实参传给另外一个函数,即把函数当成另外一个函数的参数

(2)返回值中包含函数名,即:一个函数的返回值为另外一个函数(若返回值为该函数本身,则为递归)

第一种情况:

原来一个支付功能函数,现在领导说要统计支付函数花费的时间,这时候不好修改原函数功能的,因为你不知道有多少业务在使用这个函数~~

import time
def order():
    '''购买商品'''
    time.sleep(1)
    print('购买函数')

def order_count(func):
    '''购买次数'''
    order_start = time.time()
    func()
    order_stop = time.time()
    print('购买花费的时间为:',(order_stop-order_start))

order_count(order)
'''
运行结果:
购买函数
购买花费的时间为: 1.0010454654693604
'''

备注:以上使用了高阶函数的条件一:函数作为参数,但这不是装饰器,显然我们违背了装饰器规则之一:不能改变被修饰函数的调用方式

第二种情况:

import time
def order():
    '''购买商品'''
    time.sleep(1)
    print('购买函数')

def order_count(func):
    '''购买次数'''
    order_start = time.time()
    func()
    order_stop = time.time()
    print('购买花费的时间为:',(order_stop-order_start))
    return func

order=order_count(order)
order()  #但其实order函数运行了两次
'''
运行结果:
购买函数
购买花费的时间为: 1.0009815692901611
购买函数
'''

备注:以上添加了高阶函数条件二:返回值中包含函数名,此功能帮我实现了不修改原调用方式的规则

  3.2:一看装饰器:所以结合高阶函数的定义,我们来看装饰器:

(1) 把一个函数名当做实参传给另一个函数(不修改被装饰函数的情况下不修改源代码 )

(2) 返回值中包含函数名(不修改函数的调用方式  )

  3.3:嵌套函数: 函数套函数,外套里面再穿一个外套,为啥这样穿,冷啊

x = ''
def grandba():
    x = '爷爷'
    print('第一层返回值:',x)
    def dad():
        x = '爸爸'
        print('第二层返回值:', x)
        def son():
            x = '儿子'
            print('第三层返回值:', x)
        son()
    dad()
grandba()
'''
执行结果:
第一层返回值: 爷爷
第二层返回值: 爸爸
第三层返回值: 儿子
'''

备注:说一下上面的执行步骤,第一步:读到def grandba():时,首先会把该函数存储到内存中,不会执行该函数内部逻辑,第二步:读到grandba(),才会根据函数名到内存中找到该函数,然后执行,此时才会执行该函数内部的逻辑代码,后面一样的道理

  3.4:再看装饰器:在被修饰函数的前面,用@符号调用

4,带参数装饰器

题外话:

之前写的时候函数w2忘记写return w2,然后报错:TypeError: 'NoneType' object is not callable,研究一下原因:

以上运行结果为:

<function order_count.<locals>.w2 at 0x0000029B13E80840>
购买的商品为: apple iphone
购买花费的时间为: 4.394911289215088

运行时打印了w2,输出的内存地址 程序运行到order("apple iphone")内存地址一模一样,流泪啊,恍然大悟了吧,其实到这一步,有一种移花接木的感觉,体会到那句话了,在不该改变原有的调用方式的基础上,给原函数添加附加功能,到这个一步就是:你就我,我就是你啊~~~

另外看到说的很好的:https://www.cnblogs.com/wupeiqi/articles/4980620.html

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章