Python装饰器详细介绍

作者:~泪小白~ 时间:2022-09-28 17:52:16 

装饰器

一、介绍

  • :代表函数的意思。装饰器本质就是是函数

  • 功能:装饰其他函数,就是为其他函数添加附加功能 

  • 被装饰函数感受不到装饰器的存在

  • 原则: 

    不能修改被装饰的函数的源代码(比如线上环境)

    不能修改被装饰的函数的调用方式 

  • 实现装饰器知识储备: 

    函数即是“变量”

    高阶函数

    嵌套函数

高阶函数+嵌套函数=>装饰器

二、通过高阶函数+嵌套函数==>实现装饰器

先分析以下两段代码能不能运行?

def foo():
   print("in the foo")
   bar()
def bar():
   print("in the bar")

foo()
def foo():
   print("in the foo")
   bar()
foo()
def bar():
   print("in the bar")

第二段代码报错:

NameError: name 'bar' is not defined

1、变量知识回顾

定义变量: 
如:定义变量:x=1,会在内存中找块内存空间把“1”存进去,把“1”的内存地址给x  

前面提到:函数即变量

# 定义函数
def test():
   pass
# 就相当于把函数体赋值给test变量
test = '函数体'  # 函数体就是一堆字符串而已
# 只不过函数调用要加上小括号调用
test()

python内存回收机制,是解释器做的。解释器到底怎么去回收这个变量? 
python解释器当中有种概念叫做引用计数。什么叫引用计数呢? 
比如:定义x=1,之后又定义了y=1或y=x,实际上又把内存空间“1”的内存地址赋值给y 
这里x代表一次引用,y代表一次引用。加起来两次引用。 
python什么时候会把“1”这个内存空间清空呢?会回收内存呢? 
当x这个变量没有了,y这个变量也没有了,便会把“1”这个内存空间清掉

del x  # 删的只是变量名,内存中的值是解释器回收

匿名函数

lambda x:x*x

匿名函数没有函数名,没有引用,所以会被垃圾回收机制立马回收掉。 
所以匿名函数要赋值给变量,把函数体赋值给变量名

calc = lambda x:x*x
print(calc(4))

现在可以再理解下最开始两段代码能不能运行的原因。 

2、高阶函数(装饰器前奏)

什么叫高阶函数呢:

  • 把一个函数名当做形实传给另外一个函数

  • 返回值中包含函数名

def f1():
   print("in the func1")
def test1(func):
   print(func)
test1(f1)

运行结果(打印内存地址)

<function func1 at 0x000002805DE12378>

如下代码,能不能运行:

def f1():
   print("in the func1")
def test1(func):
   print(func)
   func()
test1(f1)

函数即变量,像&ldquo;x=1,y=x&rdquo;,同样f是一个是一个函数,可不可以像一个变量一样来回赋值呢?

import time
def func1():
   print("in the func1")
   time.sleep(1)
def test1(func):
   start_time = time.time()
   func()
   stop_time = time.time()
   print("the func run time is %s" %(stop_time-start_time))
test1(func1)

到这里,貌似实现了装饰函数的功能。 
看上面装饰器的原则: 
这里:没有修改func1的源代码,但是调用方式改变了。现在是test1(func1),之前是func1() 
现在能做到哪一点呢? 
把一个函数名当做实参传给另外一个函数(不修改被装饰的函数源代码的情况下为其添加功能)

2) 下面用第二个条件(返回值中包含函数名),做另外一个高阶函数

import time
def func2():
   time.sleep(1)
   print("in the func2")
def test2(func):
   print(func)
   return(func)
print(test2(func2))

运行结果:

<function func2 at 0x00000162F3672378>
<function func2 at 0x00000162F3672378>

把函数内存地址都打印出来了,看到这么多内存地址,有什么想法? 
加上小括号就能运行。 
上面代码&ldquo;test2(func2())&rdquo;和&ldquo;test2(func2)&rdquo;有什么区别?加上小括号是函数返回结果,不加是函数内存地址。所以加上小括号就不符合高阶函数定义了。 
既然以后有了函数的内存地址,是不是可以赋值给其他变量?下面

import time
def func2():
   print("in the func2")
   time.sleep(1)
def test2(func):
   print(func)
   return(func)
t = test2(func2)
print(t)
t()

好像还没什么用,怎么让他有用呢? 
把test2(func2)赋值给func2

import time
def func2():
   print("in the func2")
   time.sleep(1)
def test2(func):
   print(func)
   return(func)
func2 = (test2(func2))
func2()

这就是高阶函数的第二个好处:返回值中包含函数名(不修改函数的调用方式)

3、嵌套函数(装饰器前戏)

嵌套函数:在一个函数体内,用def去声明一个函数

def foo():
   print("in the foo")
   def bar():
       print("in the bar")
   bar()
foo()

看一下下面的代码是不是嵌套:

def foo():
   print("in the foo")
def bar():
   foo()
bar()

注意函数嵌套和函数调用区别  

局部作用域和全局作用域的访问顺序:

x = 0
def grandpa():
   # x = 1
   def dad():
       x = 2
       def son():
           x = 3
           print(x)
       son()
   dad()
grandpa()

三、装饰器

1、装饰器

前面铺垫了那么多,现在开讲正题:装饰器 
先用高阶函数实现给函数不修改源代码的情况下添加功能

import time
def deco(func):
   start_time = time.time()
   func()
   stop_time = time.time()
   print("the func tun time is %s" %(stop_time-start_time))
def test1():
   time.sleep(1)
   print("in the test1")
def test2():
   time.sleep(1)
   print("in the test2")
deco(test1)
deco(test2)

按照上面说的,如何实现不改变调用方式?直接&ldquo;test1 = deco(test1)&rdquo;和&ldquo;test2 = deco(test2)&rdquo;吗? 
别忘记了,第二种方式,高阶函数要加上return,如下

import time
def deco(func):
   start_time = time.time()
   return func()
   stop_time = time.time()
   print("the func tun time is %s" %(stop_time-start_time))
def test1():
   time.sleep(1)
   print("in the test1")
def test2():
   time.sleep(1)
   print("in the test2")
test1 = deco(test1)
test2 = deco(test2)
deco(test1)
deco(test2)

虽然没有修改源代码和调用方式,但是函数加上return,函数就结束了,然并卵。怎么实现呢? 
前面一直在用高阶函数,还没有用嵌套函数,加上嵌套函数能不能实现呢?看一下

import time
def timer(func):  # timer(test1)  func=test1
   def deco():
       start_time = time.time()
       func()
       stop_time = time.time()
       print("the func tun time is %s" %(stop_time-start_time))
   return deco  # 返回deco的内存地址
def test1():
   time.sleep(1)
   print("in the test1")
def test2():
   time.sleep(1)
   print("in the test2")
print(timer(test1))  # 可见:返回deco的内存地址
test1 = timer(test1)
test1()
timer(test2)()

到此,完成实现了装饰器的功能。但是还是有点麻烦,如何能不要&ldquo;test1 = timer(test1)&rdquo;, 
python解释器提供了语法糖&ldquo;@&rdquo;符合,给哪个函数新增功能,就加在哪个函数头部

import time
def timer(func):  # timer(test1)  func=test1
   def deco():
       start_time = time.time()
       func()
       stop_time = time.time()
       print("the func tun time is %s" %(stop_time-start_time))
   return deco  # 返回deco的内存地址
@timer
def test1():
   time.sleep(1)
   print("in the test1")
@timer
def test2():
   time.sleep(1)
   print("in the test2")

test1()
test2()

2、有参装饰器

前面实现了装饰器的功能,但是如果函数有参数,能不能也能运行呢

import time
def timer(func):  # timer(test1)  func=test1
   def deco():
       start_time = time.time()
       func()
       stop_time = time.time()
       print("the func tun time is %s" %(stop_time-start_time))
   return deco  # 返回deco的内存地址
@timer
def test1():
   time.sleep(1)
   print("in the test1")
@timer
def test2(name):
   time.sleep(1)
   print("in the test2",name)

test1()
test2()

报错:丢失参数

TypeError: test2() missing 1 required positional argument: 'name'

@timer 相当于 test2=timer(test2) =deco 
test2() 相当于运行deco(),所以没指定参数,报错。 
如何传参数呢?为了适应各种不同参数的函数

import time
def timer(func):  # timer(test1)  func=test1
   def deco(*args,**kwargs):
       start_time = time.time()
       func(*args,**kwargs)
       stop_time = time.time()
       print("the func tun time is %s" %(stop_time-start_time))
   return deco  # 返回deco的内存地址
@timer
def test1():
   time.sleep(1)
   print("in the test1")
@timer
def test2(name):
   time.sleep(1)
   print("in the test2",name)

test1()
test2("fgf")

3、终极装饰器

注意,上面的例子中还没有涉及返回值,看下面的例子可以体会一下 
假设:公司网站需要验证登录,有不同的验证方式:本地认证、LDAP认证等

#/usr/bin/env python
# -*- coding: UTF-8 -*-
import time
user,passwd = 'fgf','abc123'
def auth(auth_type):
   print("auth func:",auth_type)
   def outer_wrapper(func):
       def wrapper(*args, **kwargs):
           print("wrapper func args:", *args, **kwargs)
           if auth_type == "local":
               username = input("Username:").strip()
               password = input("Password:").strip()
               if user == username and passwd == password:
                   print("\033[32;1mUser has passed authentication\033[0m")
                   res = func(*args, **kwargs)  # from home
                   print("---after authenticaion ")
                   return res
               else:
                   exit("\033[31;1mInvalid username or password\033[0m")
           elif auth_type == "ldap":
               print("搞毛线ldap,不会。。。。")

return wrapper
   return outer_wrapper
def index():
   print("welcome to index page")
@auth(auth_type="local") # home = wrapper()
def home():
   print("welcome to home  page")
   return "from home"

@auth(auth_type="ldap")
def bbs():
   print("welcome to bbs  page")
index()
print(home()) #wrapper()
bbs()

来源:https://www.cnblogs.com/leixiaobai/articles/8031391.html

标签:Python,装饰器
0
投稿

猜你喜欢

  • python 图像判断,清晰度(明暗),彩色与黑白实例

    2023-09-04 12:58:00
  • Python自动化构建工具scons使用入门笔记

    2023-09-21 19:58:16
  • python飞机大战pygame碰撞检测实现方法分析

    2023-01-05 18:55:52
  • MySQL版本低了不支持两个时间戳类型的值解决方法

    2024-01-18 14:13:21
  • js和asp操作fso比较

    2007-09-23 09:17:00
  • 深入讲解Python函数中参数的使用及默认参数的陷阱

    2022-04-21 20:09:14
  • Python网络编程 Python套接字编程

    2022-06-09 09:41:32
  • Python中itertools库的四个函数介绍

    2021-05-04 14:34:05
  • MySQL中批量删除指定前缀表的sql语句

    2024-01-17 07:37:07
  • 用OpenCV将视频分解成单帧图片,图片合成视频示例

    2023-04-25 22:55:03
  • Python读取键盘输入的2种方法

    2023-03-14 22:08:37
  • XML简易教程之二

    2008-09-05 17:19:00
  • numpy np.newaxis 的实用分享

    2023-01-21 06:06:25
  • 优雅地使用loading(推荐)

    2024-04-30 08:42:01
  • Python实现简单的文件传输与MySQL备份的脚本分享

    2024-01-21 04:44:44
  • Python使用base64模块进行二进制数据编码详解

    2023-08-26 20:13:13
  • Python文本处理简单易懂方法解析

    2021-08-09 02:45:16
  • python正则匹配查询港澳通行证办理进度示例分享

    2021-05-09 19:29:55
  • JavaScript观察者模式原理与用法实例详解

    2024-04-19 10:02:48
  • python模块简介之有序字典(OrderedDict)

    2023-12-14 07:46:46
  • asp之家 网络编程 m.aspxhome.com