Как встроенные типы защищены от перезаписи (присвоения) их методов? - PullRequest
0 голосов
/ 01 мая 2018

Я заметил, что

int.__str__ = lambda x: pass

выдает ошибку.

Я понимаю, почему это запрещено. Но как? Могу ли я использовать это в «нормальном» коде?

1 Ответ

0 голосов
/ 01 мая 2018

Для установки атрибутов непосредственно на самом int и других встроенных типах (а не на их экземплярах) эта защита применяется в type.__setattr__, которая специально запрещает установку атрибутов для встроенных типов:

static int
type_setattro(PyTypeObject *type, PyObject *name, PyObject *value)
{
    int res;
    if (!(type->tp_flags & Py_TPFLAGS_HEAPTYPE)) {
        PyErr_Format(
            PyExc_TypeError,
            "can't set attributes of built-in/extension type '%s'",
            type->tp_name);
        return -1;
    }
    ...

Py_TPFLAGS_HEAPTYPE - это флаг, который указывает, был ли тип определен в Python, а не в C.


Вы не можете делать то же самое с вашими собственными классами, если не реализуете их в C. Вы можете сделать вид, что делаете это, написав метакласс с пользовательским __setattr__, но это усложняет использование других полезных метаклассов, и это по-прежнему не мешает кому-то напрямую вызывать type.__setattr__ на ваших занятиях. (Попытка подобного трюка с object.__setattr__(int, ...) не работает, потому что есть специальная проверка , чтобы поймать это.)


Вы не спрашивали об экземплярах встроенных типов, но они также интересны. Экземпляры большинства встроенных типов не могут иметь атрибуты, установленные на них просто потому, что такие атрибуты некуда поместить - нет __dict__. Вместо того, чтобы иметь специальную «не разрешенную настройку» __setattr__ или не иметь __setattr__, они обычно наследуют __setattr__ от object, который знает, как обрабатывать объекты без __dict__:

descr = _PyType_Lookup(tp, name);

if (descr != NULL) {
    Py_INCREF(descr);
    f = descr->ob_type->tp_descr_set;
    if (f != NULL) {
        res = f(descr, obj, value);
        goto done;
    }
}

if (dict == NULL) {
    dictptr = _PyObject_GetDictPtr(obj);
    if (dictptr == NULL) {
        if (descr == NULL) {
            PyErr_Format(PyExc_AttributeError,
                         "'%.100s' object has no attribute '%U'",
                         tp->tp_name, name);
        }
        else {
            PyErr_Format(PyExc_AttributeError,
                         "'%.50s' object attribute '%U' is read-only",
                         tp->tp_name, name);
        }
        goto done;
    }
    ...
...