这篇博客主要是阅读python之旅 时做的笔记。提取出最主要的知识点,供个人在以后中快速查阅。
迭代器
在 Python 中,迭代器是指遵循迭代器协议(iterator protocol)的对象。
含有 __iter__() 方法或 __getitem__() 方法的对象称之为可迭代对象。
可以使用下面方法进行判定是否可迭代
>>> hasattr((), '__iter__')
True
>>> hasattr([], '__iter__')
True
>>> hasattr({}, '__iter__')
True
>>> hasattr(123, '__iter__')
False
>>> hasattr('abc', '__iter__')
False
>>> hasattr('abc', '__getitem__')
True
我们熟知的字典(dict)、元组(tuple)、集合(set)和字符串对象都是可迭代的。
那么什么是可迭代协议呢?
迭代器协议(iterator protocol)是指要实现对象的 iter() 和 next() 方法(注意:Python3 要实现 next() 方法),其中,iter() 方法返回迭代器对象本身,next() 方法返回容器的下一个元素,在没有后续元素时抛出 StopIteration 异常。
值得注意的是,可迭代不一定是迭代器,上面说了迭代器必须是满足两个方法。
>>> hasattr((1, 2, 3), '__iter__')
True
>>> hasattr((1, 2, 3), 'next') # 有 __iter__ 方法但是没有 next 方法,不是迭代器
False
#或是简单点判定
>>> from collections import Iterator
>>>
>>> isinstance((), Iterator)
False
>>> isinstance({}, Iterator)
False
因此虽然元组、列表和字典等对象是可迭代的,但它们却不是迭代器!
那咋让这些可迭代的变成迭代器啊。。iter() 函数就可以
>>> from collections import Iterator
>>> isinstance(iter([1, 2, 3]), Iterator) # 使用 iter() 函数,获得迭代器对象
True
>>> isinstance(iter('abc'), Iterator)
True
>>>
>>> my_str = 'abc'
>>> my_iter = iter(my_str) # 获得迭代器对象
>>> isinstance(my_iter, Iterator)
True
>>> next(my_iter) # 可使用内置的 next() 函数获得下一个元素
'a'
Python 的 for 循环就是先通过内置函数 iter() 获得一个迭代器,然后再不断调用 next() 函数实现的,比如:
for x in [1, 2, 3]:
print i
#等价于
# 获得 Iterator 对象
it = iter([1, 2, 3])
# 循环
while True:
try:
# 获得下一个值
x = next(it)
print x
except StopIteration:
# 没有后续元素,退出循环
break
斐波那契数列迭代器
# -*- coding: utf-8 -*-
from collections import Iterator
class Fib(object):
def __init__(self):
self.a, self.b = 0, 1
# 返回迭代器对象本身
def __iter__(self):
return self
# 返回容器下一个元素
def next(self):
self.a, self.b = self.b, self.a + self.b
return self.a
def main():
fib = Fib() # fib 是一个迭代器
print 'isinstance(fib, Iterator): ', isinstance(fib, Iterator)
for i in fib:
if i > 10:
break
print i
if __name__ == '__main__':
main()
在类的实现中,我们定义了__iter__ 方法,它返回对象本身,这个方法会在遍历时被 Python 内置的 iter() 函数调用,返回一个迭代器。类中的next() 方法用于返回容器的下一个元素,当使用 for 循环进行遍历的时候,就会使用 Python 内置的 next() 函数调用对象的 next 方法(在 Python3 中是 __next__ 方法)对迭代器进行遍历。
生成器
这个挺有意思。。不骗你。
生成器是一种迭代器,在每次迭代时返回一个值,直到抛出 StopIteration 异常。
带有yield关键字就是生成器函数,调用该函数会返回一个生成器。
生成器就是说,我不要一次一个完整的list了,因为可能根本用不到一个完整的list,我只需要在实际用到时候,取其中某一些就行。这时候就需要一个算法,进行惰性求值。
最简单的生成器:将列表的[]
改成()
.
>>> L = [x * x for x in range(10)]
>>> L
[0, 1, 4, 9, 16, 25, 36, 49, 64, 81]
>>> g = (x * x for x in range(10))
>>> g
<generator object <genexpr> at 0x1022ef630>
然后不断next()就行
>>> next(g)
0
>>> next(g)
1
>>> next(g)
4
>>> next(g)
9
>>> next(g)
16
>>> next(g)
25
>>> next(g)
36
>>> next(g)
49
>>> next(g)
64
>>> next(g)
81
>>> next(g)
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
StopIteration
用for来调用生成器
>>> g = (x * x for x in range(10))
>>> for n in g:
... print(n)
...
0
1
4
9
16
25
36
49
64
81
那么如何自定义生成器呢?
函数定义中有yield,那么就会变成生成器,看下面例子
>>> def generator_function():
... print 'hello 1'
... yield 1
... print 'hello 2'
... yield 2
... print 'hello 3'
>>>
>>> g = generator_function() # 函数没有立即执行,而是返回了一个生成器,当然也是一个迭代器
>>> g.next() # 当使用 next() 方法,或使用 next(g) 的时候开始执行,遇到 yield 暂停
hello 1
1
>>> g.next() # 从原来暂停的地方继续执行
hello 2
2
>>> g.next() # 从原来暂停的地方继续执行,没有 yield,抛出异常
hello 3
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
StopIteration
可以看出
1. 首次调用生成器函数时,返回的是一个生成器,也是迭代器。
2. 通过next()可以让函数开始执行。并且在yield时停下,返回 yield后面的值。比如这里是字符‘1’, ‘2’之类的。
3. 再次调用next()会从原来停下的地方继续执行,如果后面没有yield,则出现异常。
#迭代器版本
>>> class Fib(object):
... def __init__(self):
... self.a, self.b = 0, 1
... def __iter__(self):
... return self
... def next(self):
... self.a, self.b = self.b, self.a + self.b
... return self.a
...
>>> f = Fib()
>>> for item in f:
... if item > 10:
... break
... print item
#生成器版本
>>> def fib():
... a, b = 0, 1
... while True:
... a, b = b, a + b
... yield a
...
>>> f = fib()
>>> for item in f:
... if item > 10:
... break
... print item
send/throw/close — 对生成器进行控制
使用 send() 方法给它发送消息;
使用 thow() 方法给它发送异常;
使用 close() 方法关闭生成器;
再看一遍,send()是将消息发送给生成器。因为生成器不是每次next执行时碰到yield就会停止,并且把yield后面的值返回吗?那我们就不能直接传入一个值进去替换掉该返回值啊,这样的话,下一次如果用到该返回值,就不是上次yield后面的值了,而是我们传入的新值。。用send。。
send相当于下面2个功能:
1. next()
2. 将上一次yield返回的值替换成传入的新值
>>> def generator_function():
... value1 = yield 0
... print 'value1 is ', value1
... value2 = yield 1
... print 'value2 is ', value2
... value3 = yield 2
... print 'value3 is ', value3
...
>>> g = generator_function()
>>> g.next() # 调用 next() 方法开始执行,返回 0
0
>>> g.send(2) #此时,可以看到,直接把value1的值1替换成2.
value1 is 2
1
>>> g.send(3)
value2 is 3
2
>>> g.send(4)
value3 is 4
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
StopIteration
throw
给生成器传入异常
>>> def generator_function():
... try:
... yield 'Normal'
... except ValueError:
... yield 'Error'
... finally:
... print 'Finally'
...
>>> g = generator_function()
>>> g.next()
'Normal'
>>> g.throw(ValueError)
'Error'
>>> g.next()
Finally
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
StopIteration
close方法
>>> def generator_function():
... yield 1
... yield 2
... yield 3
...
>>> g = generator_function()
>>>
>>> g.next()
1
>>> g.close() # 关闭生成器
>>> g.next()
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
StopIteration
上下文管理器
上下文就是环境,上下文管理器满足上下文管理器协议
- 上下文管理器协议,是指要实现对象的 __enter__() 和 __exit__() 方法。
from math import sqrt, pow
class Point(object):
def __init__(self, x, y):
print 'initialize x and y'
self.x, self.y = x, y
def __enter__(self):
print "Entering context"
return self
def __exit__(self, type, value, traceback):
print "Exiting context"
def get_distance(self):
distance = sqrt(pow(self.x, 2) + pow(self.y, 2))
return distance
#使用with调用上下文管理器
with Point(3, 4) as pt:
print 'distance: ', pt.get_distance()
# output
initialize x and y # 调用了 __init__ 方法
Entering context # 调用了 __enter__ 方法
distance: 5.0 # 调用了 get_distance 方法
Exiting context # 调用了 __exit__ 方法
Python某些内置对象可以直接用with语句。常见的是文件操作。
file = open('somefile', 'r')
try:
for line in file:
print line
finally:
file.close() # 确保关闭文件
#使用with
with open('somefile', 'r') as file:
for line in file:
print line