给定一个字典迭代器,获取字典
Given a dict iterator, get the dict
给定一个列表迭代器,你可以通过 pickle 协议找到原始列表:
>>> L = [1, 2, 3]
>>> Li = iter(L)
>>> Li.__reduce__()[1][0] is L
True
给定一个字典迭代器,你如何找到原始字典?我只能找到一种使用 CPython 实现细节的 hacky 方法(通过垃圾收集器):
>>> def get_dict(dict_iterator):
... [d] = gc.get_referents(dict_iterator)
... return d
...
>>> d = {}
>>> get_dict(iter(d)) is d
True
没有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
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
这与依赖 gc.get_referents()
:
一样多
>>> d = {'foo': 42}
>>> it = iter(d)
>>> dict_from_dictiter(it)
{'foo': 42}
>>> dict_from_dictiter(it) is d
True
>>> list(it)
['foo']
>>> dict_from_dictiter(it) is None
True
目前,至少在 Python 3.8 及之前的 CPython 版本中,没有其他选项可用。
给定一个列表迭代器,你可以通过 pickle 协议找到原始列表:
>>> L = [1, 2, 3]
>>> Li = iter(L)
>>> Li.__reduce__()[1][0] is L
True
给定一个字典迭代器,你如何找到原始字典?我只能找到一种使用 CPython 实现细节的 hacky 方法(通过垃圾收集器):
>>> def get_dict(dict_iterator):
... [d] = gc.get_referents(dict_iterator)
... return d
...
>>> d = {}
>>> get_dict(iter(d)) is d
True
没有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
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
这与依赖 gc.get_referents()
:
>>> d = {'foo': 42}
>>> it = iter(d)
>>> dict_from_dictiter(it)
{'foo': 42}
>>> dict_from_dictiter(it) is d
True
>>> list(it)
['foo']
>>> dict_from_dictiter(it) is None
True
目前,至少在 Python 3.8 及之前的 CPython 版本中,没有其他选项可用。