python - intbitset __init__ 导致 SIGSEGV

标签 python

以下代码导致出现段错误。我不太确定为什么...

import numpy as np
from intbitset import intbitset

arr = np.array([1,2,3,4,5])

# This works
intbitset(arr.tolist())
=> intbitset([1, 2, 3, 4, 5])

# This throws SIGSEGV
intbitset([x for x in arr])

[x for x in arr] 完美运行并按预期返回列表。

有人对此有解释吗?在输入 intbitset ctr 之前,列表理解不会被评估为列表吗?

我已经在 Python 3.6.3 和 2.7.13 上进行了测试(需要将 zip 更改为 itertools.izip)。两者都崩溃。 intbitset 版本为 2.3.0

最佳答案

这里有一些有趣的事情:

$ gdb python 
...
(gdb) run crash.py
...
Thread 1 "python" received signal SIGSEGV, Segmentation fault.
0x00007ffff745338b in ?? () from /usr/lib/libpython3.6m.so.1.0
(gdb) bt
#0  0x00007ffff745338b in ?? ()
   from /usr/lib/libpython3.6m.so.1.0
#1  0x00007ffff74a245f in ?? ()
   from /usr/lib/libpython3.6m.so.1.0
#2  0x00007ffff73f0565 in PyList_Append ()
   from /usr/lib/libpython3.6m.so.1.0
#3  0x00007ffff73a2580 in ?? ()
   from /usr/lib/libpython3.6m.so.1.0
#4  0x00007ffff7404b55 in _PyCFunction_FastCallDict ()
   from /usr/lib/libpython3.6m.so.1.0
#5  0x00007ffff740e10f in _PyObject_FastCallDict ()
   from /usr/lib/libpython3.6m.so.1.0
#6  0x00007ffff73fc9d0 in PyFile_WriteObject ()
   from /usr/lib/libpython3.6m.so.1.0
#7  0x00007ffff74a3d6a in PyFile_WriteString ()
   from /usr/lib/libpython3.6m.so.1.0
#8  0x00007ffff74b2f9d in PyTraceBack_Print ()
   from /usr/lib/libpython3.6m.so.1.0
#9  0x00007ffff7491154 in ?? ()
   from /usr/lib/libpython3.6m.so.1.0
#10 0x00007ffff7327d14 in ?? ()
   from /usr/lib/libpython3.6m.so.1.0
#11 0x00007ffff749136e in PyErr_Display ()
   from /usr/lib/libpython3.6m.so.1.0
#12 0x00007ffff74c890a in ?? ()
   from /usr/lib/libpython3.6m.so.1.0
#13 0x00007ffff7404ad0 in _PyCFunction_FastCallDict ()
   from /usr/lib/libpython3.6m.so.1.0
#14 0x00007ffff740e10f in _PyObject_FastCallDict ()
   from /usr/lib/libpython3.6m.so.1.0
#15 0x00007ffff7492c5b in PyErr_PrintEx ()
  3.6m.so.1.0
#16 0x00007ffff74939d1 in PyRun_SimpleFileExFlags ()
   from /usr/lib/libpython3.6m.so.1.0
#17 0x00007ffff748970b in Py_Main ()
   from /usr/lib/libpython3.6m.so.1.0
#18 0x0000555555554c39 in main ()

观察对 PyErr_Display 的调用和 PyTraceBack_Print .看起来 Python 试图显示错误,但在此过程中崩溃了。事实上,这不会崩溃:

try:
    intbitset([x for x in arr])
except Exception as ex:
    print(repr(ex))

相反,它输出以下内容:

ValueError('retrieving integers from rhs is impossible: invalid index to scalar variable.')

引发此异常 here in intbitset.__cinit__ .注意 __cinit__ 是一个特殊的 Cython 函数。

它是为了响应来自 gen_arrtype_subscript 的另一个异常而引发的在 numpy C 代码中。它可以通过像这样索引标量来触发:

>>> import numpy as np
>>> arr = np.array([1,2,3,4,5])
>>> arr[0][0]
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
IndexError: invalid index to scalar variable.

intbitset的原因触发此异常的是 this line :

  tuple_of_tuples = rhs and hasattr(rhs, '__getitem__') and hasattr(rhs[0], '__getitem__')

确实,numpy 标量(在本例中为 numpy.int64)确实有一个 __getitem__ ,他们只是不喜欢你调用它。这导致 intbitset错误地假设它正在接收 sequence made of tuples ,这会触发异常引发调用 __getitem__ .

这解释了为什么如果你传递一个生成器它不会崩溃,intbitset(x for x in arr) : 它没有 __getitem__ ,因此 intbitset 进入了不同的代码路径。如果你通过 intbitset(arr)直接,tuple_of_tuples当尝试转换 arr 时,行会触发另一个异常到 bool :

ValueError: The truth value of an array with more than one element is ambiguous. Use a.any() or a.all()

这个异常是 numpy 的非 Pythonic(与列表转换为 bool 的方式不一致),但这就是它的工作方式。

那么为什么 invalid index to scalar异常导致段错误,即使 truth value of an array不是吗?事实上,如果我把 raise ValueError()之后,两者都崩溃了,所以在这两种情况下显然都发生了未定义的行为,而且有时不会崩溃纯属幸运。

我的猜测是 intbitset通过从 __cinit__ 引发异常来做一些意想不到的事情.它在 Cython 文档中没有明确禁止,所以我不确定如何或什么。

关于python - intbitset __init__ 导致 SIGSEGV,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/49029636/

相关文章:

python - 从 Axes(或Figure)获取 QuadMesh 对象

python - 将 numpy 向量转换为 cvxopt

python - TensorFlow 数据集 API 中的 IDE 断点映射 py_function?

Python 正则表达式模式 * 未按预期工作

函数中的python语法错误

python - JupyterLab/Python/Pandas - 比较两个数据帧

python - PyQtgraph y轴标签非科学计数法

python - 为什么内置的 string.isdigit() 实现比我自己的实现更快?

python - 索引会使 find_one() 更快吗?

python - 将重复的行转换为带有标题的多列