Python装饰器详细介绍


Posted in Python onMarch 25, 2022

装饰器

一、介绍

  • :代表函数的意思。装饰器本质就是是函数
  • 功能:装饰其他函数,就是为其他函数添加附加功能 
  • 被装饰函数感受不到装饰器的存在
  • 原则: 

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

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

  • 实现装饰器知识储备: 

    函数即是“变量”

    高阶函数

    嵌套函数

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

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

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

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)

函数即变量,像“x=1,y=x”,同样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>

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

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)

按照上面说的,如何实现不改变调用方式?直接“test1 = deco(test1)”和“test2 = deco(test2)”吗? 
别忘记了,第二种方式,高阶函数要加上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)()

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

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()

到此这篇关于Python装饰器详细讲解的文章就介绍到这了,更多相关Python装饰器内容请搜索三水点靠木以前的文章或继续浏览下面的相关文章希望大家以后多多支持三水点靠木!

Python 相关文章推荐
python使用sorted函数对列表进行排序的方法
Apr 04 Python
介绍Python中的__future__模块
Apr 27 Python
约瑟夫问题的Python和C++求解方法
Aug 20 Python
python正则表达式re之compile函数解析
Oct 25 Python
python特性语法之遍历、公共方法、引用
Aug 08 Python
python 自定义异常和异常捕捉的方法
Oct 18 Python
对pandas通过索引提取dataframe的行方法详解
Feb 01 Python
python读取Excel表格文件的方法
Sep 02 Python
Python读取分割压缩TXT文本文件实例
Feb 14 Python
Python爬虫爬取微信朋友圈
Aug 06 Python
Python爬取酷狗MP3音频的步骤
Feb 26 Python
Python绘画好看的星空图
Mar 17 Python
python中数组和列表的简单实例
Mar 25 #Python
Python if else条件语句形式详解
python中的getter与setter你了解吗
Mar 24 #Python
Python编程中内置的NotImplemented类型的用法
Mar 23 #Python
pandas进行数据输入和输出的方法详解
Mar 23 #Python
基于Python编写简易版的天天跑酷游戏的示例代码
Python中的嵌套循环详情
Mar 23 #Python
You might like
PHP删除数组中空值的方法介绍
2014/04/14 PHP
详解PHP 二维数组排序保持键名不变
2019/03/06 PHP
PHP大文件分块上传功能实例详解
2019/07/22 PHP
可以支持多中格式的JS键盘
2007/05/02 Javascript
让回调函数 showResponse 也带上参数的代码
2007/08/13 Javascript
jquery判断checkbox(复选框)是否被选中的代码
2010/10/20 Javascript
Highcharts 非常实用的Javascript统计图demo示例
2013/07/03 Javascript
jQuery对下拉框,单选框,多选框的操作
2014/02/21 Javascript
js中函数调用的两种常用方法使用介绍
2014/07/17 Javascript
使用jQuery不判断浏览器高度解决iframe自适应高度问题
2014/12/16 Javascript
JS实现兼容性好,带缓冲的动感网页右键菜单效果
2015/09/18 Javascript
jQuery+Ajax实现限制查询间隔的方法
2016/06/07 Javascript
ThinkJS中如何使用MongoDB的CURD操作
2016/12/13 Javascript
angularjs中$http异步上传Excel文件方法
2018/02/23 Javascript
微信小程序下拉框组件使用方法详解
2018/12/28 Javascript
js核心基础之构造函数constructor用法实例分析
2019/05/11 Javascript
Python迭代和迭代器详解
2016/11/10 Python
Python基础练习之用户登录实现代码分享
2017/11/08 Python
Python使用matplotlib的pie函数绘制饼状图功能示例
2018/01/08 Python
详解pytorch 0.4.0迁移指南
2019/06/16 Python
django中账号密码验证登陆功能的实现方法
2019/07/15 Python
Python交互式图形编程的实现
2019/07/25 Python
Python TCP通信客户端服务端代码实例
2019/11/21 Python
django 模型中的计算字段实例
2020/05/19 Python
Travelstart沙特阿拉伯:廉价航班、豪华酒店和实惠的汽车租赁优惠
2019/04/06 全球购物
NHL官方在线商店:Shop.NHL.com
2020/05/01 全球购物
什么是serialVersionUID
2016/03/04 面试题
linux面试题参考答案(10)
2016/10/26 面试题
应届大学生自荐信格式
2013/09/21 职场文书
大学生毕业的自我评价分享
2014/01/02 职场文书
银行求职自荐信范文
2015/03/04 职场文书
2015年国庆节活动总结
2015/03/23 职场文书
2015年幼儿园中班开学寄语
2015/05/27 职场文书
2015年中秋节主持词
2015/07/30 职场文书
pycharm安装深度学习pytorch的d2l包失败问题解决
2022/03/25 Python
Windows7下FTP搭建图文教程
2022/08/05 Servers