Python是duck typed,通常可以避免在处理原始对象时使用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 :(得分:7)
Dict键实际上是实现集合的界面而不是列表的接口,因此您可以直接与其他集合一起使用dict键执行集合操作:
d.keys() & {'foo', 'bar'} # returns {'foo'}
但是它没有实现__getitem__
,__setitem__
,__delitem__
和insert
方法,这些方法需要像列表一样“嘎嘎”,所以它不能执行任何列表操作,而无需先将其显式转换为列表:
ls + list(d.keys()) # returns ['hello', 'foo']
答案 1 :(得分:1)
在python源代码中对list
类型(或其子元素)进行了明确检查(因此,即使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%的时间都使用的简单,快速的实现感到满意。 / p>
答案 2 :(得分:0)
如果您进入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']