I know that plenty of people ask questions on this subject, but I haven't seen my specific one asked. When subclassing you can override __init__() the same way that you can override any other method. My question is why in the example below this doesn't seem to be working correctly:
import random
class MyRand(random.Random):
def __init__(self, myvar1, myvar2, x=None):
# ( ... my code ...)
super(MyRand, self).__init__(x)
Remember that Random's constructor has the following signature: __init__(self, x=None) where x is an optional seed. I want to keep that feature in my subclass, but also I want to require two mandatory variables, myvar1 and myvar2.
However, when you try and instantiate (without a seed) you get an error:
MyRand('var1', 'var2')
TypeError: seed expected at most 1 arguments, got 2
This is because python thinks you want Random's constructor and passes your two arguments 'var1' and 'var2' as the seed. The seed (which is called from inside Random's constructor) only wants 1 argument, and so you get an error.
However, if you do
MyRand(myvar1='var1', myvar2='var2')
This works, here python understands that you're passing it your two mandatory variables and not passing it the optional seed.
But I think the first case should work too. What's going on?
2 Answers 2
In Python two methods are called when a object is created. __new__ and __init__. Like many classes implemented in C, random.Random uses __new__ to initialize itself (see random_new). You have to overwrite it and call it with the appropriate parameters:
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 Comments
random.Random itself doesn't implement a __new__; it's inheriting one from _random.Random. Note the underscore. random.Random implements an __init__ method, making it really awkward to work with because you have to override both, and because the initialization order can get really weird.You've mis-diagnosed the problem a little. The problem is that random.Random's initialization isn't entirely contained in random.Random.__init__. It also inherits _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;
}
You're going to have to override __new__, too, and only pass the seed argument it expects, positionally (because it doesn't understand it as a keyword argument).
They really shouldn't be mixing __init__ and __new__ like this. Initialization order gets really weird when you do that.
4 Comments
random.list and dict only set up an empty object in __new__ and do all the initialization in __init__, while types like tuple and int do all the initialization in __new__ and don't implement their own __init__. Almost no classes examine their arguments in both __init__ and __new__.
superin Python 3 haven't any necessary arguments.Exception. Time to have a closer look to those obscure__xxxx__members.__init__is never executed, and sosuperisn't either.