Reputation:
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?
Upvotes: 13
Views: 7155
Reputation: 42768
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 ...)
Upvotes: 11
Reputation: 281476
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.
Upvotes: 8