Python3.1中的视图?
在Python3.1中,视图到底是什么呢?它们的表现方式和迭代器有点像,而且也可以转换成列表。那么,视图和迭代器有什么不同呢?
3 个回答
迭代器和视图有什么不同?
我把这个问题换个说法,就是“可迭代对象和迭代器有什么区别?”
可迭代对象是指那些可以被遍历的对象,比如可以在一个 for
循环中使用的对象。
迭代器是可以通过 next()
函数来调用的对象,也就是说它在 Python2 中实现了 .next()
方法,在 Python3 中实现了 .__next__()
方法。迭代器通常用来包装一个可迭代对象,并逐个返回我们感兴趣的项目。所有的迭代器都是可迭代的,但反过来并不一定成立(并不是所有可迭代对象都是迭代器)。
视图是 可迭代 对象,而不是 迭代器。
我们来看一些代码来区分这两者(Python 3):
在 “Python 3 新特性” 的文档中,明确说明了哪些函数返回迭代器。map()
、filter()
和 zip()
确实返回迭代器,而 dict.items()
、dict.values()
和 dict.keys()
被认为返回的是视图对象。至于 range()
,虽然它返回的内容描述不够精确,但我们知道它不是迭代器。
使用 map()
来将列表中的所有数字翻倍
m = map(lambda x: x*2, [0,1,2])
hasattr(m, '__next__')
# True
next(m)
# 0
next(m)
# 2
next(m)
# 4
next(m)
# StopIteration ...
使用 filter()
来提取所有奇数
f = filter(lambda x: x%2==1, [0,1,2,3,4,5,6])
hasattr(f, '__next__')
# True
next(f)
# 1
next(f)
# 3
next(f)
# 5
next(f)
# StopIteration ...
尝试用 range()
以相同的方式生成一系列数字
r = range(3)
hasattr(r, '__next__')
# False
next(r)
# TypeError: 'range' object is not an iterator
但它是一个可迭代对象,所以我们应该能够用迭代器来包装它
it = iter(r)
next(it)
# 0
next(it)
# 1
next(it)
# 2
next(it)
# StopIteration ...
dict.items()
以及 dict.keys()
和 dict.values()
在 Python 3 中也不返回迭代器
d = {'a': 0, 'b': 1, 'c': 2}
items = d.items()
hasattr(items, '__next__')
# False
it = iter(items)
next(it)
# ('b', 1)
next(it)
# ('c', 2)
next(it)
# ('a', 0)
迭代器只能在一个 for
循环中使用,而可迭代对象可以在后续的 for
循环中重复使用。每次在这个上下文中使用可迭代对象时,它会隐式地返回一个新的迭代器(通过它的 __iter__()
方法)。下面的自定义类通过输出列表对象和返回的迭代器对象的内存 id
来演示这一点:
class mylist(list):
def __iter__(self, *a, **kw):
print('id of iterable is still:', id(self))
rv = super().__iter__(*a, **kw)
print('id of iterator is now:', id(rv))
return rv
l = mylist('abc')
for
循环可以使用可迭代对象,并会隐式地获得一个迭代器
for c in l:
print(c)
# id of iterable is still: 139696242511768
# id of iterator is now: 139696242308880
# a
# b
# c
后续的 for
循环可以使用同一个可迭代对象,但会得到另一个迭代器
for c in l:
print(c)
# id of iterable is still: 139696242511768
# id of iterator is now: 139696242445616
# a
# b
# c
我们也可以显式地获得一个迭代器
it = iter(l)
# id of iterable is still: 139696242511768
# id of iterator is now: 139696242463688
但它只能使用一次
for c in it:
print(c)
# a
# b
# c
for c in it:
print(c)
for c in it:
print(c)
我建议你看看这个链接。它似乎是解释得最清楚的。
根据我的理解,视图(views)主要和dict
(字典)有关,可以强制转换成list
(列表)。你还可以把它们变成一个迭代器,这样就可以通过for
循环或者调用next
来逐个访问里面的元素。
更新:来自时光机的更新链接
根据我的理解,视图仍然和它创建时的那个对象是有联系的。对原始对象的修改会影响到这个视图。
来自文档(关于字典视图的内容):
>>> dishes = {'eggs': 2, 'sausage': 1, 'bacon': 1, 'spam': 500}
>>> keys = dishes.keys()
>>> values = dishes.values()
>>> # iteration
>>> n = 0
>>> for val in values:
... n += val
>>> print(n)
504
>>> # keys and values are iterated over in the same order
>>> list(keys)
['eggs', 'bacon', 'sausage', 'spam']
>>> list(values)
[2, 1, 1, 500]
>>> # view objects are dynamic and reflect dict changes
>>> del dishes['eggs']
>>> del dishes['sausage']
>>> list(keys)
['spam', 'bacon']
>>> # set operations
>>> keys & {'eggs', 'bacon', 'salad'}
{'bacon'}