函数的装饰器

闭包函数,
闭包函数 = 函数嵌套定义+ 函数对象+ 名称空间与作用域
闭: 指的是该函数时定义在一个函数内部的函数
包; 指的是该函数访问了一个来之于外层函数的变量

例:

def func():
    x = 3
def func():
    print(x)

 


这就是闭包函数, 把x 封闭到局部空间中

为函数体传参:
1. 直接使用参数的形式

def wrapper(x):
    print(x)
    wrapper(2)

 

2. 把函数体想要的参数包给它

def outter(x):
    # x = 111
    def wrapper():
        print(x)
        return wrapper # 把函数wrapper返回到全局作用域中

 


调用时可以在全局调用
闭包函数实现了变量定义在函数内代码只能内部访问, 二外部访问不到


装饰器
什么是装饰器
装饰器就是装饰函数的一种功能, 对原函数进行装饰
装饰器的定义, 在不改变源代码情况下, 为函数添加新功能,
函数体和调用方式都不能改变
开放封闭原则

1. 被装饰函数index

def index(x,y):
    print(x,y)
index(
1,2)

 

2. 为index添加新功能 '运行时间计时'

import time
def index(x,y):
    start = time.time()
    time.sleep(1)
    ends = time.time()
    print(ends-start)
    index(1,2)

 


改变了源代码, 不可行

3. 
def index(x,y):
    print(x,y)

    start = time.time()
index(1,2)
ends = time.time()
print(ends-start)

 

没改变源代码,也没改变调用方式
但执行起来麻烦, 如果有多处需要修改, 那么就很麻烦了

4. 定义一个装饰函数, 把函数放到装饰器内
原:

def index(x,y):
  print(x,y)
def wrapper():
  start = time.time()
index(
1,2) ends = time.time() print(ends-start) wrapper()

 


功能实现了, 但调用方式也跟着改变了, 不可行

5. 把wrapper函数内的index修改为参数形式

def index(x,y):
  print(x,y)

def wrapper(func):
  start = time.time()
func(
1,2) ends = time.time() print(ends-start) wrapper(index)

 


直接为wrapper函数传参的方式不行, 因为index是在全局, 而现在把index放到
局部中, 调用时还得先调用wrapper, 显然不符合, 不可行

6. 基于闭包函数吧wrapper函数想要传参包给index, 然后基于函数对象
把值也包给index,

def index(x,y):
    print(x,y)

def wrapper(func):
  def inner():
    start = time.time()
    func(1,2)
  ends = time.time()
  print(ends-start)
  return inner
index
= outter(index) index()

 


这样大致的模仿传来传参方式了, 但如果我想把函数内的参数改变呢
还是不行, 继续改,

7. 这里使用到* 和 ** 的知识点了, 把func传参位置换成*和**

def index(x,y):
  print(x,y)
def outter(func):
  def wrapper(*args,**kwargs):
    start = time.time()
    func(*args,**kwargs)
    ends = time.time()
    print(ends -start)
  return wrapper
index(1,2)

 


完美保证了不该源代码和调用方式,而且还可以装饰任意函数

这样就完善了, 怎么传参都行, 但是有没有想到, 如果函数有返回值呢,
作何处理, 下面处理

8. 在前方案改进, 把wrapper函数的返回值与被装饰函数报持一致

def index(x,y):
  print(x,y)
  return 123

def wrapper(func):
  def inner(*args,**kwargs):
    start = time.time()
    res = func(*args,**kwargs)
    ends = time.time()
    print(ends-start)
    return res
  return inner
ret = index(1,2)
print(ret) 

 


这样既可不改源代码, 又没有改变调用方式,
基本装饰器就这么实现了

标准装饰器如下

def wrapper(func):
  def inner(*args,**kwargs):
  """功能代码"""
    res = func(*args,**kwargs)
  """功能代码"""
  return res
return inner


语糖的使用
@装饰器

@wrapper
def func():
  print('only')

 

了解:
文档注释,
把原函数的注释和文档一并拷贝到被装饰的函数中
用到functools 模块中的wraps
使用方法
在装饰器内接受函数的变量名头顶上加@wraps
例:

def wrapper(func):
  @wraps # 完美
  def inner(*args,**kwargs):
    start = time.time()
    res = func(*args,**kwargs)
    ends = time.time()
    print(ends-start)
  return res
return inner

 


叠加多个装饰器
例:

def deco1(func1):
  def inner():
    print(func1):
  return inner
  
def deco2(func2):
  def inner():
    print(func2)
  return inner

@deco1
@deco2
def index(x):
print(x)

 

多个装饰器叠加的方式, 直接在函数头顶加
简单概述
当程序运行遇到语法糖@ 是, 语法糖函数会马上把@下的函数名
返回给 '@函数' 然后运行监测 '@函数' ;但不会马上运行函数体代码
结论
加载顺序从下往上
执行顺序自上往下内层的函数


2、叠加多个装饰器
2.1 加载顺序:自下而上
2.2 执行顺序:自上而下运行内层的wrapper函数

def deco1(func1): # func1 = wrapper2的内存地址
  def wrapper1(*args,**kwargs):
    print('wrapper1====>')
    res1=func1(*args,**kwargs)
  return res1
return wrapper1

def deco2(func2): # func2 = wrapper3的内存地址
  def wrapper2(*args,**kwargs):
    print('wrapper2====>')
    res2=func2(*args,**kwargs)
  return res2
return wrapper2

def deco3(func3): # func3 = 最原始的那个被装饰函数的内存地址
  def wrapper3(*args,**kwargs):
    print('wrapper3====>')
    res3=func3(*args,**kwargs)
  return res3
return wrapper3

# index=wrapper1的内存地址
@deco1 # deco1(wrapper2的内存地址)=>wrapper1的内存地址
@deco2 # deco2(wrapper3的内存地址)=>wrapper2的内存地址
@deco3 # deco3(最原始的那个被装饰函数的内存地址)=>wrapper3的内存地址
def index(x,y):
print('index=>',x,y)

index(1,2)

结果
"""
wrapper1====>'
wrapper2====>
wrapper3====>
index=>1,2
"""

 



 

posted @ 2020-07-21 17:29  π陈少π  阅读(181)  评论(0编辑  收藏  举报