day1-2装饰器前奏2

 

装饰器前奏2

实现装饰器需要的只是储备:

  1、函数即“变量”

  2、高阶函数

    a、把一个函数名当做实参传给另一个函数

    b、返回值中包含函数名

 

  3、嵌套函数

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

我们来看下下面几种函数中哪些是正确的哪些是错误的。本质就是将bar()函数的位置改变,看下执行结果。

# 第一种  会提示错误,因为没有定义bar()函数
def foo():
    print("in the foo")
    bar()

foo()

# 第二种     不会出错。
def bar():
    print("in the bar")
def foo():
    print("in the foo")
    bar()

foo()

# 第三种       不会出错。
def foo():
    print("in the foo")
    bar()
def bar():
    print("in the bar")
foo()

# 第四种      会出错,提示bar()没有定义,因为bar()在函数执行后面才定义
def foo():
    print("in the foo")
    bar()

foo()

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

我们先复习一下变量的内容。然后再来解释上面为什么会出错。

函数即变量的解释过程:

 

 python解释器回收机制:

      当变量名通过del被删除后,内存内容会在定期查看内存引用后发现 “函数体”  没有被引用了才会被清空。函数体不会在del删除变量名后立即被清空。

匿名函数的回收机制:

了解了这个回收机制,我们就知道为什么出错。

 

 二、高阶函数

  

    a、把一个函数名当做实参传给另一个函数

       (好处:在不修改被装饰函数源代码的情况下为其添加功能)

    b、返回值中包含函数名

      (好处:不修改函数的调用方式。)

 

 

通过案例来细化上面两种方式高阶函数:

1、例:把一个函数名当做实参传给另一个函数

# 第一个标准定义高阶函数:把一个函数名当做实参传给另一个函数,可以实现不改变被装饰函数的源代码
import time
def bar():
    time.sleep(3)
    print("in the bar")
def test1(func):
    start_time = time.time()
    func()
    stop_time = time.time()
    print("the func run time is %s" %(stop_time-start_time))

test1(bar)

执行结果:

小结:

   上面案例实现不改变被装饰函数的源代码

 

 

2、例:返回值中包含函数名

# 按照第二种方式定义高阶函数:返回值中包含函数名
import time
def bar():
    time.sleep(3)
    print("in the bar")
def test2(func):
    print(func)
    return func
#将test3()定义一个变量,是为了后面好调用,t其实就是一个返回值func(内存地址)
# t=test2(bar)   #func=bar=bar的函数体(内存地址)
# t()
#为了实现不改变调用方式,将上面的t变为bar,覆盖bar的值.这样就实现了调用方式不改变。
bar = test2(bar)
bar()

执行结果:

小结:

  上面案例 没有修改函数的调用方式。

 

通过上面两个案例达到了装饰器的两个原则。  

装饰器原则:

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

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

下面紧接着学习嵌套函数。

 

三、嵌套函数

  理解:在一个函数中通过def定义另一个函数。

理解嵌套函数和函数的调用的区别:

  通过下面代码来区别:

#嵌套函数
def foo():
    print("in the foo")
    def bar():         #函数嵌套,在一个函数中通过def定义另一个函数。
        print("in the bar")
    bar()   #bar函数只能在这里调用,bar是局部变量,只在foo函数中生效。
foo()

# 函数调用
def test2():
    print("in the test2")
def test1():
    print("in the test1")
    test2()       #这里就是函数的调用,非函数的嵌套。
test1()

执行结果:

 

简单说下嵌套函数的作用域

# 局部作用域和全局作用域的访问顺序。  先从里面往外面找,先局部后全局
x = 0
def grandpa():
    x = 1
    def dad():
        x = 2
        def son():
            x = 3
            print("x的值>>>",x)
        son()
    dad()
grandpa()

执行结果:

 

  

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

 装饰器第一阶段:

import time
#通过嵌套函数+高阶函数生成一个装饰器。
def timmer(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    

@timmer    # 相当于下面的test1=timmer(test1)
def test1():
    time.sleep(3)
    print("in the test1")
@timmer      # 相当于下面的test2=timmer(test2)
def test2():
    time.sleep(3)
    print("in the test2")

# test1=timmer(test1)
test1()
# test2=timmer(test2)
test2()

执行结果:

 

装饰器第二阶段:

  当被装饰的函数中有参数时上面的代码就不行了。

import time
#通过嵌套函数+高阶函数生成一个装饰器。如果被修饰有多个变量。
#当test2()有多个变量时,装饰器如何写。
def timmer(func):           #func=test2
    def deco():
        start_time = time.time()
        func()
        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      # 相当于下面的test2=timmer(test2)
def test2(name,age):           #test2有多个变量时
    print("in the test2",name,age)

# test1=timmer(test1)
test1()
# test2=timmer(test2)
test2("zhang3",18)

 

上面的代码中将test2()加了两个变量,这时再使用上面的装饰器时就会出错,提示少了两个位置参数。

执行结果:

 

这时就要修改装饰器。装饰器修改如下:

 

import time
#通过嵌套函数+高阶函数生成一个装饰器。如果被修饰有多个变量。
#当test2()有多个变量时,装饰器如何写。
def timmer(func):           #func=test2
    #这是一个装饰器,作用:增加一个函数运行时间。
    def deco(*args,**kwargs):     #加两种参数组。
        start_time = time.time()
        func(*args,**kwargs)   #或者func(args,kwargs)
        stop_time = time.time()
        print("the func run time is %s" %(stop_time-start_time))
    return deco

@timmer    # @timmer叫做语法糖,相当于下面的test1=timmer(test1)
def test1():
    #这时test1()函数。
    time.sleep(3)
    print("in the test1")
@timmer      # @timmer叫做语法糖,相当于下面的test2=timmer(test2)
def test2(name,age):           #test2有多个变量时
    #这时test2函数。
    print("in the test2",name,age)

# test1=timmer(test1)
test1()
# test2=timmer(test2)
test2("zhang3",18)

执行结果:

 

装饰器第三阶段:

案例:

  公司网站有页面,一个页面一个函数,以前谁都可以登陆,现在加部分页面需要登陆才能查看页面。home页面登陆验证,bbs页面需要ldap验证,这里模拟一下ldap验证。

# 公司网站有页面,一个页面一个函数,以前谁都可以登陆,现在加部分页面需要登陆才能查看页面。
#home页面登陆验证,bbs页面需要ldap验证,这里模拟一下ldap验证。
user,passwd = "zhang3","abc123"
def auth(auth_type):      #auth_type = local或ldap
    #定义一个auth装饰器,作用登陆验证,本地验证和ldap验证。
    print("auth auth_type is ",auth_type)    #打印auth_type的值。
    def outside(func):     #func = home 或 bbs
        print("outside func is ",func)    #打印一下func的内存地址。
        def wrapper(*args,**kwargs):
            if auth_type == "local":
                #如果auth_type 为local则执行下面本地验证。
                username = input("Username>>>")
                password = input("Password>>>")
                if user ==username and passwd == password:
                    res = func(*args,**kwargs)
                    print("\033[32;1m%s\033[0m登陆成功"%username)
                    return res      #为了不改变被装饰函数的返回值,需要加上返回值。
                else:
                    print("\033[31;1m无效的用户名或密码\033[0m")
            elif auth_type == "ldap":
                #如果auth_type等于ldap则执行ldap验证。
                print("\033[30;1m ldap验证成功\033[0m")
                res = func(*args, **kwargs)
                return res
        return wrapper
    return outside

def index():
    #定义一个函数index,模拟主页。
    print("this is index page")

@auth(auth_type = "local")
def home():
    #定义一个Home函数,模拟home页面。
    print("this is home page")
    return "home"

@auth(auth_type = "ldap")
def bbs():
    #定义一个bbs函数,模拟bbs页面。
    print("this is bbs page")

index()
print(home())   #显示home返回值。看下返回值是否改变。
bbs()

执行结果:

 

posted on 2017-07-09 11:13  aptech2017  阅读(62)  评论(0)    收藏  举报

导航