函数装饰器,namespace、闭包和函数嵌套

# ! /usr/bin/env python
# -*- coding:utf-8 -*-
# import time
# # 定义一个基本的函数,被装饰的函数,在保证函数的调用和源代码不变的情况下,为该函数添加功能,这就是装饰器,实际上要用的功能主要是函数的嵌套、闭包
# def index(x,y):
# time.sleep(2)
# print("index func--->%s %s"%(x,y))
#
# # index(1,3)
# # 增加的功能是统计该函数的运行时间
# start_time = time.time()
# index(1,3)
# stop_time =time.time()
# print("index 运行时间为:",stop_time - start_time)

# 通过上面的方式是可以实现该功能,但是假如调用的地方很多,就需要在每一个调用的地方进行修改,这样不利于代码的维护
# 方式一:直接修改函数的功能
#
# import time
# def index(x,y):
# start_time = time.time()
# time.sleep(2)
# print("index func--->%s %s" % (x, y))
# stop_time = time.time()
# print("index 运行时间为:", stop_time - start_time)
#
# index(1,3)

# 通过以上的方式,直接修改了index函数的功能,是能达到增加功能,且不修改原函数的调用方式,但是违反了函数闭包原则,修改了函数的源代码,不符合修改习惯,则需要向另外的方法。

# 方式二:将在其他函数调用的时候,执行index函数

# import time
# def index(x,y):
# time.sleep(2)
# print("index func--->%s %s" % (x, y))
#
#
# def timmer():
# start_time = time.time()
# index(1,3)
# stop_time = time.time()
# print("index 运行时间为:", stop_time - start_time)
#
# timmer()
# 新定义一个函数timmer,在函数的调用时候,直接调用index,这种方法也是能实现所需要的功能,但是修改了函数调用的方式,且写的有点死,后续无法做维护。

# 方式三:做闭包关系,将index方法定义到外层函数中,然后调用
# import time
# def timmer(x,y):
# def index(x,y):
# time.sleep(2)
# print("index func--->%s %s" % (x, y))
# start_time = time.time()
# index(x, y)
# stop_time = time.time()
# print("index 运行时间为:", stop_time - start_time)

# timmer(1,3)

# 上面的方法,将index函数封闭在timmer函数体内,也没有修改index的源代码,但是修改了index的调用方式,没有修改传参,方法感觉可行。如果index函数方法确实需要修改源代码,比如增加了参数,这个时候怎么弄?

# 方式三的优化:args和kwargs,设置形参
# import time
# def timmer(*args,**kwargs):
# def index(x,y):
# time.sleep(2)
# print("index func--->%s %s" % (x, y))
# start_time = time.time()
# index(*args,**kwargs)
# stop_time = time.time()
# print("index 运行时间为:", stop_time - start_time)
#
# timmer(1,3)
# 通过args和kwargs,可以将参数传递进index中,不论index函数后续增加多少形参,都可以满足条件。

# 解决调用方式的问题,使用闭包,将函数的内存地址,当成返回值返回,然后赋值到全局变量中
import time
# def timmer(*args,**kwargs):
# start_time = time.time()
# def index(x,y):
# time.sleep(2)
# print("index func--->%s %s" % (x, y))
# index(x,y)
# stop_time = time.time()
# print("index 运行时间为:", stop_time - start_time)
# return index
#
# index=timmer()
# index(1,3)
# 这种情况下,貌似没有改变index的调用方式,但是没有实现添加功能的方法,需要修改一下,改变一下逻辑
#
# import time
# def index(x, y):
# time.sleep(2)
# print("index func--->%s %s" % (x, y))
#
# def timmer(*args,**kwargs):
# def wrapper(*args,**kwargs):
# start_time = time.time()
# index(*args,**kwargs)
# stop_time = time.time()
# print("index 运行时间为:", stop_time - start_time)
# return wrapper
#
# aaa=timmer()
# aaa(1,3)
# 这种方式中,就实现了传参和功能,但是修改了函数的调用方式,且被装饰的函数被固定死,无法扩展,需要继续优化,将函数做为变量,传递进闭包函数中。


# import time
# def index(x, y):
# time.sleep(2)
# print("index func--->%s %s" % (x, y))
#
# def timmer(func,*args,**kwargs):
# def wrapper(*args,**kwargs):
# start_time = time.time()
# func(*args,**kwargs)
# stop_time = time.time()
# print("index 运行时间为:", stop_time - start_time)
# return wrapper
#
# aaa=timmer(index)
# aaa(1,3)

# 根据上面修改之后,任何函数,都可以添加这个timmer的功能,只要将函数名作为变量,传递进参数即可。这种情况下,还是没有解决函数调用时方法不变的问题。
# import time
# def index(x, y):
# time.sleep(2)
# print("index func--->%s %s" % (x, y))
#
# def timmer(func,*args,**kwargs):
# def wrapper(*args,**kwargs):
# start_time = time.time()
# func(*args,**kwargs)
# stop_time = time.time()
# print("index 运行时间为:", stop_time - start_time)
# return wrapper
#
# index=timmer(index)
# index(1,3)
# index(2,3)
# 这种修改下,可以直接将全局的index指向的内存地址,修改为闭包函数中的局部namespace里面的func,实际上也就是index,这里有一点需要理解的是,index是全局namespace中的变量名,而func是一个局部namespace中的变量,将全局变量名指向的内存地址,作为参数传递到局部中使用,然后再将局部指向的内存地址,重新副职给全局namespace中的变量名。
# 这就实现了在不修改index源代码和调用方式的情况下,为index方法增加了一个统计时间的功能。
# 这里就引出来装饰器,装饰器实际上实现的功能,就是一个将局部内存地址指向的值,重新赋值给全局名称空间的变量名,让调用的时候以为调用的还是全局名称空间的方法。上面的修改可以通过装饰器来实现,具体如下:
# import time
# def timmer(func,*args,**kwargs):
# def wrapper(*args,**kwargs):
# start_time = time.time()
# func(*args,**kwargs)
# stop_time = time.time()
# print("index 运行时间为:", stop_time - start_time)
# return wrapper
# @timmer
# def index(x, y):
# time.sleep(2)
# print("index func--->%s %s" % (x, y))
#
# # index=timmer(index)
# index(1,3)
# index(2,3)

# 其中@timmer就是使用timeer装饰index,装饰器的定义,一定是在被装饰的函数之前定义的,不然会出现语法错误。

# 这里的index比较简单,没有返回值的概念在里面,假如index有返回值,这个时候该如何处理呢?
import time
def timmer(func,*args,**kwargs):
def wrapper(*args,**kwargs):
start_time = time.time()
res = func(*args,**kwargs)
stop_time = time.time()
print("index 运行时间为:", stop_time - start_time)
return res
return wrapper
@timmer
def index(x, y):
time.sleep(2)
print("index func--->%s %s" % (x, y))
return x,y

# index=timmer(index)
result1 = index(1,3)
print(result1)

# 在内层函数中,还是保持被装饰函数的调用方法不变,被装饰的函数,有返回值,就赋值给一个变量,然后在内层函数执行完之后返回该值。这种情况就比较通用了,如果被装饰的函数,没有返回值,那自然就是None,如果有返回值,直接用变量就返回了。

# 综上,装饰器,就是使用了函数的嵌套、namespace和作用域、函数的闭包来实现的。
# 函数的闭包,就是说在保证内层函数方法和定义不变的情况下,因为内层函数需要使用一个全局namespace的变量名、或者是一个上一层namespace的变量名,这个时候就可以使用闭包的方式,将该函数嵌套在某一个函数的定义中,然后在外层将该变量名传入,在内层函数执行中进行引用,外层函数返回时,返回的是内层函数的内存地址,然后就可以直接加()进行调用。

装饰器@实际上就是将下方的函数,作为参数,传入到@后的函数中


posted @ 2020-10-08 21:42  波波波波波  阅读(168)  评论(0编辑  收藏  举报