33

I am trying to connect via telnet to a laboratory instrument. I'd like to extend the Telnet class from the telnetlib module in the standard library, to include functions specific to our instrument:

import telnetlib
class Instrument(telnetlib.Telnet):
    def __init__(self, host=None, port=0, timeout=5):
        super(Instrument,self).__init__(host, port, timeout)

All I am trying to do in this code is inherit the __init__ method from the parent class (telnetlib.Telnet) and pass on the standard arguments, so I can add things to __init__ later. This formula has worked for me on other occasions; this time it gives me an error at the super() statement when I try to instantiate:

TypeError: must be type, not classobj

I looked at the source code for telnetlib, and Telnet appears to be an old-style class (it doesn't inherit from object) - I'm wondering if this could be the source of my problem? If so, how can it be overcome? I've seen some code examples where the derived class inherits both from the superclass and object, though I'm not entirely sure if this is a response to the same problem as me.

Full disclosure: I have also tried using telnetlib.Telnet in place of super(), and from telnetlib import Telnet with Telnet in place of super(). The problem persists in these cases.

Thanks!

Benjamin Hodgson
  • 42,952
  • 15
  • 108
  • 157

2 Answers2

45

You need to call the constructor like this:

telnetlib.Telnet.__init__(self, host, port, timeout)

You need to add the explicit self since telnet.Telnet.__init__ is not a bound method but rather an unbound method, i.e. witout an instance assigned. So when calling it you need to pass the instance explicitely.

>>> Test.__init__
<unbound method Test.__init__>
>>> Test().__init__
<bound method Test.__init__ of <__main__.Test instance at 0x7fb54c984e18>>
>>> Test.__init__()
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
TypeError: unbound method __init__() must be called with Test instance as first argument (got nothing instead)
ThiefMaster
  • 310,957
  • 84
  • 592
  • 636
  • I've tried that - it gives me the error: `TypeError: unbound method __init__() must be called with Telnet instance as first argument (got str instance instead)` – Benjamin Hodgson Jul 17 '12 at 17:46
  • Please show the exact code and error you get with the code I just posted. Because that's actually how it's done if you want to call the parent constructor of an old-style class. – ThiefMaster Jul 17 '12 at 17:47
  • Ah, I've spotted the difference between your code and mine - you had `self` as the first argument of `__init__`. It works now - thanks for replying so quickly! Would you be able to explain to me why you need to give `self` as the first argument to the constructor? I'm new to Python. – Benjamin Hodgson Jul 17 '12 at 17:50
  • 1
    Note that this will give you an old-style class still. – Marcin Jul 17 '12 at 17:58
  • You always need to pass self to any method (except class and static methods). This is taken care of semi-automatically in normal dot-calling: you call `self.foo(a, b)`, and Python turns that into a call to `foo(self, a, b)`. But in this case, you're not calling `__init__` on the object, you're calling it on the class, which means you have to add `self` explicitly. (This is slightly inaccurate in a few details, but close enough for learning purposes.) – abarnert Jul 17 '12 at 18:34
16

You have to inherit from object, and you must put it after the old-style class you are trying to inherit from (so that object's methods aren't found first):

>>> class Instrument(telnetlib.Telnet,object):
...     def __init__(self, host=None, port=0, timeout=5):
...         super(Instrument,self).__init__(host, port, timeout)
...
>>> Instrument()
<__main__.Instrument object at 0x0000000001FECA90>

Inheriting from object gives you a new-style class which works with super.

Marcin
  • 48,559
  • 18
  • 128
  • 201
  • 2
    Is it safe to inherit from an old-style class in a way that creates a new-style class based on it? – ThiefMaster Jul 17 '12 at 18:13
  • @ThiefMaster I'm not aware of any issues, but I've never done it in anger, so I really have no information. Do you anticipate any problems? – Marcin Jul 17 '12 at 18:25
  • I'm unfamiliar with multi-inheritance in Python. Since a new-style class doesn't confer any advantages for my purpose, I'm going to stick with TheifMaster's strategy above. Thanks anyway though! – Benjamin Hodgson Jul 17 '12 at 21:09
  • 1
    @poorsod What do you imagine is special about "multi-inheritance" in python? One of the main advantages of new-style classes is that they do in fact deal with multiple inheritance correctly. – Marcin Jul 17 '12 at 21:23
  • I'm sure you're right, but there are certain things I'm unclear of that make me wary of doing it (including the fact that you don't know whether it would break things!). For example, how does this differ from going into the telnetlib source and changing `def Telnet:` to `def Telnet(Object):`? I'm also unclear on what a call to `super` would return. – Benjamin Hodgson Jul 17 '12 at 22:12
  • @poorsod Well, first of all, this doesn't alter the `Telnet` object; second of all, `Object` is not defined. A call to `super` will return what it always returns: a proxy object. It just seems that you're not comfortable with python's class system in general. – Marcin Jul 18 '12 at 13:21
  • Well obviously that's just a typo, should be lowercase `object`. Maybe I have some more reading to do; I just don't understand what is supposed to happen when I call `super().__init__`, if both superclasses have an `__init__` method defined. – Benjamin Hodgson Jul 18 '12 at 14:15
  • 1
    @poorsod The point of `super` + new-style classes is to make this work predictably. The answer is that you will get the first `__init__` found by searching `__mro__`. – Marcin Jul 18 '12 at 14:40