ChatGPT解决这个技术问题 Extra ChatGPT

super() raises "TypeError: must be type, not classobj" for new-style class

The following use of super() raises a TypeError: why?

>>> from  HTMLParser import HTMLParser
>>> class TextParser(HTMLParser):
...     def __init__(self):
...         super(TextParser, self).__init__()
...         self.all_data = []
...         
>>> TextParser()
(...)
TypeError: must be type, not classobj

There is a similar question on StackOverflow: Python super() raises TypeError, where the error is explained by the fact that the user class is not a new-style class. However, the class above is a new-style class, as it inherits from object:

>>> isinstance(HTMLParser(), object)
True

What am I missing? How can I use super(), here?

Using HTMLParser.__init__(self) instead of super(TextParser, self).__init__() would work, but I would like to understand the TypeError.

PS: Joachim pointed out that being a new-style-class instance is not equivalent to being an object. I read the opposite many times, hence my confusion (example of new-style class instance test based on object instance test: https://stackoverflow.com/revisions/2655651/3).

Thanks for your question and answer. I wonder why 2.7's super.__doc__ doesn't mention anything about old vs new style!
Thanks. :) Docstrings typically contain less information than the full, HTML version of the documentation. The fact that super() works only for new-style classes (and objects) is mentioned in the HTML doc (docs.python.org/library/functions.html#super).
This is not a duplicate (see the updated question, and the accepted answer).

C
Community

Alright, it's the usual "super() cannot be used with an old-style class".

However, the important point is that the correct test for "is this a new-style instance (i.e. object)?" is

>>> class OldStyle: pass
>>> instance = OldStyle()
>>> issubclass(instance.__class__, object)
False

and not (as in the question):

>>> isinstance(instance, object)
True

For classes, the correct "is this a new-style class" test is:

>>> issubclass(OldStyle, object)  # OldStyle is not a new-style class
False
>>> issubclass(int, object)  # int is a new-style class
True

The crucial point is that with old-style classes, the class of an instance and its type are distinct. Here, OldStyle().__class__ is OldStyle, which does not inherit from object, while type(OldStyle()) is the instance type, which does inherit from object. Basically, an old-style class just creates objects of type instance (whereas a new-style class creates objects whose type is the class itself). This is probably why the instance OldStyle() is an object: its type() inherits from object (the fact that its class does not inherit from object does not count: old-style classes merely construct new objects of type instance). Partial reference: https://stackoverflow.com/a/9699961/42973.

PS: The difference between a new-style class and an old-style one can also be seen with:

>>> type(OldStyle)  # OldStyle creates objects but is not itself a type
classobj
>>> isinstance(OldStyle, type)
False
>>> type(int)  # A new-style class is a type
type

(old-style classes are not types, so they cannot be the type of their instances).


And this is one of the reasons we now have Python 3.
BTW: (Oldstyle().__class__ is Oldstyle) is True
@Tino: indeed, but the point of OldStyle().__class__ is to show how to test whether an object (OldStyle()) comes from an old-style class. With only new-style classes in mind, one might be tempted to do the test with isinstance(OldStyle(), object) instead.
It is preposterous how much of the python standard library still in 2.7.x doesn't inherit from object, thus screwing you by proxy.
@NickBastin - this is not a coincidence. It's all in order to push everybody into Python 3. Where "everything is fine already." But - caveat emptor - it's only bait and switch.
C
Colin Su

super() can be used only in the new-style classes, which means the root class needs to inherit from the 'object' class.

For example, the top class need to be like this:

class SomeClass(object):
    def __init__(self):
        ....

not

class SomeClass():
    def __init__(self):
        ....

So, the solution is that call the parent's init method directly, like this way:

class TextParser(HTMLParser):
    def __init__(self):
        HTMLParser.__init__(self)
        self.all_data = []

For me, I had to do this: HTMLParser.__init__(self) I am curious as to if your last example worked?
@EOL What do mean? jeffp just pointed out that the code given in this answer is wrong due to lack of self parameter in HTMLParser.__init__() call.
@PiotrDobrogost: Sorry, my remark was about LittleQ's answer, not about jeffp's (good) point.
@jeffp sorry, it's a typo, I just type it out on SO but haven't test it, my fault. thanks for correcting
Upvote for a fix that works with existing code, such as logging.Formatter in python2.6
E
Eric O Lebigot

You can also use class TextParser(HTMLParser, object):. This makes TextParser a new-style class, and super() can be used.


An upvote from me, as adding the inheritance from object is a good idea. (That said, this answer does not address the issue of understanding the TypeError of the question.)
E
Eric O Lebigot

The problem is that super needs an object as an ancestor:

>>> class oldstyle:
...     def __init__(self): self.os = True

>>> class myclass(oldstyle):
...     def __init__(self): super(myclass, self).__init__()

>>> myclass()
TypeError: must be type, not classobj

On closer examination one finds:

>>> type(myclass)
classobj

But:

>>> class newstyle(object): pass

>>> type(newstyle)
type    

So the solution to your problem would be to inherit from object as well as from HTMLParser. But make sure object comes last in the classes MRO:

>>> class myclass(oldstyle, object):
...     def __init__(self): super(myclass, self).__init__()

>>> myclass().os
True

Valid points, but they are already in previous answers. Also, instead of checking type(myclass), what matters is whether myclass inherit from object (i.e. whether isinstance(myclass, object) is true—it is false).
S
Some programmer dude

If you look at the inheritance tree (in version 2.6), HTMLParser inherits from SGMLParser which inherits from ParserBase which doesn't inherits from object. I.e. HTMLParser is an old-style class.

About your checking with isinstance, I did a quick test in ipython:

In [1]: class A:
   ...:     pass
   ...: 

In [2]: isinstance(A, object)
Out[2]: True

Even if a class is old-style class, it's still an instance of object.


I believe that the correct test should be isinstance(A(), object), not isinstance(A, object), no? With the latter, you are testing whether the class A is an object, whereas the question is whether instances of A are an object, right?
PS: the best test seems to be issubclass(HTMLParser, object), which returns False.
J
Jacob Abraham

the correct way to do will be as following in the old-style classes which doesn't inherit from 'object'

class A:
    def foo(self):
        return "Hi there"

class B(A):
    def foo(self, name):
        return A.foo(self) + name

In the question, this method is already mentioned: the problem is to understand why an error message was produced, not to make it go away (in particular in this way).
Besides, this solution will not work in the case that we want a call an instance of the class A that stores an state.
q
qwerty_so

FWIW and though I'm no Python guru I got by with this

>>> class TextParser(HTMLParser):
...    def handle_starttag(self, tag, attrs):
...        if tag == "b":
...            self.all_data.append("bold")
...        else:
...            self.all_data.append("other")
...     
...         
>>> p = TextParser()
>>> p.all_data = []
>>> p.feed(text)
>>> print p.all_data
(...)

Just got me the parse results back as needed.