Может ли 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
Другие вопросы по тегам