给出字典迭代器,得到字典

问题描述 投票:5回答:1

给出列表迭代器,您可以通过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
python dictionary iterator python-internals
1个回答
0
投票

没有API可以从迭代器中找到源可迭代对象。这是有意的,迭代器被视为一次性对象。迭代并丢弃。这样的话,一旦到达终点,他们通常会删除可迭代的引用;如果仍然无法获得更多元素,保留它有什么意义?

您在列表迭代器和字典迭代器中都看到了,一旦完成迭代,发现的hack会生成空对象或None。列表迭代器在腌制时使用一个空列表:

>>> 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]
[]

并且字典迭代器仅将指向原始字典的指针设置为null,因此之后没有引用对象:

>>> import gc
>>> it = iter({'foo': 42})
>>> gc.get_referents(it)
[{'foo': 42}]
>>> list(it)
['foo']
>>> gc.get_referents(it)
[]

[您的骇客都是:骇客。它们取决于实现,并且可能并且可能会在Python版本之间进行更改。当前,使用iter(dictionary).__reduce__()等效于iter, list(copy(self)),而不是访问字典,因为这被认为是更好的实现,但是将来的版本可能会使用完全不同的东西,等等。

对于词典,当前唯一可用的其他选项是使用ctypes访问di_dict pointer in the dictiter struct

di_dict

与依赖dictiter一样,这是一门骇客:

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

© www.soinside.com 2019 - 2024. All rights reserved.