Question: There's a situation involving sub-classing I can't figure out. I'm sub-classing Random (the reason is besides the point). Here's a basic example of what I have: import random class MyRandom(random.Random): def __init__(self, x): # x isn't used here, but it's necessary to show the problem. print("Before") super().__init__() # Nothing passed to parent print("After") MyRandom([])  The above code, when run, gives the following error (and doesn't print "Before"): >>> import test Traceback (most recent call last): File "<stdin>", line 1, in <module> File "C:\Users\_\PycharmProjects\first\test.py", line 11, in <module> MyRandom([]) TypeError: unhashable type: 'list'  To me, this doesn't make any sense. Somehow, the argument to MyRandom is apparently being passed directly to Random.__init__ even though I'm not passing it along, and the list is being treated as a seed. "Before" never prints, so apparently my initializer is never even being called. I thought maybe this was somehow due to the parent of Random being implemented in C and this was causing weirdness, but a similar case with list sub-classing doesn't yield an error saying that ints aren't iterable: class MyList(list): def __init__(self, y): print("Before") super().__init__() print("After") r = MyList(2) # Prints "Before", "After"   I have no clue how to even approach this. I rarely ever sub-class, and even rarer is it that I sub-class a built-in, so I must have developed a hole in my knowledge. This is not how I expect sub-classing to work. If anyone can explain what's going on here, I'd appreciate it.  Python 3.9

Answer: Instantiating a class causes its __new__ method to be called. It is passed the name of the class and the arguments in the constructor call. So MyRandom([1, 2]) results in the call MyRandom.__new__(MyRandom, [1, 2]). (3.9.10 documentation). Because there isn't a MyRandom.__new__() method, the base classes are searched. random.Random does have a __new__() method (see random_new() in _randommodule.c). So we get a call something like this random_new(MyRandom, [1, 2]). Looking at the C code for random_new(), it calls random_seed(self, [1, 2]). Because the second argument isn't Null, or None, or an int, or a subclass of int, the code calls PyObject_Hash([1, 2]). But a list isn't hashable, hence the error. If __new__() returns a instance of the class, then the __init__() method is called with the arguments in the constuctor call. One possible fix is to define a MyRandom.__new__() method, which calls super().__new__() but only passes the appropriate args. class MyRandom(random.Random): def __new__(cls, *args, **kwargs): #print(f"In __new__: {args=}, {kwargs=}") # Random.__new__ expects an optional seed. We are going to # implement out own RNG, so ignore args and kwargs. Pass in a # junk integer value so that Random.__new__ doesn't waste time # trying to access urandom or calling time to initialize the MT RNG # since we aren't going to use it anyway. return super().__new__(cls, 123) def __init__(cls, *args, **kwargs): #print(f"In __init__: {args=}, {kwargs=}") # initialize your custom RNG here pass  Also override the methods: random(), seed(), getstate(), setstate(), and optionally getrandbits(). An alternative fix is to only use keyword arguments in the __init__() methods of the subclasses. The C code for random_new() checks to see if a an instance of random.Random is being created. If true, the code throws and error if there are any keyword arguments. However, if a subclass is being created, any keyword arguments are ignored by random_new(), but can be used in the subclass __init__(). class MyRandom(random.Random): def __init__(self, *, x): # make x a keyword only argument print("Before") super().__init__() # Nothing passed to parent print("After") MyRandom(x=[])  Interestingly, in Python 3.10, the code for random_new has been changed to raise an error if more that 1 positional argument is supplied.