使用列表迭代器,您可以通过pickle协议找到原始列表:
>>> L = [1, 2, 3]
>>> Li = iter(L)
>>> Li.__reduce__()[1][0] is L
True
给出一个字典迭代器,如何找到原始字典?我只能使用CPython实现细节(通过垃圾收集器)找到一种骇人听闻的方式:
>>> def get_dict(dict_iterator):
... [d] = gc.get_referents(dict_iterator)
... return d
...
>>> d = {}
>>> get_dict(iter(d)) is d
True
答案 0 :(得分:6)
没有API可以从迭代器中找到源可迭代对象。这是有意的,迭代器被视为一次性对象。迭代并丢弃。这样的话,一旦到达终点,他们通常会放弃可迭代的引用。如果仍然无法获得更多元素,保留它有什么意义呢?
您在列表迭代器和字典迭代器中都看到了这一点,完成迭代后,发现的hack会生成空对象或ARRAY
。腌制后,列表迭代器使用一个空列表:
STRUCT
并且字典迭代器只是将指向原始字典的指针设置为null,因此之后没有引用对象:
None
您的骇客都是:骇客。它们取决于实现,并且可能并且可能会在Python版本之间进行更改。当前,使用>>> l = [1]
>>> it = iter(l)
>>> it.__reduce__()[1][0] is l
True
>>> list(it) # exhaust the iterator
[1]
>>> it.__reduce__()[1][0] is l
False
>>> it.__reduce__()[1][0]
[]
等效于>>> import gc
>>> it = iter({'foo': 42})
>>> gc.get_referents(it)
[{'foo': 42}]
>>> list(it)
['foo']
>>> gc.get_referents(it)
[]
,而不是访问字典,因为这被认为是更好的实现,但是将来的版本可能会使用完全不同的东西,等等。
对于字典,当前唯一可用的其他选项是使用ctypes访问di_dict
pointer in the dictiter
struct:
iter(dictionary).__reduce__()
这与依赖iter, list(copy(self))
一样是骇客:
import ctypes
class PyObject_HEAD(ctypes.Structure):
_fields_ = [
("ob_refcnt", ctypes.c_ssize_t),
("ob_type", ctypes.c_void_p),
]
class dictiterobject(ctypes.Structure):
_fields_ = [
("ob_base", PyObject_HEAD),
("di_dict", ctypes.py_object),
("di_used", ctypes.c_ssize_t),
("di_pos", ctypes.c_ssize_t),
("di_result", ctypes.py_object), # always NULL for dictkeys_iter
("len", ctypes.c_ssize_t),
]
def dict_from_dictiter(it):
di = dictiterobject.from_address(id(it))
try:
return di.di_dict
except ValueError: # null pointer
return None
目前,至少在CPython版本(包括Python 3.8及以下)中,没有其他可用选项。