为什么没有__getitem__引发TypeError

时间:2016-05-09 19:27:08

标签: python

所以问题很简单: 如果我们有一个随机类,让我们说一个int,我们尝试访问一个未定义的属性:

my_int = 5
my_int.this_is_a_test

我们会收到此错误:

AttributeError: 'int' object has no attribute 'this_is_a_test'

但是如果我们尝试访问它的索引(在这种情况下Python会查找__getitem__属性):

my_int = 5
my_int[0]

我们得到:

TypeError: 'int' object has no attribute '__getitem__'

异常类型更改背后的逻辑是什么?对我来说,TypeError被引发似乎很奇怪,抱怨缺少属性(AttributeError似乎是一个更好的候选者)

2 个答案:

答案 0 :(得分:3)

这取决于你的意图。

In [1]: my_int = 5

In [2]: my_int.__getitem__(0)  # -> AttributeError

In [3]: my_int[0]  # -> TypeError

当您使用.时,您隐式调用getattr函数,如果该属性不存在,则自然会引发AttributeError

更新2 。让我们看一下字节码。

In [11]: import dis

In [12]: def via_operator():
             my_int = 5
             my_int[0]


In [13]: def via_getattr():
             my_int = 5
             my_int.__getitem__(0)

In [14]: dis.dis(via_operator)
  2           0 LOAD_CONST               1 (5)
              3 STORE_FAST               0 (my_int)

  3           6 LOAD_FAST                0 (my_int)
              9 LOAD_CONST               2 (0)
             12 BINARY_SUBSCR       
             13 POP_TOP             
             14 LOAD_CONST               0 (None)
             17 RETURN_VALUE        

In [15]: dis.dis(via_getattr)
  2           0 LOAD_CONST               1 (5)
              3 STORE_FAST               0 (my_int)

  3           6 LOAD_FAST                0 (my_int)
              9 LOAD_ATTR                0 (__getitem__)
             12 LOAD_CONST               2 (0)
             15 CALL_FUNCTION            1
             18 POP_TOP             
             19 LOAD_CONST               0 (None)
             22 RETURN_VALUE   

如您所见,[]有一个特殊的虚拟机指令。来自docs

  

BINARY_SUBSCR:实现TOS = TOS1 [TOS]。

因此,当您执行指令失败时,提出TypeError是很自然的。

更新1 :查看getattr来源,很清楚此功能永远不会引发TypeError,因此[]操作员并没有在引擎盖下调用它(至少对于内置类型,尽管找到源代码来澄清这一点比较好)。

static PyObject *
builtin_getattr(PyObject *self, PyObject *args)
{
    PyObject *v, *result, *dflt = NULL;
    PyObject *name;

    if (!PyArg_UnpackTuple(args, "getattr", 2, 3, &v, &name, &dflt))
        return NULL;
#ifdef Py_USING_UNICODE
    if (PyUnicode_Check(name)) {
        name = _PyUnicode_AsDefaultEncodedString(name, NULL);
        if (name == NULL)
            return NULL;
    }
#endif

    if (!PyString_Check(name)) {
        PyErr_SetString(PyExc_TypeError,
                        "getattr(): attribute name must be string");
        return NULL;
    }
    result = PyObject_GetAttr(v, name);
    if (result == NULL && dflt != NULL &&
        PyErr_ExceptionMatches(PyExc_AttributeError))
    {
        PyErr_Clear();
        Py_INCREF(dflt);
        result = dflt;
    }
    return result;
}

答案 1 :(得分:1)

AttributeError的{​​{1}}可能会产生误导,因为您没有尝试访问my_int[0]属性,而您正试图访问项目。引发my_int是因为TypeError不支持下标,并且此异常消息已在Python 3.X中更新。

话虽如此,但int没有AttributeError是不合适的。我怀疑这可能是__getitem__,因为数字(TypeErrorintfloat)是唯一不支持下标的内置数据类型。

当出现此错误时,您会认为long包含不同类型的对象,因此my_int