第一类对象first-class object——python 中的一等公民
1.在运行期创建
2.可用作函数参数或返回值
3.可存入变量的实体
4.函数返回值可以是其他函数
5.内部函数可以访问包含它的函数的局部变量
函数作为一等公民,都能干什么?
1.把函数赋值给变量
2.在某个函数内部定义另外一个函数
装饰器的本质
装饰器本质上是一个函数,是一个能够返回函数对象的高阶函数。
带有装饰器的函数的运行流程
def decorator(func):
def wrapper(*args, **kwargs):
print('函数执行前,要添加的功能')
ret = func(*args, **kwargs)
print('函数执行后,要添加的功能')
return ret
return wrapper
def g():
print('被装饰函数')
g = decorator(g)
g()
具体流程:
1.内存中创建了变量decorator,指向该函数的内存空间,同时创建变量g,指向自己的函数内存空间
2.执行g = decorator(g),在decorator指向的函数内存空间中添加变量wrapper,它也指向一个函数内存空间。然后将变量wrapper指向的内存地址赋值给变量g。此时g不再指向原先的函数g()的内存空间。
3.执行g(),此时g()代表的是返回的内层函数wrapper(*args,**kwargs)。解释器开始执行wrapper()函数中的代码,执行第一个print语句后。
4.执行 ret = func(*args, **kwargs),在wrapper内存空间中创建了变量func,指向函数g()。让后运行g()中代码,有返回值就赋值给ret,再执行下一个print语句,最后将ret作为返回值返回。
装饰器的应用
无参数的装饰器
登陆验证session
Django开发Web项目中,对于一些需要用户登录之后才能访问的页面,通常需要使用session来验证当前是否有用户登陆,如果没有用户登陆,就重定向到登陆页面。
在Flask中,由于路由系统也是使用装饰器来实现的,因此在为视图函数配置验证session的装饰器时需要注意两个装饰器的位置,需要将验证session的装饰器放在aap.route装饰器下面,这样才能保证在进入路由匹配之前验证session
有参数的装饰器
Flask的路由系统
Django的inclusion_tag
装饰器的作用
让其他函数在不需要在任何代码变动的前提下增加额外功能。 有了装饰器,可以抽离出大量与函数功能本身无关的雷同代码并继续重用
装饰器的设计模式
开放封闭原则 对扩展开放 任何程序,不可能在设计之初就想好了所有功能,并且未来不作任何更新和修改。必须允许代码扩展,添加新功能
对修改封闭 已经存在的函数如果再要修改,会影响到已经使用该函数功能的用户,而且修改可能会导致程序崩溃
装饰器的固定格式
def decorator(func_name):
def wrapper(*args, **kwargs):
print('执行函数前,要装饰的功能')
ret = func_name(*args, **kwargs)
print('执行函数后,要装饰的功能')
return ret
return wrapper
@decorator
def func():
print('要被装饰的函数')
@decorator <===> func = decorator(func)
有参数的装饰器
装饰器可以带参数,最多的用途就是作为一个装饰器的开关
F = True
# F = False
import time
def outer(flag):
def decorator(func):
def inner(*args, **kwargs):
if flag:
start = time.time()
time.sleep(2)
ret = func(*args, **kwargs)
print('函数运行时间====>', time.time()-start)
# return ret
else:
ret = func(*args, **kwargs)
# return ret #重复调用语句
return ret
return inner
return decorator
@outer(F)
def func1():
print('func1 . . . .run .....')
func1()
@outer(F)
def func2():
print('func2 . . . .run .....')
func2()
@outer(F)
def func3():
print('func3 . . . .run .....')
func3()
@outer(F)
def func4():
print('func4 . . . .run .....')
func4()
------------------------------------------------------------------------------------------------------------------------------
#运行结果
func1 . . . .run .....
函数运行时间====> 2.0003795623779297
func2 . . . .run .....
函数运行时间====> 2.0000627040863037
func3 . . . .run .....
函数运行时间====> 2.0010735988616943
func4 . . . .run .....
函数运行时间====> 2.0007920265197754
有参数装饰器的应用场景
Flask项目中,@app.route实现路由系统,就是使用的有参数的装饰器
多个装饰器装饰一个函数
多个装饰器装饰一个函数,要注意装饰的功能的调用顺序
def decorator_one(func):
def inner(*args, **kwargs):
print('decorator111111111.....decorating...before.. func')
ret = func(*args, **kwargs)
print('decorator111111111.....decorating...after.. func')
return ret
return inner
def decorator_two(func):
def inner(*args, **kwargs):
print('decorator222222222 .....decorating...before.. func')
ret = func(*args, **kwargs)
print('decorator222222222 .....decorating...after.. func')
return ret
return inner
@decorator_one
@decorator_two
def func():
print(func.__name__, 'runing............')
# 等价于
# def func():
# print(func.__name__, 'runing............')
# func = decorator_one(decorator_two(func))
-------------------------------------------------------------------------------------------------------------------------------
#输出结果
decorator111111111.....decorating...before.. func
decorator222222222 .....decorating...before.. func
inner runing............
decorator222222222 .....decorating...after.. func
decorator111111111.....decorating...after.. func
应用
Flask应用中,为视图函数增加额外验证session的装饰器时,出现了多个装饰器秀是同一个函数的情况
装饰器记录功能
用来记录装饰器装饰的函数以及执行了装饰器功能的函数
import time
log_1 = []
log_2 = []
def decorator(func):
log_1.append((func.__name__, func)) # 获取文件中所有被装饰器修饰的函数
def inner(*args, **kwargs):
log_2.append((func.__name__, func)) #获取被装饰并且执行过的函数
start = time.time()
ret = func(*args, **kwargs)
print(time.time() - start)
return ret
return inner
@decorator
def f():
print(f.__name__, 'f run ....')
f()
@decorator
def g():
print(g.__name__, 'g run ....')
print(log_1)
print(log_2)
---------------------------------------------------------------------------------------------------------------------
#运行结果
inner f run ....
0.0
[('f', <function f at 0x000002ED8FE1B8C8>), ('g', <function g at 0x000002ED8FE1B9D8>)]
[('f', <function f at 0x000002ED8FE1B8C8>)]
合并多个装饰器的方法
def decorator_one(func):
def inner(*args, **kwargs):
print('decorator111111111.....decorating...before.. func')
ret = func(*args, **kwargs)
print('decorator111111111.....decorating...after.. func')
return ret
return inner
def decorator_two(func):
def inner(*args, **kwargs):
print('decorator222222222 .....decorating...before.. func')
ret = func(*args, **kwargs)
print('decorator222222222 .....decorating...after.. func')
return ret
return inner
# 正常使用是这样的
#
# @decorator_one
# @decorator_two # 等价于 func = decorator_one(decorator_two(func))
# def func():
# print(func.__name__, 'runing............')
#
# func()
def composed(*decs):
def deco(f):
for dec in reversed(decs): # 注意顺序
f = dec(f)
return f
return deco
#现在
@composed(decorator_one, decorator_two)
def func():
print(func.__name__, 'runing...........')
func()
这是因为
# @decorator_one
# @decorator_two # =====>>>等价于 func = decorator_one(decorator_two(func))
# def func():
'''balabala....'''
pass
参考:https://stackoverflow.com/questions/5409450/can-i-combine-two-decorators-into-a-single-one-in-python
装饰器练习
1.编写装饰器,实现缓存网页内容的功能:
具体:实现下载的页面存放于文件中,如果文件内有值(文件大小不为0),就优先从文件中读取网页内容,否则,就去下载,然后存到文件中
url_l = []
from urllib.request import urlopen
def get_cache(func):
def inner(*args,**kwargs):
url = args[0]
filename = str(hash(url))
if url in url_l:
f = open(filename,'rb')
ret = f.read()
else:
url_l.append(url)
ret = func(*args, **kwargs)
f = open(filename,'wb')
f.write(ret)
f.close()
return ret
return inner
@get_cache
def get(url):
return urlopen(url).read()
编写装饰器,为多个函数加上认证的功能(用户的账号密码来源于文件),要求登录成功一次,后续的函数都无需再输入用户名和密码。 注意:从文件中读出字符串形式的字典,可以用eval(’{“name”:“egon”,“password”:“123”}’)转成字典格式
'''
编写装饰器,为多个函数加上认证的功能(用户的账号密码来源于文件),
要求登录成功一次,后续的函数都无需再输入用户名和密码
注意:从文件中读出字符串形式的字典,
可以用eval('{"name":"egon","password":"123"}')转成字典格式
'''
user_dic = {
'user' : None,
'status' : False
}
def get_dic(file):
with open(file) as f:
data = f.read()
return data
def check_info(func):
def inner(*args, **kwargs):
if not user_dic['status']:
name = input('name => ').strip()
password = input('password => ').strip()
# user_info = eval(open('info.txt', 'r', encoding='utf-8').read())
user_info = eval(get_dic('info.txt'))
if user_info.get(name) and user_info[name] == password:
print('login successful')
user_dic['user'] = name
user_dic['status']=True
ret = func(*args, **kwargs)
return ret
else:
print('login failed')
else:
ret = func(*args, **kwargs)
return ret
return inner
@check_info
def f1():
print(f1.__name__, 'f1 runing......')
f1()
@check_info
def f2():
print(f2.__name__, 'f2 runing......')
f2()
@check_info
def f3():
print(f3.__name__, 'f3 runing......')
f3()
@check_info
def f4():
print(f4.__name__, 'f4 runing......')
f4()
函数注释被装饰器装饰的时候,func.__doc__显示函数注释的方法
functools.wraps装饰器
# 当函数被装饰器装饰ide时候,是无法调用出其文档字符串的
def outer(func):
def inner(*args,**kwargs):
return func(*args, **kwargs)
return inner
@outer
def func():
'''这是一个函数文档字符串,当被装饰器装饰的时候,func.__doc__不会调用出其注释,可以是使用func.wraps装饰器解决这个问题
:return: None
'''
pass
print(func.__doc__)
--------------------------------------------------------------------------
# 结果
None
---------------------------------------------------------------------------------
# 解决方法
import functools
def outer(func):
@functools.wraps(func)
def inner(*args,**kwargs):
return func(*args, **kwargs)
return inner
@outer
def func():
'''这是一个函数文档字符串,当被装饰器装饰的时候,func.__doc__不会调用出其注释,可以是使用func.wraps装饰器解决这个问题
:return: None
'''
pass
print(func.__doc__)
-------------------------------------------------------------------------------------------------
# 结果
D:\Python36\python.exe D:/files/python_practice/20170814-0820/0814/practice.py
这是一个函数文档字符串,当被装饰器装饰的时候,func.__doc__不会调用出其注释,可以是使用func.wraps装饰器解决这个问题
:return: None
装饰一个类
import time
def wrapper(cls):
def inner(*args, **kwargs):
print('%s 开始运行'%cls.__name__)
start = time.time()
obj = cls(*args, **kwargs)
print('%s 运行了%s S'%(cls.__name__, time.time()-start))
return obj
return inner
@wrapper
class DayAction:
def __init__(self):
time.sleep(2)
d = DayAction()
-------------------------------------------------------------------------------------------
# 运行结果
DayAction 开始运行
DayAction 运行了2.0000367164611816 S