装饰器
装饰器本质上就是一个闭包函数,他可以在不改变源码的情况下,增加其他的功能
闭包
:内层函数对外层函数的非全局变量的引用叫闭包,他的本质是函数的嵌套
应用场景:小范围的需要给类增加其他功能就可以使用装饰器
插入日志,性能测试,缓存,事务处理等场景
原则:装饰器遵循开放封闭原则
1、对外扩展是开放的
任何一个程序,不可能在设计之初就想好了所有的功能,并且未来不做任何更新和修改.所以我们允许代码扩展、添加新功能
2、对内修改时封闭的
一个程序在修改的时候,可能已经交付给他人使用了,如果这时候我们对其进行修改,很有可能影响其他已经在使用该程序的用户
无参数装饰器
# 性能测试
import time
def timer(func):
def inner(*args,**kwargs):
start = time.time()
ret = func(*args,**kwargs) # func() 的返回值赋值给 ret
time.sleep(1)
print('所用时间:',time.time() - start - 1)
return ret # inner函数的返回值 返回给调用者 func1
return inner
@timer # 没有参数 timer = timer(func1)
def func1(*args,**kwargs):
print('打印参数:',args)
print('打印参数:',*args)
return 'func1 的返回值' # 因为是 func() 调用的所以返回给 func()
print(func1(666,555,444)) # 得到 ret 打印得到 :func1 的返回值
补充:函数的有效信息
def login(username, password):
"""
函数的注释信息
:param (参数) username: 用户名
:param (参数)password: 密码
:return: (返回值) True 登录成功 False 登录失败
"""
print(login.__name__) # 打印这个函数的函数名
print(login.__doc__) # 打印这个函数的注释信息
return username + password
print login('你好', '中国')
#-------------打印结果----------------
login
函数的注释信息
:param (参数) username: 用户名
:param (参数)password: 密码
:return: (返回值) True 登录成功 False 登录失败
你好中国
装饰器修复技术
函数的有效信息通常记录着一些有用信息,但是一旦和装饰器联系在一块的话,就会出现打印为空的现象
def wrapper(func):
def inner(*args, **kwargs):
print("在前面执行的代码。。。。")
func()
print("在后面执行的代码...")
return inner
@wrapper
def f():
"""
这是一个用来测试装饰器修复技术的函数
"""
print("哈哈哈")
if __name__ == '__main__':
print("执行的函数名:", f.__name__)
print("函数的注释:", f.__doc__)
# 打印结果
执行的函数名: inner
函数的注释: None
这是因为我们所执行的f() 函数实际上执行的是装饰器中的 inner() 函数,所以打印为空
添加装饰器修复技术@wraps
from functools import wraps # 导入 weaps模块
def wrapper(func):
@wraps(func) # 装饰 inner函数
def inner(*args, **kwargs):
print("在前面执行的代码。。。。")
func()
print("在后面执行的代码...")
return inner
@wrapper
def f():
"""
这是一个用来测试装饰器修复技术的函数
"""
print("哈哈哈")
if __name__ == '__main__':
print("执行的函数名:", f.__name__)
print("函数的注释:", f.__doc__)
# 打印结果
执行的函数名: f
函数的注释: 这是一个用来测试装饰器修复技术的函数
但在django框架中使用的装饰器修复技术并不是这个
# CBV装饰器:
from django.utils.decorateors import method_decorator(是给CBV视图的)
# csrf相关的装饰器:
from django.views.decoraters.csrf import csrf_exempt,csrf_protect
csrf_exempt # 取消伪造保护
csrf_protect # 当全局没有保护校验时,可以通过其获取保护
示例:@method_decorator(csrf_exempt)
注意:不能直接加在 get 或 post 方法上,可以加在 dispatch上或类上(指定装饰的方法)
@method_decorator(csrf_exempt, name='get')
带参数装饰器
def outer(flag):
def timer(func):
def inner(*args,**kwargs):
if flag:
print('''执行函数之前要做的''')
re = func(*args,**kwargs)
if flag:
print('''执行函数之后要做的''')
return re
return inner
return timer
@outer(True) # 这是两步 1. timer = outer(True) 将参数传进去
# 2.@timer == timer=timer(func)
def func():
print(111)
func()
多个装饰器装饰一个
在函数中如果有装饰器,计算机会将有语法糖的代码和下面被装饰的代码视为一条,所以他才会执行完语法糖后找到被装饰的函数,如果有多个装饰器,则优先执行离函数近的语法糖,最后依次向上执行其他的语法糖
def xxx1(f): # f = inner2
def inner1():
print('''装饰前1''')
rte = f()
print('''装饰后1''')
return inner1
def xxx2(f): # f = func 函数
def inner2():
print('''装饰前2''')
rte = f()
print('''装饰后2''')
return inner2
@xxx1 # func = xxx1(func) * 里面的func = inner2, 外面的最新变量func = inner1
@xxx2 # 优先执行 func = xxx2 (func) * 里面的 func 是下面的函数,外面的func = inner2
def func():
print('函数')
func() 由此得 func = inner1
结果:
装饰前1
装饰前2
函数
装饰后2
装饰后1
在哪里用过装饰器:用户登录
在哪里用过带参数的装饰器:flask的路由系统
迭代
重复做一些事很多次(就现在循环中做的那样)
可迭代对象 Iterable
内部实现 __iter__
方法 (可以直接作用于for循环的对象统称为可迭代对象,可以使用isinstance() 来判断 Iterable
)
迭代器 Iterator
有 __next__
方法的就是迭代器,迭代器是从第一个值开始一个一个的取,直到取完才结束,中间只能前进不能后退,也不能生成值可以通过 isinstance()
判断对象是否是 Iterator
生成器
具有 yeild方法,生成器是基于迭代器的,不仅能取值,还能生成值.被执行后返回的是一个生成器 生成器的本质就是一个迭代器
生成器
通过列表生成式,我们可以直接创建一个列表.但是,受到内存限制,列表容量可定是有限的.而且,我们创建一个包含100万个元素的列表,不仅占用很大的储存空间,如果我们仅仅需要访问前面几个元素,那后面绝大多数元素占用的空间都白白浪费了.
在Python中像列表生成器这样 一边循环一边计算的机制称为生成器generator
要创建一个生成器generator
有很多中方法
- 用列表推导式来实现 将
[]
改成()
L = [ x for x in range(2) ]
G = ( x for x in range(2))
print(L)
print(G.__next__())
print(G.__next__())
结果:
[0, 1]
0
1
如果再加一个 print G.__next__() 就会报错,因为这里面只有两个,找不到第三个
报错信息:
Traceback (most recent call last):
File "D:/xxx/生成器.py", line 368, in <module>
print(G.__next__())
StopIteration
注意:只有调用
__next__
一次,就会生成一个值,直到生成最后一个元素,其他时候就是一段普通的代码
不断的调用__next__
很不方便,正确的方法是for
循环,因为生成器也是可迭代对象,而且通过for
不用关心StopIteration
的错误
手写 for 循环
G = ( x for x in range(3) )
while 1:
try:
print(G.__next__())
except StopIteration as f:
print('没了',f.value)
break # 跳出循环
结果
0
1
2
没了 None
- 用函数来实现,在定义中包含
yield
关键字
generator 非常强大.如果推算的算法比较复杂,用类似列表生成式的
for
循环无法实现就可以用函数来实现.
比如,著名的斐波拉契数列(Finonacci) 除去第一个和第二个以外,任意一个数都可有前两个数相加得到
1,1,2,3,5,8,13,21,34........
用函数实现 斐波拉契数列
def fib(max):
n,a,b = 0,0,1 # 循环次数,ab 前两位数
li = []
while n < max:
a,b = b,a + b
n = n + 1
li.append(a)
return li
通过观察,
fib
定义了斐波拉契数列的推算规则,可以从第一个元素开始推算出后续任意的元素,他和 generator 的距离 仅有一个yield
def fib(max):
n,a,b = 0,0,1 # 循环次数,ab 前两位数
while n < max:
a,b = b,a + b
n = n + 1
yield b # 不在需要列表来保存,调用一次,生成一个
yield '不返回 return'
return '返回 return'
x = fib(6)
print('return',x)
print( x.__next__() )
print( x.__next__() )
结果:
return <generator object fib at 0x0000019C749541A8>
1
不返回 return
由此可知 函数时顺序执行,遇到
return
或最后一行就返回,而生成器的函数,是每次调用__next__
的执行,遇到yield
返回,再次执行,从上一次返回的yield
继续执行
迭代器
Python中迭代器的本质上每次调用next()方法都返回下一个元素或抛出StopIteration的容器对象
由于Python中没有“迭代器”这个类,因此具有以下两个特性的类都可以称为“迭代器”类:
1.有__next__()
方法,返回容器的下一个元素或抛出StopIteration异常
2.有__iter__()
方法,返回迭代器本身
迭代器和可迭代对象:都可以使用for循环,当然,迭代器内部多了一个
__next__
方法
判断迭代器和可迭代的方法
1. 判断内部是否含有__next__
方法
2. lterable 判断是不是可迭代对象
3. Iterator 判断是不是迭代器
判断是否为可迭代对象
from collections import Iterable
print( isinstance([],Iterable ))
print( isinstance({},Iterable ))
print( isinstance('abc',Iterable ))
print( isinstance((1,2,3),Iterable ))
print( isinstance({1,2,3},Iterable ))
print( isinstance(100,Iterable ))
print( isinstance( range(10),Iterable )) # range
True
True
True
True
True
False
True
# 判断是否为迭代器
from collections import Iterator
print( isinstance([],Iterator ))
print( isinstance({},Iterator ))
print( isinstance('abc',Iterator ))
print( isinstance((1,2,3),Iterator ))
print( isinstance({1,2,3},Iterator ))
print( isinstance(100,Iterator ))
print( isinstance( range(10),Iterator )) # range
print( isinstance(( x for x in range(10)),Iterator )) # 生成器
print( isinstance(map(lambda x:x,[1,2,3]),Iterator )) # map
False
False
False
False
False
False
False
True
True
range函数 是一个可迭代的,但不是迭代器
map函数 自带迭代器
虽然列表、字符串.....这些不是迭代器,但是我们可以把它变成迭代器
为什么list、dict、str等数据类型不是Iterator?
这是因为Python的Iterator对象表示的是一个数据流,Iterator对象可以被next()函数调用并不断返回下一个数据,直到没有数据时抛出StopIteration错误。可以把这个数据流看做是一个有序序列,但我们却不能提前知道序列的长度,只能不断通过next()函数实现按需计算下一个数据,所以Iterator的计算是惰性的,只有在需要返回下一个数据时它才会计算。
Iterator甚至可以表示一个无限大的数据流,例如全体自然数。而使用list是永远不可能存储全体自然数的。
使用 Iter()
可以将迭代对象转化为迭代器
>>> isinstance(iter([]), Iterator)
True
>>> isinstance(iter('abc'), Iterator)
True
迭代器小结
- 凡是可作用于
for
循环的对象都是Iterable(可迭代对象)
类型; - 凡是可作用于
__next__
(next()函数)的对象都是Iterator(迭代器)
类型,它们表示一个惰性计算的序列; - 集合数据类型如list、dict、str等是Iterable但不是Iterator,不过可以通过iter()函数获得一个Iterator对象。
- Python的for循环本质上就是通过不断调用next()函数实现的
for x in [1, 2, 3, 4, 5]:
pass
实际上完全等价于:
# 首先获得Iterator对象:
it = iter([1, 2, 3, 4, 5])
# 循环:
while True:
try:
# 获得下一个值:
x = next(it)
except StopIteration:
# 遇到StopIteration就退出循环
break