- html - 出于某种原因,IE8 对我的 Sass 文件中继承的 html5 CSS 不友好?
- JMeter 在响应断言中使用 span 标签的问题
- html - 在 :hover and :active? 上具有不同效果的 CSS 动画
- html - 相对于居中的 html 内容固定的 CSS 重复背景?
class foo:
def __init__(self, data):
self.data = data
def __len__(self):
return self.data
如果我通过为 data
传递一个字符串来运行它,那么在对此类的实例调用 len
时会出现错误。具体来说,我得到了 'str' object cannot be interpreted as an integer
。
那么__len__
中的return
语句是否必须是一个整数?我想如果我覆盖它,它应该能够输出我想要的任何东西,那为什么这不可能呢?
最佳答案
简短回答
在 C 级,Python 将 __len__
插入到一个特殊槽中,该槽捕获对 __len__
调用的输出,并对其进行一些验证以确保它是正确的.
长答案
为了回答这个问题,我们必须深入了解当 len
时幕后发生的事情。在 Python 中调用。
首先,让我们建立一些行为。
>>> class foo:
... def __init__(self, data):
... self.data = data
... def __len__(self):
... return self.data
...
>>> len(foo(-1))
Traceback:
...
ValueError: __len__() should return >= 0
>>> len(foo('5'))
Traceback:
...
TypeError: 'str' object cannot be interpreted as an integer
>>> len(foo(5))
5
当您调用 len
时, C 函数 builtin_len
被调用。让我们来看看这个。
static PyObject *
builtin_len(PyObject *module, PyObject *obj)
/*[clinic end generated code: output=fa7a270d314dfb6c input=bc55598da9e9c9b5]*/
{
Py_ssize_t res;
res = PyObject_Size(obj); // <=== THIS IS WHAT IS IMPORTANT!!!
if (res < 0 && PyErr_Occurred())
return NULL;
return PyLong_FromSsize_t(res);
}
您会注意到 PyObject_Size
正在调用函数 - 此函数将返回任意 Python 对象的大小。让我们进一步深入兔子洞。
Py_ssize_t
PyObject_Size(PyObject *o)
{
PySequenceMethods *m;
if (o == NULL) {
null_error();
return -1;
}
m = o->ob_type->tp_as_sequence;
if (m && m->sq_length)
return m->sq_length(o); // <==== THIS IS WHAT IS IMPORTANT!!!
return PyMapping_Size(o);
}
它检查类型是否定义了 sq_length
函数(序列长度),如果是,则调用它来获取长度。似乎在 C 级别,Python 将定义 __len__
的所有对象归类为序列或映射(即使这不是我们在 Python 级别对它们的看法);在我们的例子中,Python 认为这个类是一个序列,所以它调用 sq_length
。
让我们快速回顾一下:对于内置类型(例如 list
、 set
等),Python 实际上并不调用函数来计算长度,而是访问存储在 C 结构中的值,使得这个非常快。这些内置类型中的每一个都定义了如何通过将访问器方法分配给 sq_length
来访问它。让我们快速浏览一下how this is implemented for lists :
static Py_ssize_t
list_length(PyListObject *a)
{
return Py_SIZE(a); // <== THIS IS A MACRO for (PyVarObject*) a->ob_size;
}
static PySequenceMethods list_as_sequence = {
...
(lenfunc)list_length, /* sq_length */
...
};
ob_size
存储对象的大小(即列表中的元素数)。因此,当sq_length
被调用时,它被发送到list_length
函数以获取ob_size
的值。
好的,这就是内置类型的处理方式……它如何处理像我们的 foo
这样的自定义类?由于“dunder 方法”(例如 __len__
)很特殊,Python 会在我们的类中检测到它们并对其进行特殊处理(具体来说,将它们插入特殊的槽中)。
其中大部分在 typeobject.c 中处理. __len__
函数被拦截并分配给 sq_length
槽(就像一个内置函数!)near the bottom of the file .
SQSLOT("__len__", sq_length, slot_sq_length, wrap_lenfunc,
"__len__($self, /)\n--\n\nReturn len(self)."),
slot_sq_length
功能是我们可以最终回答您的问题的地方。
static Py_ssize_t
slot_sq_length(PyObject *self)
{
PyObject *res = call_method(self, &PyId___len__, NULL);
Py_ssize_t len;
if (res == NULL)
return -1;
len = PyNumber_AsSsize_t(res, PyExc_OverflowError); // <=== HERE!!!
Py_DECREF(res);
if (len < 0) { // <== AND HERE!!!
if (!PyErr_Occurred())
PyErr_SetString(PyExc_ValueError,
"__len__() should return >= 0");
return -1;
}
return len;
}
这里有两点需要注意:
ValueError
消息 "__len__() should return >= 0"
。这正是我尝试调用 len(foo(-1))
时收到的错误!__len__
的返回值强制转换为 Py_ssize_t
(Py_ssize_t
是一个已签名 size_t
的版本,它就像一种特殊类型的整数,保证能够索引容器中的内容)。OK,我们来看PyNumber_AsSsize_t
的实现.它有点长,所以我将省略不相关的内容。
Py_ssize_t
PyNumber_AsSsize_t(PyObject *item, PyObject *err)
{
Py_ssize_t result;
PyObject *runerr;
PyObject *value = PyNumber_Index(item);
if (value == NULL)
return -1;
/* OMITTED FOR BREVITY */
此处的相关位在 PyNumber_Index
中,Python 使用它来将任意对象转换为适合索引的整数。 这里是您问题的实际答案所在。我已经做了一些注释。
PyObject *
PyNumber_Index(PyObject *item)
{
PyObject *result = NULL;
if (item == NULL) {
return null_error();
}
if (PyLong_Check(item)) { // IS THE OBJECT ALREADY AN int? IF SO, RETURN IT NOW.
Py_INCREF(item);
return item;
}
if (!PyIndex_Check(item)) { // DOES THE OBJECT DEFINE __index__? IF NOT, FAIL.
PyErr_Format(PyExc_TypeError,
"'%.200s' object cannot be interpreted "
"as an integer", item->ob_type->tp_name);
return NULL;
}
result = item->ob_type->tp_as_number->nb_index(item);
if (!result || PyLong_CheckExact(result))
return result;
if (!PyLong_Check(result)) { // IF __index__ DOES NOT RETURN AN int, FAIL.
PyErr_Format(PyExc_TypeError,
"__index__ returned non-int (type %.200s)",
result->ob_type->tp_name);
Py_DECREF(result);
return NULL;
}
/* Issue #17576: warn if 'result' not of exact type int. */
if (PyErr_WarnFormat(PyExc_DeprecationWarning, 1,
"__index__ returned non-int (type %.200s). "
"The ability to return an instance of a strict subclass of int "
"is deprecated, and may be removed in a future version of Python.",
result->ob_type->tp_name)) {
Py_DECREF(result);
return NULL;
}
return result;
}
根据您收到的错误,我们可以看到 '5'
没有定义 __index__
。我们可以自己验证:
>>> '5'.__index__()
Traceback:
...
AttributeError: 'str' object has no attribute '__index__'
关于python - 调用len时Python如何保证__len__的返回值是整数?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/42521449/
我正在处理一组标记为 160 个组的 173k 点。我想通过合并最接近的(到 9 或 10 个组)来减少组/集群的数量。我搜索过 sklearn 或类似的库,但没有成功。 我猜它只是通过 knn 聚类
我有一个扁平数字列表,这些数字逻辑上以 3 为一组,其中每个三元组是 (number, __ignored, flag[0 or 1]),例如: [7,56,1, 8,0,0, 2,0,0, 6,1,
我正在使用 pipenv 来管理我的包。我想编写一个 python 脚本来调用另一个使用不同虚拟环境(VE)的 python 脚本。 如何运行使用 VE1 的 python 脚本 1 并调用另一个 p
假设我有一个文件 script.py 位于 path = "foo/bar/script.py"。我正在寻找一种在 Python 中通过函数 execute_script() 从我的主要 Python
这听起来像是谜语或笑话,但实际上我还没有找到这个问题的答案。 问题到底是什么? 我想运行 2 个脚本。在第一个脚本中,我调用另一个脚本,但我希望它们继续并行,而不是在两个单独的线程中。主要是我不希望第
我有一个带有 python 2.5.5 的软件。我想发送一个命令,该命令将在 python 2.7.5 中启动一个脚本,然后继续执行该脚本。 我试过用 #!python2.7.5 和http://re
我在 python 命令行(使用 python 2.7)中,并尝试运行 Python 脚本。我的操作系统是 Windows 7。我已将我的目录设置为包含我所有脚本的文件夹,使用: os.chdir("
剧透:部分解决(见最后)。 以下是使用 Python 嵌入的代码示例: #include int main(int argc, char** argv) { Py_SetPythonHome
假设我有以下列表,对应于及时的股票价格: prices = [1, 3, 7, 10, 9, 8, 5, 3, 6, 8, 12, 9, 6, 10, 13, 8, 4, 11] 我想确定以下总体上最
所以我试图在选择某个单选按钮时更改此框架的背景。 我的框架位于一个类中,并且单选按钮的功能位于该类之外。 (这样我就可以在所有其他框架上调用它们。) 问题是每当我选择单选按钮时都会出现以下错误: co
我正在尝试将字符串与 python 中的正则表达式进行比较,如下所示, #!/usr/bin/env python3 import re str1 = "Expecting property name
考虑以下原型(prototype) Boost.Python 模块,该模块从单独的 C++ 头文件中引入类“D”。 /* file: a/b.cpp */ BOOST_PYTHON_MODULE(c)
如何编写一个程序来“识别函数调用的行号?” python 检查模块提供了定位行号的选项,但是, def di(): return inspect.currentframe().f_back.f_l
我已经使用 macports 安装了 Python 2.7,并且由于我的 $PATH 变量,这就是我输入 $ python 时得到的变量。然而,virtualenv 默认使用 Python 2.6,除
我只想问如何加快 python 上的 re.search 速度。 我有一个很长的字符串行,长度为 176861(即带有一些符号的字母数字字符),我使用此函数测试了该行以进行研究: def getExe
list1= [u'%app%%General%%Council%', u'%people%', u'%people%%Regional%%Council%%Mandate%', u'%ppp%%Ge
这个问题在这里已经有了答案: Is it Pythonic to use list comprehensions for just side effects? (7 个答案) 关闭 4 个月前。 告
我想用 Python 将两个列表组合成一个列表,方法如下: a = [1,1,1,2,2,2,3,3,3,3] b= ["Sun", "is", "bright", "June","and" ,"Ju
我正在运行带有最新 Boost 发行版 (1.55.0) 的 Mac OS X 10.8.4 (Darwin 12.4.0)。我正在按照说明 here构建包含在我的发行版中的教程 Boost-Pyth
学习 Python,我正在尝试制作一个没有任何第 3 方库的网络抓取工具,这样过程对我来说并没有简化,而且我知道我在做什么。我浏览了一些在线资源,但所有这些都让我对某些事情感到困惑。 html 看起来
我是一名优秀的程序员,十分优秀!