Python学习V---高级特性

这篇博客主要是阅读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
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值