Python的reversed()函数如何工作?

时间:2018-07-04 22:07:31

标签: python cpython python-internals

根据Python's docs,如果未实现reversed()__getitem__使用__len____reversed__

我遇到了奇怪的行为,但无法解释:

>>> class A(dict):
...     pass
...
>>> reversed(A())
Traceback (most recent call last):
    ...
TypeError: 'A' object is not reversible

>>> class B(dict):
...     def __getitem__(self, key):
...         return super().__getitem__(key)
...     def __len__(self):
...         return super().__len__()
...
>>> reversed(B())
Traceback (most recent call last):
    ...
TypeError: 'B' object is not reversible

>>> class C:
...     def __getitem__(self, key):
...         return "item"
...     def __len__(self):
...         return 1
...
>>> reversed(C())
<reversed object at 0x00000000022BB9B0>

尽管在映射类型上调用reversed()毫无意义,但它怎么知道它是映射呢?是否在内部检查isinstance(inst, dict)?是否检查任何常规映射,例如collections.abc.Mapping?有没有办法在不实现__reversed__的情况下覆盖此行为?

我认为这可能是由于dict实现了抛出__reversed__的{​​{1}}或等于TypeError的原因,就像禁用None一样,但__hash__却是空的,抛出了dict.__reversed__

2 个答案:

答案 0 :(得分:7)

是的,在PySequence_Check used by reversed中有dict类型的检查。

// cpython/Objects/enumobject.c
if (!PySequence_Check(seq)) {
    PyErr_Format(PyExc_TypeError,
                 "'%.200s' object is not reversible",
                 Py_TYPE(seq)->tp_name);
    return NULL;
}


// cpython/Objects/abstract.c

int
PySequence_Check(PyObject *s)
{
    if (PyDict_Check(s))
        return 0;
    return s != NULL && s->ob_type->tp_as_sequence &&
        s->ob_type->tp_as_sequence->sq_item != NULL;
}

答案 1 :(得分:0)

此函数在 PEP-322 中通过 python 实现:

def reversed(x):
    if hasattr(x, 'keys'):
        raise ValueError("mappings do not support reverse iteration")
    i = len(x)
    while i > 0:
        i -= 1
        yield x[i]