【澳门新萄京app】装饰器高潮

作者: 澳门新萄京app  发布:2019-11-14

 注:在函数test1()的上面加上@timmer,作用相当于test1 = timmer(test1),又因为timmer函数中返回wrapper,所以有test1 = timmer(test1) = wrapper,因此最后一行代码在使用test()方式调用函数的时候,实际调用的是wrapper(),而wrapper中的变量func接收了timemr(test1)传进来的test1,所以实际调用时,先执行wrapper中附加的功能(计时),之后执行test1()本身的功能。

四、带参数的装饰器

之前我们的装饰器都是没有带参数的,其实我们已经能解决90%的问题了,但是如果说有一种情况:就是在你访问不通页面时,你用的验证的方式来源不同,这时你该怎么办?

#本地验证
user,passwd = "zhangqigao","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 auth_type == "local":
                if user == username and passwd == password:
                    print("33[32mUser has passed authentication33[0m")
                    res = func(*args,**kwargs)
                    print("--after authentication")
                    return res
                else:
                    exit("Invalid username or password")
            elif auth_type == "ldap":
                pass
        return wrapper
    return outer_wrapper

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

@auth(auth_type="local")  #带参数装饰器
def home():
    print("welcome to home page")
    return "from home"

@auth(auth_type="ldap")   #带参数装饰器
def bbs():
    print("welcome  to bbs page")

index()
home()
bbs()

 重上面的例子可以看出,执行步骤:

  1. outer_wrapper = auth(auth_type="local")
  2. home = outer_wrapper(home)
  3. home()

所以这个函数的作用分别是:

  1. auth(auth_type) 传递装饰器的参数
  2. outer_wrapper(func) 把函数当做实参传递进来
  3. wrapper(*args,**kwargs) 真正执行装饰的函数

b.返回值中包含函数名(不修改函数的调用方式)

三、执行函数有返回值

上面的例子,被调用的函数都没有返回值,那如果,我们被调函数有返回值,该如何做呢?

def timmer(func):  #timmer(test1) func=test1
    def deco(*args,**kwargs):
        res = func(*args,**kwargs) #这边传入函数结果赋给res
        return res   # 返回res
    return deco

@timmer
def test1():  # test1 =  timmer(test1)
    print("in the test1")
    return "from the test1" #执行函数test1有返回值

res = test1()
print(res)

#输出
in the test1
from the test1

 通过上面的例子,可以看出,其实就是在内置函数中把传入参数的执行结果赋给res,然后再返回res变量。

(3)嵌套函数

一、概述

我们之前介绍了大幅片的内容,感觉跟装饰器半毛钱关系都没有,其实不然,我们分别详细阐述了高阶函数和内置函数,下面我们就来讲讲什么是真正的装饰器。

(1)不能修改被装饰的函数的源代码

二、装饰器定义

首先装饰器实现的条件:高阶函数+嵌套函数 =》装饰器

1、定义

import time

#定义内置函数
def timmer(func):  #timmer(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

#装饰test1函数
@timmer  # 相当于test1 = timmer(test1)
def test1():
    time.sleep(3)
    print("in the test1")

#直接执行test1函数    
test1()

#输出
in the test1
the func run time is 3.0002999305725098

 执行步骤:

  1. 执行timmer函数,timmer(test1) 返回值赋值给test1变量,即test1=timmer(test1)
  2. 此时的test1的值是执行timmer函数返回值deco,即test1=deco
  3. 所以执行test1,其实就是执行的是deco函数,test1()其实就是执行deco函数。

2、执行函数带参数

我们先来试试,如果被装饰的函数需要传入参数怎么办?

import time

def timmer(func):  #timmer(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

@timmer
def test2(name,age):
    print("name:%s,age:%s"%(name,age))

test2()

#输出
Traceback (most recent call last):
  File "D:/PycharmProjects/pyhomework/day4/装饰器/装饰器高潮.py", line 23, in <module>
    test2()
  File "D:/PycharmProjects/pyhomework/day4/装饰器/装饰器高潮.py", line 8, in deco
    func()   #run test1()
TypeError: test2() missing 2 required positional arguments: 'name' and 'age' #缺少传入name和age参数

 很显然是错误的,错误的。因为这边执行的test2函数其实就是执行的deco函数,deco函数体内的func()其实就是执行test2函数,但是,test2需要传入name和age两个参数,所以报错。那怎么解决呢?我们只能传入参数了,但是你又不能确定传入几个参数,所以我们只能用非固定参数传参。代码如下:

import time

def timmer(func):  #timmer(test1) func=test1
    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

#不带参数
@timmer  # 相当于test1 = timmer(test1)
def test1():
    time.sleep(3)
    print("in the test1")

#带参数
@timmer
def test2(name,age):
    print("name:%s,age:%s"%(name,age))
#调用
test1()
test2("zhangqigao",22)

#输出
#test1
in the test1
the func run time is 3.0010883808135986 
#test2
name:zhangqigao,age:22
the func run time is 0.0  #test2

此时对于test1()函数定义上的@before_timmer(func_type = "type1"),我的理解是:test2 = before_timmer(func_type = “type2”),又因为before_timmer中有return timmer,所以test2 = before_timmer(func_type = “type2”) = timmer(test2),同时由于timmer中有return wrapper,所以 test2 = before_timmer(func_type = “type2”) = timmer(test2) = wrapper,因此,当调用test2(“suxy”)时,还是最后还是相当于调用了wrapper(“suxy”)。

C:appAnaconda3python.exe E:/python_workspace/pure_python/day4/decorator.py
in the test1
the func run time is 1.000464916229248

in the test2,  suxy
the func run time is 1.0004689693450928

Process finished with exit code 0
# -*- coding:utf-8 -*-
#Author:Suxy

import time

def timmer(func):
    def wrapper():
        start_time = time.time()
        func()
        stop_time = time.time()
        print("the func run time is %s" %(stop_time - start_time))
    return  wrapper

@timmer  #test1 = timmer(test1),即test1 = wrapper
def test1():
    time.sleep(1)
    print("in the test1")

@timmer
def test2(name):
    time.sleep(1)
    print("in the test2, ", name)

test1()
test2("suxy")

澳门新萄京app,1.被装饰函数没有参数

 可见,函数test2()中明明返回了9527,但是最后输出确是None,违反了装饰器不能修改被装饰函数的返回结果的原则。这是因为装饰器的wrapper中调用func(*args, **kwargs)函数(注: func(*args, **kwargs) = test2(*args, **kwargs))时,只是进行直接的调用,而没有将调用的结果返回。可以将装饰器代码修改为 return func(*args, **kwargs),或者先用res = func(*args, **kwargs)将函数执行结果保存到res中,最后再返回res。如下:

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

 输出:

 

# -*- coding:utf-8 -*-
#Author:Suxy

import time

def timmer(func):
    def wrapper(*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  wrapper

@timmer  #test1 = timmer(test1),即test1 = wrapper
def test1():
    time.sleep(1)
    print("in the test1")

@timmer
def test2(name):
    time.sleep(1)
    print("nin the test2, ", name)

test1()
test2("suxy")
# -*- coding:utf-8 -*-
#Author:Suxy

import time

def timmer(func):
    def wrapper(*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  wrapper

@timmer
def test2(name):
    time.sleep(1)
    print("nin the test2, ", name)
    return 9527

print(test2("suxy"))

a.把一个函数名当做实参传给另一个函数(在不修改被装饰函数源代码的情况下为其添加功能)

 

假设被装饰的函数为test1(),此时的test1()没有形参,默认返回NULL;装饰器timmer完成的附加功能是计时。

 

# -*- coding:utf-8 -*-
#Author:Suxy

import time

def timmer(func):
    def wrapper():
        start_time = time.time()
        func()
        stop_time = time.time()
        print("the func run time is %s" %(stop_time - start_time))
    return  wrapper

@timmer  #test1 = timmer(test1),即test1 = wrapper
def test1():
    time.sleep(1)
    print("in the test1")

test1()

 输出:

 输出:

2.实现装饰器的知识储备:

关于函数即变量,我的理解如下所示,即当定义一个函数时,相当于在内存中划一块内存空间,里面存放函数的函数体,而函数名指向这块空间——这跟普通的函数定义如 a = 9527 是一样的。如此一来,函数名便可以跟普通变量一样,作为参数传给其他函数,也能在函数中作为结果被返回。

 仔细对比这里和3中装饰器代码的区别可以发现,这一步相当于又新建了一个装饰器before_timmer将原先的装饰器timmer包了起来,并将timmer函数作为结果返回,使得可以在before_timmer装饰器中先完成判断功能,再调用timmer计时功能。

装饰器在装饰函数时必须遵循3个重要的原则:

(1)函数即“变量”

装饰器不仅要能装饰无参函数,还得能装饰带参函数。这里以只带一个参数的函数test2(name)为例。此时若是不修改上面装饰器的任何代码便用来装饰test2(name),则会报错,具体如下:

 2.被装饰函数带有参数

由第1步可知,在使用装饰器timmer装饰函数test2之后,调用test2(“suxy”)就相当于调用wrapper(“suxy”)函数,而当前的wrapper()函数是不接收参数的。

 3.上面第2步所说的装饰器已经能够装饰一般的带参函数了,不过,若是被装饰函数具有返回值,则上面的装饰器就有问题了。如下(为缩减篇幅,暂将函数test1省略掉):

本文由澳门新萄京app发布于澳门新萄京app,转载请注明出处:【澳门新萄京app】装饰器高潮

关键词:

上一篇:图像的模糊检测方法,图像边缘检测
下一篇:没有了