Are Python's dictionary view objects an exception to its assignment by value nature
根据栈溢出的这个问题,python中的赋值总是按值进行的,因此不能更改原始源。例如(来自同一个问题)
1 2 3 4 5 6 | locs = [ [1], [2] ] for loc in locs: loc = [] print locs # prints => [ [1], [2] ] |
但是,字典视图对象显示了相反的行为
1 2 3 4 5 6 7 | bike = {"Manufacturer":"Honda","Model":"CBR","cc":250,"price":70,"mileage":74} keys = bike.keys() print(keys) bike["tyres"] = 2 print(keys) |
这是输出:
1 2 | dict_keys(['mileage', 'price', 'Model', 'Manufacturer', 'cc']) dict_keys(['cc', 'Manufacturer', 'tyres', 'mileage', 'Model', 'price']) |
它们是否可以被视为按价值性质分配的一个例外?如果是的话,python3还有哪些例外?
不,这也不例外。没有为字典视图分配任何内容。视图明确记录为动态:
They provide a dynamic view on the dictionary’s entries, which means that when the dictionary changes, the view reflects these changes.
这是因为它们只存储对原始字典的引用,并以不同于字典API的方式直接访问键、值或
如果需要,可以在对象上构建自己的视图,但请注意,这样的对象仍然需要对原始对象的引用:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 | from collections.abc import Sequence class ListReversedView(Sequence): def __init__(self, lst): self._lst = lst def __getitem__(self, idx): if idx < 0: new = (-idx) - 1 else: new = len(self) - idx - 1 if new < 0: raise IndexError(new) return self._lst[new] def __len__(self): return len(self._lst) def __repr__(self): return f"[{', '.join(map(repr, self))}]" |
上面的示例为您提供了不同的列表内容视图;对列表的更改反映在该对象提供的"视图"中。不需要对python的赋值模型做任何特殊的操作;赋值仍然只是对对象的引用,并且这个视图对象中的
1 2 3 4 5 6 7 | >>> foo = ['spam', 'ham', 'eggs'] >>> view = ListReversedView(foo) >>> view ['eggs', 'ham', 'spam'] >>> foo[-1] = 'bacon' >>> view ['bacon', 'ham', 'spam'] |
循环回到您的列表循环;您仍然可以分配回列表对象本身;重新绑定名称可能不起作用,但重新绑定索引工作正常:
1 2 | for index in range(len(locs)): locs[index] = [] |
总之,python对象都位于一个堆中,名称和属性只是引用这些对象。可以存在多个引用,并且每个这样的引用都将看到对对象所做的更改(如果允许)。分配只更改特定引用指向的内容。dict视图在这里也不例外,它们只是继续引用从中创建它们的字典。
您可能想了解一下Python模型;我强烈推荐NedBatchelder关于Python名称和值的事实和神话。