python - 1 + 1在python中可以等于3吗?
问题描述
Before carrying on any further, I am aware that one should never do this. This question is purely for educational purposes; I undertook this exercise as a means of better understanding python's internals, the ctypes
, and how they work.
I am aware that it is relatively easy to change the value of integers in python. Actually, there's a whole lot you can do by messing with the internals. From the C API reference,
The current implementation keeps an array of integer objects for all integers between -5 and 256, when you create an int in that range you actually just get back a reference to the existing object. So it should be possible to change the value of 1. I suspect the behaviour of Python in this case is undefined. :-)
考虑到 1 的值被 CPython 缓存,这样做应该相对容易(或者,至少是可能的)。经过一番挖掘,我发现ctypes
这是要走的路。但是,我尝试的大部分内容都会导致段错误。我通过更改 2 的值来接近。
import ctypes
def deref(addr, typ):
return ctypes.cast(addr, ctypes.POINTER(typ))
deref(id(2), ctypes.c_int)[6] = 1
1 + 1 现在给出了不正确的结果(朝着正确方向迈出了一步),但我无法让它评估为“3”:
>>> 1 + 1
1
>>> 1 + 2
1
>>> 1 + 3
[1] 61014 segmentation fault python3.6
我已经尝试过类似的事情,但以 abarnert 的internals
模块失败告终。有没有办法在 python中1 + 1
进行评估?3
还是“1”如此重要以至于如果不给我的解释器造成段错误就无法完成这项工作?
解决方案
免责声明:此答案仅适用于 CPython;我可能也错过了问题的重点......
我能够(有点)通过用 C 编写 Python 扩展来实现这一点。
里面有Objects/intobject.c
一个 info struct PyInt_Type
。它的tp_as_number
字段是一个运算符函数表,nb_add
其中的字段是加法运算符:
// the function in the same file that nb_add points to
static PyObject *
int_add(PyIntObject *v, PyIntObject *w)
...
PyInt_Type
是一个公开的全局变量,可以dlsym
在 Unix / GetProcAddress
WinAPI 中检索:
#include <dlfcn.h>
...
// symbol look-up from the Python extension
void* addr = dlsym(RTLD_DEFAULT, "PyInt_Type");
// pointer to PyInt_Type
PyTypeObject *int_type = addr;
// pointer to int_as_number (PyInt_Type.tp_as_number)
PyNumberMethods *int_funcs = int_type->tp_as_number;
// pointer to int_add (tp_as_number->nb_add)
int_add_orig = int_funcs->nb_add;
// override this with a custom function
int_funcs->nb_add = (binaryfunc)int_add_new;
...
// custom add function
PyObject *int_add_new(PyIntObject *v, PyIntObject *w)
{
long a = PyInt_AS_LONG(v);
long b = PyInt_AS_LONG(w);
// 1 + 1 = 3 special case
if (a == 1 && b == 1) {
return PyInt_FromLong(3);
}
// for all other cases default to the
// original add function which was retrieved earlier
return int_add_orig((PyObject *)v, (PyObject *)w);
}
通过保留所有原始代码和内部变量,新代码避免了之前遇到的段错误:
>>> # load the extension
>>> import [...]
>>> 1 + 1
2
>>> # call the extension function which overloads the add operator
>>> 1 + 1
3
>>> 1 + 0
1
>>> 1 + 2
3
>>> 1 + 3
4
推荐阅读
- python - 将数据框列表组合成一个新的数据框
- bash - 使用 sed 在文件中添加 sed 行
- javascript - 基于位置选择的着陆页重定向(Cookie?)
- c++ - 抽象类接口可以模板化吗
- r - 哪个正则表达式从文本中的引号中删除标点符号
- python - 如何将具有字符串和整数值的多个数据帧转换为 4D 数据结构
- reactjs - React 如何修复“滑动解锁”错误消息?
- javascript - 自定义元素原型中的覆盖函数会更改 this 对象
- reactjs - 禁用按钮的自定义样式也适用于常规按钮
- javascript - 如何对在画布上绘制的任意用户提交的 Javascript 代码进行沙箱处理?