Python3.1中的视图?

20 投票
3 回答
6305 浏览
提问于 2025-04-16 04:32

在Python3.1中,视图到底是什么呢?它们的表现方式和迭代器有点像,而且也可以转换成列表。那么,视图和迭代器有什么不同呢?

3 个回答

2

迭代器和视图有什么不同?

我把这个问题换个说法,就是“可迭代对象和迭代器有什么区别?”

可迭代对象是指那些可以被遍历的对象,比如可以在一个 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)
5

我建议你看看这个链接。它似乎是解释得最清楚的。

根据我的理解,视图(views)主要和dict(字典)有关,可以强制转换成list(列表)。你还可以把它们变成一个迭代器,这样就可以通过for循环或者调用next来逐个访问里面的元素。

更新:来自时光机的更新链接

13

根据我的理解,视图仍然和它创建时的那个对象是有联系的。对原始对象的修改会影响到这个视图。

来自文档(关于字典视图的内容):

>>> 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'}

撰写回答