Why Python allows classes to inherit the class with exact same name?

Hi there :wave:

I noticed (from an accidental typo) that I could inherit a class with the exact same name.

class A:
    x = 0
    def __init__(self):
        print(self.x)

class A(A):
    x = -1
    def __init__(self):
        ## A.__init__(self) #=> RecursionError
        super().__init__()

This works correctly as far as we use super, and I thought this can be useful to customize other libraries, however, I found one thing strange.

>>> print(A.__mro__)
(<class '__main__.A'>, <class '__main__.A'>, <class 'object'>)

As of now, I think that statements such as class A(A) should be avoided.
Is there any reason for this being accepted?
Or do you come up with something wrong that this causes?

1 Like

Yes, you are probably correct that most of the time you should avoid
writing a class that inherits from another class with the same name.
Even though it is perfectly safe if you know what you are doing, and use
super, that sort of name reuse may be confusing.

As for why it is allowed, it isn’t a specific decision to allow it. It
is just a consequence of the way names and objects work in Python.

1 Like

As an example, this is also legal Python:

x = 2
x = x * 2

Trite, but the same thing. Binding the existing name “x” to a new
object.

Cheers,
Cameron Simpson cs@cskk.id.au

1 Like

Thank you very much for the clear explanation!

Seems to be similar at a glance, but other languages such as c++ prohibit such inheritance.

error: redefinition of 'struct A'
 struct A : A {};
        ^

And I never get confused by x = x * 2 :slightly_smiling_face:

Fair enough.

It is unusual to rebind a class name, but what is common is subclassing
a name from another package:

from foo import A as FooA

class A(FooA):

You can see here that I’m defining an “A” class which subclasses the “A”
from the “foo” package. So I brought in the foo.A name under a different
local name. Also:

import foo

class A(foo.A):

Real world example from my own code:

import mailbox
...
class Maildir(mailbox.Maildir):
    ''' A faster Maildir interface.
        Trust os.listdir, don't fsync, etc.
    '''

Anyway, I’m sure you’ve thought of all this. If class A(A) is
confusing or counterintuitive (and I think I would view it the same) do
it more clearly.

Cheers,
Cameron Simpson cs@cskk.id.au

There is one key difference here: in Python, x = x * 2 rebinds the name x to a new object, the result of x * 2. In C++, the object named x is assigned the value x * 2

This just to illustrate the point that “the way names and objects work”, to borrow Steven’s phrase, is quite different in different languages.

C++ forbids redefinition of any types, not just this type of inheritance. The error message you pasted isn’t related to inheritance at all, it’s telling you are trying to redefine A. You would get the same error even if the second definition of A didn’t inherit from any other types, or inherited from a type that was not named A.

1 Like

Yes, it’s practical and easy to read. In contrast, using the same class name (without module name) is very confusing.

Exactly! Thanks, I didn’t notice that point. (If I can say in addition, ‘redefinition in the same scope’)
It is not so simple to compare different languages.

Yes. It reminded me of this post (+= should be the same as = + but its giving me error (list) - #4 by sweeneyde)