Python - 三大器 迭代器,生层器,装饰器
Python - 三大器 迭代器,生层器,装饰器
在介绍三大器之前先来了解一下容器和可迭代对象...
一. 容器
容器是一种把多个元素组织在一起的数据结构,容器中的元素可以逐个地迭代获取,可以用in, not in关键字判断元素是否包含在容器中。通常这类数据结构把所有的元素存储在内存中(也有一些特例,并不是所有的元素都放在内存,比如迭代器和生成器对象)在Python中,常见的容器对象有:
- list, deque...
- set, frozensets(不可变集合)...
- dict, defaultdict, OrderedDict, Counter...
- tuple, namedtuple...
- str
容器的概念就像一个盒子,可以往里面装东西.当它可以用来询问某个元素是否包含在其中时,那么这个对象就可以认为是一个容器,比如 list,set,tuples都是容器对象:
>>> assert 1 in [1, 2, 3] # lists
>>> assert 4 not in [1, 2, 3]
>>> assert 1 in {1, 2, 3} # sets
>>> assert 4 not in {1, 2, 3}
>>> assert 1 in (1, 2, 3) # tuples
>>> assert 4 not in (1, 2, 3)
询问某元素是否在dict中用dict的中key:
>>> d = {1: 'foo', 2: 'bar', 3: 'qux'}
>>> assert 1 in d
>>> assert 'foo' not in d # 'foo' 不是dict中的元素
询问某substring是否在string中:
>>> s = 'foobar'
>>> assert 'b' in s
>>> assert 'x' not in s
>>> assert 'foo' in s
尽管绝大多数容器都提供了某种方式来获取其中的每一个元素,但这并不是容器本身提供的能力,而是可迭代对象赋予了容器这种能力,当然并不是所有的容器都是可迭代的,比如:Bloom filter,虽然Bloom filter可以用来检测某个元素是否包含在容器中,但是并不能从容器中获取其中的每一个值,因为Bloom filter压根就没把元素存储在容器中,而是通过一个散列函数映射成一个值保存在数组中。
二. 可迭代对象(iterable)
大部分对象都是可迭代,只要实现了__iter__方法的对象就是可迭代的。
__iter__方法会返回迭代器(iterator)本身,例如:
>>> lst = [1,2,3]
>>> lst.__iter__()
<listiterator object at 0x7f97c549aa50>
Python提供一些语句和关键字用于访问可迭代对象的元素,比如for循环、列表解析、逻辑操作符等。
判断一个对象是否是可迭代对象:
>>> from collections import Iterable # 只导入Iterable方法
>>> isinstance('abc', Iterable)
True
>>> isinstance(1, Iterable)
False
>>> isinstance([], Iterable)
True
这里的isinstance()函数用于判断对象类型。
可迭代对象一般都用for循环遍历元素,也就是能用for循环的对象都可称为可迭代对象。
例如,遍历列表:
>>> lst = [1, 2, 3]
>>> for i in lst:
... print i
...
三. 迭代器
迭代器协议是指:对象必须提供一个next方法,执行该方法要么返回迭代中的下一项,要么就引起一个Stoplteration异常,以终止迭代(只能往后走不能往前退)
实现了迭代器协议的对象(对象内部定义了一个__iter__()方法)
python中的内部工具(如for循环,sum,min,max函数等)基于迭代器协议访问对象。
使用迭代器的好处:
1)如果使用列表,计算值时会一次获取所有值,那么就会占用更多的内存。而迭代器则是一个接一个计算。
2)使代码更通用、更简单。
判断是否是迭代器:
>>> from collections import Iterator
>>> isinstance(d, Iterator)
False
>>> isinstance(d.iteritems(), Iterator)
True
使用next方法:
>>> iter_items = d.iteritems()
>>> iter_items.next()
('a', 1)
>>> iter_items.next()
('c', 3)
>>> iter_items.next()
('b', 2)
迭代器的原理:
1 #基于迭代器协议
2 li = [1,2,3]
3 diedai_l = li.__iter__()
4 print(diedai_l.__next__())
5 print(diedai_l.__next__())
6 print(diedai_l.__next__())
7 # print(diedai_l.__next__()) # 超出边界报错
8
9 #下标
10 print(li[0])
11 print(li[1])
12 print(li[2])
13 # print(li[3]) # 超出边境报错
14
15 # 用while循环模拟for循环机制
16 diedai_l = li.__iter__()
17 while True:
18 try:
19 print(diedai_l.__next__())
20 except StopIteration:
21 print("迭代完毕,循环终止")
22 break
23
24 # for循环访问方式
25 # for循环本质就是遵循迭代器协议的访问方式,先调用diedai_l=li.__iter__方法
26 # 或者直接diedai_l=iter(l),然后依次执行diedai_l.__next__(),直到捕捉到
27 # StopItearation终止循环
28 # for循环所有的对象的本质都是一样的原理
四. 生成器
可以理解为一种数据类型,自动实现迭代器协议
在调用生成器运行的过程中,每次遇到yield时函数会暂停并保存当前所有的运行信息,返回yield的值。并在下一次执行next()方法时从当前位置继续运行
-
表现形式:
生成器函数 带yield的函数(1、返回值 2、保留函数的运行状态)
next(t) t.__next__ t.send(可以给上一层的yield传值)
# 用生成器函数 # yield 相当于return控制的是函数的返回值 # x=yield的另外一个特性,接收send传过来的值,赋值给x def test(): print("开始啦") first = yield # return 1 first = None print("第一次",first) yield 2 print("第二次") t = test() print(test().__next__()) res = t.__next__() # next(t) print(res) res = t.send("函数停留在first那个位置,我就是给first赋值的") print(res) 输出结果 开始啦 None 开始啦 None 第一次 函数停留在first那个位置,我就是给first赋值的
-
生成器表达式
print(sum(i for i in range(10000))) # 表达式一般用for循环 (i for i in range(10000))
作用 节省内存,在内部已经实现了__iter__的方法
五. 装饰器
1. 定义
在不修改被修饰函数的源代码和调用方式的情况下给其添加额外功能.
装饰器 = 高阶函数+函数嵌套+闭包
import time #导入时间模块
def foo(func): # func = test
def bar(*args,**kwargs):
start_time = time.time()
res = func(*args,**kwargs) # 这里就是在运行test() 赋予变量
stop_time = time.tiem()
print("一场LOL时间为{}").format(stop_time-start_time)
return res # 返回test()的值
return bar
def test(name, age):
time.sleep(1)
print("哈哈")
return "heihei"
res = test("德玛西亚", age=10)
print(ret)
六. 闭包
根据这句话,其实我们自己就可以总结出在python语言中形成闭包的三个条件,缺一不可:
1)必须有一个内嵌函数(函数里定义的函数)——这对应函数之间的嵌套
2)内嵌函数必须引用一个定义在闭合范围内(外部函数里)的变量——内部函数引用外部变量
3)外部函数必须返回内嵌函数——必须返回那个内部函数
def funx():
x=5
def funy():
nonlocal x
x+=1
return x
return funy
python闭包的优点:
避免使用全局变量
可以提供部分数据的隐藏
可以提供更优雅的面向对象实现