Python Forum

Full Version: Issue python3.6 while inheriting telnet library
You're currently viewing a stripped down version of our content. View the full version with proper formatting.
I am trying to create below hierarchy in my python code. But I am facing some issue when I am inheriting telnet library. This issue is coming in python 3.6.
import telnetlib
class A(telnetlib.Telnet):
    def __init__(self):
        print("init A")
        super(A, self).__init__()

class B(object):
    def __init__(self):
        print("init B")
        super(B, self).__init__()

class C(B):
    def __init__(self):
        print("init C")
        super(C, self).__init__()

class D(object):
    def __init__(self):
        print("init D")
        super(D, self).__init__()

class E(C,D):
    def __init__(self):
        print("init E")
        super(E, self).__init__()

class F(A,E):
    def __init__(self):
        print("init F")
        super(F, self).__init__()

if __name__ == '__main__':
    F()
Output in python3.6:-
Output:
init F init A
Output in python2.7:-
Output:
init F init A init E init C init B init D Exception AttributeError: "'F' object has no attribute 'sock'" in <bound method F.__del__ of <__main__.F object at 0x7f6a9f36a6d0>> ignored
Output in python3.6 without telnet library:-
Output:
init F init A init E init C init B init D
if I run the same code and derive class 'A' from object instead of telnetlib.Telnet the constructor calling works fine.

Can somebody please guide me here, What I am doing wrong. I am really stuck here.

The same code works perfectly fine with python 2.7.

Thanks in advance!

Regards, Sourabh Jaiswal
The short answer is because the Telnet class is not calling super().__init__() so the initialisation process stops when the mro is exhausted or an __init__ method does not call super().__init__.
In your case the mro of F is:
Output:
MRO: (<class '__main__.F'>, <class '__main__.A'>, <class 'telnetlib.Telnet'>, <class '__main__.E'>, <class '__main__.C'>, <class '__main__.B'>, <class '__main__.D'>, <class 'object'>)
So F.__init__ calls super(), that calls A.__init__ that calls super() that calls Telnet.__init__ and is done.

For your case this might be a easy solution, invert the inheritance in F so:
class F(E, A):
    def __init__(self):
        print("init F")
        super().__init__()
And now:
Output:
MRO: (<class '__main__.F'>, <class '__main__.E'>, <class '__main__.C'>, <class '__main__.B'>, <class '__main__.D'>, <class '__main__.A'>, <class 'telnetlib.Telnet'>, <class 'object'>) init F init E init C init B init D init A
But take into account that many times the order of inheritance is important!.
If you cannot invert the inheritance order, the only option is to remove the super() and replace them with the explicit calls to the __init__ methods.

Nevertheless this is one of the most obscure corners of python, so be prepared to read tons of magic explanations when you search in google for "python3 super multiple inheritance"

Ah, and is perfectly possible that not calling __init__ in Telnet is intentional, as the author requires that if you inherit from it it must be the last in the mro chain order.
Thanks a Lot for a detailed reply. I am quite new to python and writing a new project application from scratch.
Because of this I was kind of forced to use python2.7 at the backend. Now I think I can change it.

Thanks again.

One Question though, How come the same code is working fine with python2. The inheritance rules are same for both python version right?

Regards,
Jaiswal
(May-08-2018, 07:31 PM)sourabhjaiswal92 Wrote: [ -> ]One Question though, How come the same code is working fine with python2. The inheritance rules are same for both python version right?

Not exactly... as I said this is something complex to understand and normally I need to refresh my knowledge the few times I try to use multiple inheritance (it is useful, but 90% of the times you can do the same with just single inheritance)

I think it has to do with the different way to implement super (it returns a "proxy" class and has changed a lot from the 2 to 3)
In python 2 the line "super(F, self).__init__()" translates in something vaguely like "for kls in F.__mro__: kls.__init__(self)" so it is like all the init methods where evaluated at the F class level.
In python 3 this cascade calling is cleaner, delegating to each subclass so it is more similar to what the programmer expects... but the price is that the chain stops in the first "non cooperative" class.

It is easy that some other in the forum that knows better the inner guts of super can explain in detail the difference as my description of the super mechanism is a nice try at best...
(May-08-2018, 09:00 PM)killerrex Wrote: [ -> ]It is easy that some other in the forum that knows better the inner guts of super can explain in detail the difference as my description of the super mechanism is a nice try at best...

No Doubt about that. It was very nice and detailed explanation.
Will try to dig some more about MRO.

Thanks a lot for your explanation.

Regards,
Sourabh Jaiswal.