Почему Python диктует ключи / значения крякать, как утка? - PullRequest
0 голосов
/ 16 октября 2018

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

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

Ответы [ 3 ]

0 голосов
/ 16 октября 2018

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

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

Или используйте этот оператор:

print(d.keys.__doc__)

В нем четко упоминается, что на выходе есть set-like объект.

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

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

x = ls + list(d.keys())
print(x)
# ['hello', 'foo']
0 голосов
/ 16 октября 2018

Существует явная проверка для типа 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% случаев.

0 голосов
/ 16 октября 2018

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

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

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

ls + list(d.keys()) # returns ['hello', 'foo']
...