给定字典迭代器,获取字典

时间:2019-10-02 19:54:37

标签: python dictionary iterator python-internals

使用列表迭代器,您可以通过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

1 个答案:

答案 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及以下)中,没有其他可用选项。