Может ли 1 + 1 быть равным 3 в питоне?
Прежде чем продолжить, я осознаю, что никогда не следует этого делать. Этот вопрос чисто для образовательных целей; Я предпринял это упражнение, чтобы лучше понять внутренности Python, ctypes
и как они работают.
Я знаю, что в python относительно легко изменить значение целых чисел. На самом деле, вы можете многое сделать, связавшись с внутренностями. Из справочника C API,
Текущая реализация хранит массив целочисленных объектов для всех целых чисел от -5 до 256, когда вы создаете int в этом диапазоне, вы на самом деле просто получаете ссылку на существующий объект. Так что должно быть возможно изменить значение 1. Я подозреваю, что поведение Python в этом случае не определено.:-)
Учитывая, что значение 1 кэшируется CPython, это должно быть относительно легко (или, по крайней мере, возможно) сделать это. Немного покопавшись, я нашел ctypes
был способ пойти. Тем не менее, большая часть того, что я пробую, приводит к segfault. Я приблизился, изменив значение 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
Я пробовал подобные вещи, заканчивающиеся неудачей с Абарнертом internals
модуль. Есть ли способ иметь 1 + 1
оценить 3
в питоне? Или же "1" настолько важен, что невозможно сделать эту работу, не причинив вред моему переводчику?
1 ответ
Отказ от ответственности: этот ответ относится только к CPython; Я мог бы также пропустить суть вопроса...
Я смог (вроде) добиться этого, написав расширение Python на C.
В Objects/intobject.c
есть информационная структура 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