Python переопределяет __init__
Я знаю, что многие люди задают вопросы по этому вопросу, но я не видел, чтобы мой конкретный спросил. При подклассе вы можете переопределить __init__()
так же, как вы можете переопределить любой другой метод. Мой вопрос в том, почему в приведенном ниже примере это не работает корректно:
import random
class MyRand(random.Random):
def __init__(self, myvar1, myvar2, x=None):
# ( ... my code ...)
super(MyRand, self).__init__(x)
Помните, что конструктор Random
имеет следующую подпись: __init__(self, x=None)
где x
- необязательное семя. Я хочу сохранить эту функцию в моем подклассе, но также хочу потребовать две обязательные переменные, myvar1
и myvar2
.
Однако, когда вы пытаетесь создать экземпляр (без семени), вы получите сообщение об ошибке:
MyRand('var1', 'var2')
TypeError: seed expected at most 1 arguments, got 2
Это связано с тем, что python считает, что вы хотите конструктор Random
и передаете ваши два аргумента "var1" и "var2" в качестве семени. Семя (вызываемое изнутри конструктора Random
) требует только 1 аргумент, и поэтому вы получаете сообщение об ошибке.
Однако, если вы делаете
MyRand(myvar1='var1', myvar2='var2')
Это работает, здесь python понимает, что вы передаете ему свои две обязательные переменные и не передаете ему необязательное семя.
Но я думаю, что первый случай тоже должен работать. Что происходит?
Ответы
Ответ 1
В Python вызывается два метода при создании объекта. __new__
и __init__
. Как и многие классы, реализованные в C, random.Random
использует __new__
для инициализации (см. random_new). Вы должны перезаписать его и вызвать его с помощью соответствующих параметров:
import random
class MyRand(random.Random):
def __new__(cls, myvar1, myvar2, x=None):
return random.Random.__new__(cls, x)
def __init__(self, myvar1, myvar2, x=None):
# ( ... my code ...)
Ответ 2
Вы неправильно определили проблему. Проблема в том, что инициализация random.Random
не полностью содержится в random.Random.__init__
. Он также наследует _random.Random.__new__
:
static PyObject *
random_new(PyTypeObject *type, PyObject *args, PyObject *kwds)
{
RandomObject *self;
PyObject *tmp;
if (type == &Random_Type && !_PyArg_NoKeywords("Random()", kwds))
return NULL;
self = (RandomObject *)type->tp_alloc(type, 0);
if (self == NULL)
return NULL;
tmp = random_seed(self, args);
if (tmp == NULL) {
Py_DECREF(self);
return NULL;
}
Py_DECREF(tmp);
return (PyObject *)self;
}
Вам также придется переопределить __new__
и передать только аргумент seed
, который он ожидает, позиционно (потому что он не понимает его как аргумент ключевого слова).
Они действительно не должны смешивать __init__
и __new__
следующим образом. Порядок инициализации становится действительно странным, когда вы это делаете.