专栏首页python3python 装饰器案例解析

python 装饰器案例解析

本文介绍几个装饰器案例,来分析装饰器是如何调用的

获取函数运行时间的例子

写装饰器,不可以一步到位,要慢慢一点一点的来

先写好2个函数

import time
def test1():
    time.sleep(1)
    print('in the test1')

def test2():
    time.sleep(2)
    print('in the test2')

test1()
test2()

执行输出

in the test1

in the test2

在不修改源代码的情况下,新增一个功能呢?

这个时候,需要用到 高阶函数

import time

def deco(func):
    start_time = time.time()
    func()
    stop_time = time.time()
    print('the func run time is %s' % (stop_time - start_time))

def test1():
    time.sleep(1)
    print('in the test1')

def test2():
    time.sleep(2)
    print('in the test2')

deco(test1)
deco(test2)

执行输出

in the test1

the func run time is 1.0009195804595947

in the test2

the func run time is 2.0000431537628174

注意:

执行的时候,不能写deco(test1()),为什么呢?这样写,是把test1函数执行的结果,传给deco了。

deco函数,不需要执行结果,它需要一个函数即可。

Pycharm编辑器写代码的时候,有自动补全功能,切记这里,要把括号删掉才行。

上面实现了显示执行时间的功能,下面考虑一个问题,如何在此基础上,直接执行

test1()和test2() 就可以实现上面的功能

需要用到变量的引用

def deco(func):
    start_time = time.time()
    func()
    stop_time = time.time()
    print('the func run time is %s' % (stop_time - start_time))

def test1():
    time.sleep(1)
    print('in the test1')

def test2():
    time.sleep(2)
    print('in the test2')

test1 = deco(test1)
test2 = deco(test2)
test1()
test2()

执行报错

TypeError: 'NoneType' object is not callable

因为deco没有return,无法得到函数内存地址

再改一下

import time
def deco(func):
    start_time = time.time()
    #返回函数内存地址
    return func
    stop_time = time.time()
    print('the func run time is %s' % (stop_time - start_time))

def test1():
    time.sleep(1)
    print('in the test1')

def test2():
    time.sleep(2)
    print('in the test2')

test1 = deco(test1)
test2 = deco(test2)
test1()
test2()

执行输出

in the test1

in the test2

从结果可以看出,函数的调用方式没有变,执行结果也没有变。搞了半天,啥都没干。

因为deco中直接returun了,所以打印时间没有输出。

上面的应用都是高阶函数,还缺嵌套函数,就成了

下面写一个嵌套函数。

def timer():
    def deco():
        pass

能不能把嵌套函数的形式融入到deco函数中呢?

把deco函数的代码直接拷贝进来,最后return deco

一个函数只有一个return,把中间的return修改为func()

将func参数移动到函数最上层

def timer(func):
    def deco():
        start_time = time.time()
        func()
        stop_time = time.time()
        print('the func run time is %s' % (stop_time - start_time))
    return deco

完整代码如下:

import time

def timer(func): #time(test1) func=test1
    def deco():
        start_time = time.time()
        func() #run test1()
        stop_time = time.time()
        print('the func run time is %s' % (stop_time - start_time))
    return deco

def test1():
    time.sleep(1)
    print('in the test1')

def test2():
    time.sleep(2)
    print('in the test2')

print(timer(test1))

执行输出

function timer.

结果返回deco函数的内存地址

有了函数的内存地址,把它赋值给一个变量,执行变量就可以了

print(timer(test1))改成

test1 = timer(test1)
test1()

执行输出

in the test1

the func run time is 1.0003840923309326

这里,效果就实现了,源代码和执行方式都没有改变。

注意: 最后的test1()行函数,已经不是原来的函数了,而是被装饰过的函数。

执行函数,需要2个步骤,太麻烦了,这不是最终效果

python 提供一个语法,用来执行装饰器函数,语法

@函数名
被装饰的函数名

这一句,需要加在被装饰函数的上一行

我删除了test2(),最终完整代码如下:

#!/usr/bin/env python
# coding: utf-8
__author__ = 'www.py3study.com'

import time

def timer(func): #timer(test) func=test1
    def deco():
        start_time = time.time()
        func() #run test1()
        stop_time = time.time()
        print('the func run time is %s' % (stop_time - start_time))
    return deco

@timer #test1=timer(test1)
def test1():
    time.sleep(1)
    print('in the test1')
    
test1()

执行输出

in the test1

the func run time is 1.0006184577941895

test1()函数上面的@timer

就相当于

test1 = timer(test1)

注意:上面写的装饰器,还不够完美

举个例子,再加一个函数,去装饰它

#!/usr/bin/env python
# coding: utf-8
__author__ = 'www.py3study.com'

import time

def timer(func):
    def deco():
        start_time = time.time()
        func()
        stop_time = time.time()
        print('the func run time is %s' % (stop_time - start_time))
    return deco

@timer
def test1():
    time.sleep(1)
    print('in the test1')

@timer
def test2(name):
    print("test",name)

test1()
test2("zhang")

执行报错

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

为什么呢?

test2上面的@timer就相当于

test2 = timer(test2)

timer(test2) -> 调用deco() -> 调用func() ->调用原test2(name)

注意,调用原test2函数的时候,需要传一个参数才行,而func()调用它的时候,没法传参数,所以程序报错。

为了解决传参的问题,需要调整一下deco函数,要求能接受参数。由于被装饰的函数,千奇百怪,有参数,没参数的都存在。使用*args,**kwargs就可以表示任意的参数。

最后终极代码如下:

#!/usr/bin/env python
# coding: utf-8
__author__ = 'www.py3study.com'

import time

def timer(func):
    def deco(*args,**kwargs):
        start_time = time.time()
        func(*args,**kwargs)
        stop_time = time.time()
        print('the func run time is %s' % (stop_time - start_time))
    return deco

@timer
def test1():
    time.sleep(1)
    print('in the test1')

@timer
def test2(name):
    time.sleep(2)
    print("test",name)

test1()
test2("zhang")

执行输出

in the test1

the func run time is 1.000878095626831

test zhang

the func run time is 2.0003161430358887

网页登陆

代码如下:

#!/usr/bin/env python
# coding: utf-8
__author__ = 'www.py3study.com'

import time

user,passwd = 'zhang','abc123'
def auth(func):
    def wrapper(*args,**kwargs):
        username = input("username:").strip()
        password = input("password:").strip()

        if user == username and passwd == password:
            print("\033[32;1mUser has passed authentication\033[0m")
            func(*args,**kwargs)
        else:
            exit("\033[31;1mInvalid username or password\033[0m")
    return  wrapper

def index():
    print("welcome to index page")

@auth
def home():
    print("welcome to home page")
@auth
def bbs():
    print("welcome to bbs page")

index()
home()
bbs()

执行输出

输出了2次用户名和密码

为什么呢?因为home和bbs页面,需要登录才能访问。

下面加一个验证方式

#!/usr/bin/env python
# coding: utf-8
__author__ = 'www.py3study.com'

import time

user,passwd = 'zhang','abc123'
def auth(auth_type):
    print("auth func:",auth_type)
    def outer_wrapper(func):
        def wrapper(*args, **kwargs):
            print("wrapper func args:",*args, **kwargs)
            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)
                print("---after authentication")
                return res
            else:
                exit("\033[31;1mInvalid username or password\033[0m")

        return wrapper
    return outer_wrapper


def index():
    print("welcome to index page")

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

index()
home()
bbs()

执行输出

现在还没有判断是哪种方式

完整代码如下:

#!/usr/bin/env python
# coding: utf-8
__author__ = 'www.py3study.com'

import time

user,passwd = 'zhang','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)
                    print("---after authentication")
                    return res
                else:
                    exit("\033[31;1mInvalid username or password\033[0m")
            elif auth_type == "ldap":
                print("This is LDAP,I can't do it")
                pass

        return wrapper
    return outer_wrapper


def index():
    print("welcome to index page")

@auth(auth_type="local")
def home():
    print("from home page")
@auth(auth_type="ldap")
def bbs():
    print("from bbs page")

index()
home()
bbs()

执行输出

执行bbs,采用的是ldap方式,我就直接输出了一段话,判断逻辑不会写啊!

本文参与腾讯云自媒体分享计划,欢迎正在阅读的你也加入,一起分享。

我来说两句

0 条评论
登录 后参与评论

相关文章

  • Python-延时操作

    在日常的开发中,往往会遇到这样的需求,需要某一个函数在一段时间之后才执行以达到某种特定的效果。此时,我们就需要某种机制,使一个函数延后执行。接下来简单介绍一下两...

    py3study
  • python time模块的使用

    我们先导入必须用到的一个module >>> import time 设置一个时间的格式,下面会用到 >>>ISOTIMEFORMAT=’%Y-%m-%d %X...

    py3study
  • python 格式化日期

    (1)time.asctime(time.localtime(time.time()))

    py3study
  • Python-延时操作

    在日常的开发中,往往会遇到这样的需求,需要某一个函数在一段时间之后才执行以达到某种特定的效果。此时,我们就需要某种机制,使一个函数延后执行。接下来简单介绍一下两...

    py3study
  • python 格式化日期

    (1)time.asctime(time.localtime(time.time()))

    py3study
  • curl你花了多长时间?

    curl -w "@curl-format.txt" -o /dev/null -s "http://www.baidu.com/"

    随心助手
  • Python日期字符串比较 转

    需要用python的脚本来快速检测一个文件内的二个时间日期字符串的大小,其实实现很简单,首先一些基础的日期格式化知识如下 复制代码

    双面人
  • Python time模块详解(时间戳↔元组形式↔格式化形式三者转化)计算时间差

    在Python中, time有三种表示形式 1 时间戳:1970年1月1日之后的秒 2 元组格式化形式 包含了:年、日、星期 得到time.stru...

    学到老
  • python 常用资源

    mojocn
  • 使用 cURL 对Web请求进行性能测试

    在做 Web 开发的时候,经常需要对 Web Page 或者 REST-ful API 做简单的 Benchmark。本文将介绍如何使用 cURL 进行简单快速...

    Debian社区

扫码关注云+社区

领取腾讯云代金券