13

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?

Matthias
13.3k6 gold badges45 silver badges50 bronze badges
asked Jan 5, 2017 at 18:11
6
  • super in Python 3 haven't any necessary arguments. Commented Jan 5, 2017 at 18:15
  • 5
    @ADR: that's neither here nor there on this question. Commented Jan 5, 2017 at 18:16
  • I don't understand the downvote on that question... Commented Jan 5, 2017 at 18:22
  • Yes, I just read the answers. I stumbled across the same behaviour when inheriting Exception. Time to have a closer look to those obscure __xxxx__ members. Commented Jan 5, 2017 at 18:23
  • @Jean-FrançoisFabre What you said is true, but does not apply here. In my sample code, my __init__ is never executed, and so super isn't either. Commented Jan 5, 2017 at 18:25

2 Answers 2

11

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 ...)
answered Jan 5, 2017 at 18:18
Sign up to request clarification or add additional context in comments.

2 Comments

thanks. I'm going to mark yours as correct. But for the sake of completeness I would suggest that you edit your answer to contain the link that user2357112 mentioned. It's useful to see the real thing in addition to being given the answer.
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.
8

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.

answered Jan 5, 2017 at 18:20

4 Comments

Is this because it's a class defined in the C or is it just a weird peculiarity of random?
@SeanMcSomething: Just a weird implementation detail of random.
@user2357112: no, most classes implemented in C behave like this.
@Daniel: Almost no classes behave like this, whether implemented in C or Python. Types like 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__.

Your Answer

Draft saved
Draft discarded

Sign up or log in

Sign up using Google
Sign up using Email and Password

Post as a guest

Required, but never shown

Post as a guest

Required, but never shown

By clicking "Post Your Answer", you agree to our terms of service and acknowledge you have read our privacy policy.