Почему Python диктует ключи / значения крякать, как утка?

1

Python является утка набран, и, как правило это позволяет избежать литья Faff при работе с примитивными объектами.

Канонический пример (и причина имени) - это тест утки: если он похож на утку, плавает, как утка, и шарлатанцы, как утка, то это, вероятно, утка.

Однако одним из примечательных исключений являются ключи/значения dict, которые выглядят как утка и плавают, как утка, но, в частности, не уклоняются, как утка.

>>> ls = ['hello']
>>> d = {'foo': 'bar'}
>>> for key in d.keys():
..      print(key)
..
'foo'
>>> ls + d.keys()
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
TypeError: can only concatenate list (not "dict_keys") to list

Может кто-нибудь просветить меня, почему это так?

  • 0
    en.wikipedia.org/wiki/Duck_typing
  • 4
    ls + list(d.keys()) ?
Показать ещё 2 комментария
Теги:
language-design

3 ответа

1
Лучший ответ

Существует явная проверка типа list (или его дочерних элементов) в исходном коде на языке python (так что даже tuple не подходит):

static PyObject *
list_concat(PyListObject *a, PyObject *bb)
{
    Py_ssize_t size;
    Py_ssize_t i;
    PyObject **src, **dest;
    PyListObject *np;
    if (!PyList_Check(bb)) {
        PyErr_Format(PyExc_TypeError,
                  "can only concatenate list (not \"%.200s\") to list",
                  bb->ob_type->tp_name);
        return NULL;
    }

поэтому python может быстро вычислить размер и перераспределить результат, не пробовав все контейнеры или итерации с правой стороны, чтобы узнать, обеспечивая очень быстрое добавление списка.

#define b ((PyListObject *)bb)
    size = Py_SIZE(a) + Py_SIZE(b);
    if (size < 0)
        return PyErr_NoMemory();
    np = (PyListObject *) PyList_New(size);
    if (np == NULL) {
        return NULL;
    }

Один из способов решения этой проблемы - использовать расширение/добавление на месте:

my_list += my_dict  # adding .keys() is useless

потому что в этом случае на месте добавить итерации с правой стороны: так каждая коллекция квалифицируется.

(или, конечно же, итерацию правой руки: + list(my_dict))

Таким образом, он может принимать любой тип, но я подозреваю, что создатели python не считают это стоящим и удовлетворены простой и быстрой реализацией, которая используется в 99% случаев.

7

Клавиши Dict на самом деле реализуют заданный интерфейс, а не список, поэтому вы можете выполнять заданные операции с ключами dict непосредственно с другими наборами:

d.keys() & {'foo', 'bar'} # returns {'foo'}

Но он не реализует __getitem__, __setitem__, __delitem__ и insert, которые необходимы для "quack" как список, поэтому он не может выполнять какие-либо операции с списком, не будучи явно преобразованным в список:

ls + list(d.keys()) # returns ['hello', 'foo']
  • 1
    но вы не можете __getitem__ list + dict и dict реализует __getitem__ : >>> d.__getitem__ <built-in method __getitem__ of dict object at 0x0000000002FD9048> . Тот факт, что __getitem__ не определен, не является причиной.
  • 0
    @ Jean-FrançoisFabre: в диктовке отсутствуют другие методы Sequence, которые требуются для обработки объекта в виде списка (например, __reversed__ ). Таблица на docs.python.org/3/library/… является полезной ссылкой.
Показать ещё 3 комментария
0

Если вы перейдете к определению d.keys() вы увидите следующее.

def keys(self): # real signature unknown; restored from __doc__
    """ D.keys() -> a set-like object providing a view on D keys """
    pass

Или используйте это утверждение:

print(d.keys.__doc__)

В нем четко говорится, что выход, set-like объект.

Теперь вы пытаетесь добавить набор в список.

Вам нужно преобразовать набор в список, а затем добавить его.

x = ls + list(d.keys())
print(x)
# ['hello', 'foo']

Ещё вопросы

Сообщество Overcoder
Наверх
Меню