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) 收藏 举报
浙公网安备 33010602011771号