目录
一, 迭代器
1. 迭代器简介
迭代是通过重复执行的代码处理相似的数据集的过程,并且本次迭代的处理数据要依赖上一次的结果,上一次产生的结果为下一次产生结果的初始状态,如果中途有任何停顿,都不能算是迭代。
常见的可迭代对象有:
- 几何数据类型,如list、tuple、dict、set、str等;
- 生成器(generator),包括生成器和带yield的生成器函数;
在python中,如果给定一个列表、元组、字符串…,我们可以通过for循环来遍历,这种遍历我们称为迭代,如下
for i in 'python':
print(i,sep=' ',end=' ') # 输出 p y t h o n
2. 判断迭代对象
可迭代对象并不是指某种具体的数据类型,是指存储了元素的一个容器对象,且容器中的元素可以通过__iter__()方法或__getitem()方法访问。
- __iter__方法的作用是让可迭代对象可以使用for…in…循环遍历;__getitem__()方法是让对象可以通过“实例名[index]”的方式访问实例中的元素。
- 一个可迭代对象不能独立进行迭代。python中通过for…in来完成迭代,凡是可迭代对象都可以直接用for…in…循环访问,这个语句内部执行:__iter__(0获得一个可迭代器,循环调用__next__()。
- 常见的可迭代对象包括:
- 集合数据类型,如list、tuple、dict、set、str等;
- 生成器,包括生成器和带yield的生成器函数;
- 可以通过collections模块的Iterable类型判断
判断一个对象是否是可迭代对象的方法:
from collections import Iterable
isinstance(变量,Iterable)
示例:
from collections import Iterable
print(isinstance([1,2,3],Iterable)) # 输出 True
3. 创建迭代器
迭代器是一个可以记住遍历的位置的对象。迭代器对象从集合的第一个元素开始访问呢,直到所有的元素被访问完结束。迭代器只能往前不会后退。
一个类作为迭代器使用时需要在类中实现两个方法:__iter__()和__next__()。
- __iter__()方法返回对象本身,即self。
- __next__()方法返回下一个数据,如果没有数据了,就需要抛出一个StopIteration异常。
示例,创建一个返回数字的迭代器,初始值为1,逐步递增1,在5此迭代后停止执行。
class MyNumber(object):
def __iter__(self):
self.a = 1
return self
def __next__(self):
if self.a <= 5:
x = self.a
self.a += 1
return x
else:
raise StopIteration # 抛出异常
myclass = MyNumber()
myitera = iter(myclass)
for x in myitera:
print(x)
输出:
1
2
3
4
5
二, 生成器
1. 生成器简介
通过列表推导式可以直接创建一个列表,但是会受到内存的限制,列表容量是有限的。并且,创建一个包含100万个元素的列表,占用很大的存储空间,如果只需要访问前面几个元素,后面的绝大部分元素占用的空间就浪费了。如果列表元素可以按照某种算法推算出来,那么可以在循环的过程中不断推算出后续的元素,就不必创建完整的list,从而节省大量的空间。
python提供了这种一边循环一边计算的机制,称为生成器generator。生成器的这个特性,解决了无限个变量和有限内存之间的矛盾问题,提供了解决方案,或者为优化内存使用效率提供了途径。
2. 创建生成器
■ 方式1:把列表生成式的[]改成(),使用next()函数获取生成器的返回值
L = [x*2 for x in range(4)]
print(L) # 输出 [0, 2, 4, 6]
print('*'*50)
# 生成器
G = (x*2 for x in range(4))
print(G) # 输出 <generator object <genexpr> at 0x000002570BBEB4F8>
# 遍历生成器G
print(next(G)) # 输出 0
print(next(G)) # 输出 2
print(next(G)) # 输出 4
print(next(G)) # 输出 6
■ 方式2:使用函数创建生成器,如果函数中有yield,那么该函数就变成生成器
如斐波拉契数列,如果使用列表生成式很难实现,使用函数就可以很方便的打印出来。
def createNum():
a,b = 0,1
for i in range(5):
print(b)
a,b = b,a+b
createNum()
输出显示
1
1
2
3
5
在createNum()函数中加入yield就变成了生成器,如下代码中,a是一个生成器对象,
- 当第一次调用next(a)的时候,生成器从上往下执行,执行到yield b的时候停止并返回b的值;
- 再次调用next(a)时,程序从原来停止的地方接着往下执行,循环执行到yield b的时候停止,返回b的值;
def createNum():
print('开始')
a,b = 0,1
for i in range(5):
print('before')
yield b
print('after 1')
a,b = b,a+b
print('after 2')
print('------')
print('stop')
a = createNum()
print(a)
print(next(a))
print(next(a))
print(next(a))
输出显示:
<generator object createNum at 0x000002C6F970B570>
开始
before
1
after 1
after 2
------
before
1
after 1
after 2
------
before
2
■ 方式3:函数改为生成器后,一般使用for循环进行迭代获取返回值,不需要关心StopIteration异常。如果要获取生成器的返回值,可以捕获StopIteration异常,返回值包含在StopIteration异常的value中。
def createNum():
print('开始')
a,b = 0,1
for i in range(5):
'''print('before')'''
yield b
'''print('after 1')'''
a,b = b,a+b
'''print('after 2')
print('------')'''
print('stop')
a = createNum()
print(a)
'''print(next(a))
print(next(a))
print(next(a))'''
print('*'*50)
for x in a:
print(x)
显示输出:
开始
1
1
2
3
5
stop
3. 生成器使用案例
■ 处理大量数据
import time
start = time.time()
print(sum([i for i in range(100000000)]))
end = time.time()
print('使用列表推导式耗时:',end-start)
start = time.time()
print(sum((i for i in range(100000000))))
end = time.time()
print('使用生成器耗时:',end-start)
显示结果,使用生成器的时间要少于使用列表推导式;并且在测试过程中,当使用列表推导式时,内存会飙升,而使用生成器时,内存几乎没有变化。
4999999950000000
使用列表推导式耗时: 8.564022779464722
4999999950000000
使用生成器耗时: 6.0585761070251465
■ 读取大文件,使用生成器yield可以挂起的特点,实现按需读取,每次读取指定大小的文件,避免因为读取文件时一次性读取过多,导致内存溢出等问题
def read_file(fpath):
BLOCK_SIZE = 1024
with open(fpath,'r',encoding='utf-8') as f:
while True:
block = f.read(BLOCK_SIZE)
if block:
yield block
else:
return
三, 装饰器
1. 闭包
闭包是能够读取其它函数内部变量的函数。闭包可以理解成定义在一个函数内部的函数。
本质上来说,闭包是将函数内部和函数外部连接起来的桥梁。
示例,
def test(n1):
print('before')
def test_in(n2):
print('test_in()...')
print(n1 + n2)
print('after')
return test_in
ret = test(10)
print(ret)
ret(1)
ret(20)
ret(30)
输出结果,在test()函数中返回的是test()函数内部的test_in()函数,那么可以把ret看作是一个函数,后面ret(1),ret(20),ret(30)就类似于调用函数test_in(),其中test_in()中的n1就等于创建ret时传进去的参数10
before
after
<function test.<locals>.test_in at 0x00000261FC623400>
test_in()...
11
test_in()...
30
test_in()...
40
2. 装饰器
装饰器,不改变原有程序的功能,不改变函数调用的接口的基础上,增添新的功能。
装饰器可以基于函数实现也可以基于类实现,使用方式基本是固定的,基本步骤是:
- 定义装饰函数(类);
- 定义业务函数;
- 在业务函数上添加@装饰函数/类名;
示例:
def wl(func):
'''装饰器函数'''
print('装饰器函数,',func)
def inner():
print('before')
func()
print('新增加的功能')
return inner
@wl # @wl表示调用函数的时候在上方加上 f1 = wl(f1)
def f1():
'''业务函数1'''
print('f1()')
@wl
def f2():
'''业务函数2'''
print('f2')
f1()
f2()
显示结果:
装饰器函数, <function f1 at 0x00000185E8E03400>
装饰器函数, <function f2 at 0x00000185E8E03510>
before
f1()
新增加的功能
before
f2
新增加的功能
python也支持多个装饰器同时使用
def w1(fn):
"""装饰器函数"""
print(fn)
def inner():
print("---1---")
return "<b>" + fn() + "</b>"
return inner
def w2(fn):
"""装饰器函数"""
print(fn)
def inner():
print("---2---")
return "<i>" + fn() + "</i>"
return inner
def w3(fn):
"""装饰器函数"""
print(fn)
def inner():
print("---3---")
return "<i>" + fn() + "</i>"
return inner
def w4(fn):
"""装饰器函数"""
print(fn)
def inner():
print("---4---")
return "<i>" + fn() + "</i>"
return inner
def w5(fn):
"""装饰器函数"""
print(fn)
def inner():
print("---5---")
return "<i>" + fn() + "</i>"
return inner
@w1
@w2
@w3
@w4
@w5
def f1():
"""业务函数"""
print("---0---")
return "hello python"
ret = f1()
print(ret)
显示结果:
<function f1 at 0x000001D89EA33620>
<function w5.<locals>.inner at 0x000001D89EA336A8>
<function w4.<locals>.inner at 0x000001D89EA33730>
<function w3.<locals>.inner at 0x000001D89EA337B8>
<function w2.<locals>.inner at 0x000001D89EA33840>
---1---
---2---
---3---
---4---
---5---
---0---
<b><i><i><i><i>hello python</i></i></i></i></b>
3. 装饰器执行的时间
python解释器执行到@w1代码时,就会开始自动进行装饰,不是等到调用的时候才装饰。
def w1(fn):
"""装饰器函数"""
print("---正在装饰---")
def inner():
print("---正在验证---")
fn()
return inner
@w1 # 只要Python解释器执行到这个代码,就开始自动进行装饰,而不是等到调用的时候才装饰
def f1():
"""业务函数"""
print("---2---")
return "hello python"
输出结果,没有调用f1()函数,输出结果打印了 正在装饰
---正在装饰---
多个装饰器执行时间
def w1(fn):
"""装饰器函数"""
print("---正在装饰1---")
def inner():
print("---正在验证---")
fn()
return inner
def w2(fn):
"""装饰器函数"""
print("---正在装饰2---")
def inner():
print("---正在验证---")
fn()
return inner
@w1
@w2
def f1():
"""业务函数"""
print("---3---")
return "hello python"
---正在装饰2---
---正在装饰1---
@w1在最上面,下面需要一个函数,但下面是@w2,必须先等 @w2装饰完再装饰。
4. 装饰器传参
传参示例1:
def w1(fn):
print('正在装饰')
def inner(a,b):
print('正在验证')
fn(a,b)
return inner
@w1
def f1(a,b):
print(a+b)
f1(10,10)
输出结果:
正在装饰
正在验证
20
不定长参数传参示例:
def w1(fn):
print('正在装饰')
def inner(*args,**kwargs):
print('正在验证')
fn(*args,**kwargs)
return inner
@w1
def f1(a,b):
print(a+b)
@w1
def f2(a,b,c):
print(a+b+c)
f1(10,10)
f2(20,20,20)
输出结果:
正在装饰
正在装饰
正在验证
20
正在验证
60
5. 装饰器返回值
# -*- coding: utf-8 -*-
"""
@File : 装饰器返回值.py
@Author : 小地瓜重新去华容道工作
@E-Mail : zoya.zh@qq.com
@Time : 22/9/18
"""
def w1(fn):
print('正在装饰')
def inner():
print('正在验证')
ret = fn()
return ret
return inner
@w1
def f1():
print('test')
return '原函数返回值'
ret = f1() # 参数接收返回值
print(ret)
显示结果:
正在装饰
正在验证
test
原函数返回值
正在验证
test
原函数返回值
6. 通用装饰器
# -*- coding: utf-8 -*-
"""
@File : 通用装饰器.py
@Author : 小地瓜重新去华容道工作
@E-Mail : zoya.zh@qq.com
@Time : 22/9/18
"""
def w1(fn):
def inner(*args,**kwargs):
print('记录日志')
ret = fn(*args,**kwargs)
return ret
return inner
@w1
def test1():
# 不需要返回值和参数
print('test1')
@w1
def test2():
# 需要返回值,不需要参数
print('test2')
return 'test2原函数返回值'
@w1
def test3(a):
# 需要参数
print('test3中数据:',a)
@w1
def test4(a):
# 需要参数和返回值
print('test4中数据:',a)
return 'test4原函数返回值'
ret1 = test1()
print('ret1 = ',ret1)
ret2 = test2()
print('ret2 = ',ret2)
ret3 = test3(4)
print('ret3 = ',ret3)
ret4 = test4(6)
print('ret4 = ',ret4)
显示结果:
记录日志
test1
ret1 = None
记录日志
test2
ret2 = test2原函数返回值
记录日志
test3中数据: 4
ret3 = None
记录日志
test4中数据: 6
ret4 = test4原函数返回值
Process finished with exit code 0
7. 装饰器带参数
def func_arg(arg):
def func(funtionName):
def func_in():
print("输出给装饰器传入的参数:%s" % arg)
if arg == "hello":
funtionName()
funtionName()
else:
funtionName()
return func_in
return func
@func_arg("hello")
def test():
print("---test---")
@func_arg("haha")
def test2():
print("---test2---")
test()
test2()
输出结果并显示:
输出给装饰器传入的参数:hello
---test---
---test---
输出给装饰器传入的参数:haha
---test2---
执行步骤:
- 限制性func_arg(‘hello’)函数,这个函数return的结果是func函数的引用;
- @func
- 使用@func对test进行装饰
装饰器传参的作用:传递的参数不同,可以用于判断做不同的事情
参考:
版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 举报,一经查实,本站将立刻删除。
文章由极客之音整理,本文链接:https://www.bmabk.com/index.php/post/46076.html