super() plantea "TypeError: debe ser type, no classobj" para la clase de estilo nuevo


El siguiente uso de super() genera un error tipográfico: ¿por qué?

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

Hay una pregunta similar en StackOverflow: Python super() plantea TypeError, donde el error se explica por el hecho de que la clase user no es una clase de estilo nuevo. Sin embargo, la clase anterior es una clase de estilo nuevo, ya que hereda de object:

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

¿Qué me estoy perdiendo? ¿Cómo puedo usar super(), aquí?

Usar HTMLParser.__init__(self) en lugar de super(TextParser, self).__init__() funcionaría, pero me gustaría entender el TypeError.

PS: Joachim señaló que ser una instancia de clase de nuevo estilo no es equivalente a ser un object. Leí lo contrario muchas veces, de ahí mi confusión (ejemplo de prueba de instancia de clase de nuevo estilo basado en object prueba de instancia: https://stackoverflow.com/revisions/2655651/3).

Author: Community, 2012-03-14

6 answers

Bien, es lo habitual "super() no se puede usar con una clase de estilo antiguo".

Sin embargo, el punto importante es que la prueba correcta para "¿es este un nuevo estilo instancia (es decir, objeto)?"es

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

Y no (como en la pregunta):

>>> isinstance(instance, object)
True

Para clases , la prueba correcta "es una clase de nuevo estilo" es:

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

El punto crucial es que con las clases de estilo antiguo, la clase de una instancia y su los tipos son distintos. Aquí, OldStyle().__class__ es OldStyle, que no hereda de object, mientras que type(OldStyle()) es el tipo instance, que hereda de object. Básicamente, una clase de estilo antiguo simplemente crea objetos de tipo instance (mientras que una clase de estilo nuevo crea objetos cuyo tipo es la propia clase). Esta es probablemente la razón por la que la instancia OldStyle() es un object: su type() hereda de object (el hecho de que su clase no hereda no de object no cuenta: las clases de estilo antiguo construir nuevos objetos de tipo instance). Referencia parcial: https://stackoverflow.com/a/9699961/42973.

PD: La diferencia entre una clase de estilo nuevo y una de estilo antiguo también se puede ver con:

>>> 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

(las clases de estilo antiguo son no tipos, por lo que no pueden ser el tipo de sus instancias).

 235
Author: Eric Lebigot,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2017-05-23 12:26:36

Super() solo se puede usar en las clases de estilo nuevo, lo que significa que la clase raíz necesita heredar de la clase 'object'.

Por ejemplo, la clase superior debe ser así:

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

No

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

Entonces, la solución es que llame al método init del padre directamente, de esta manera:

class TextParser(HTMLParser):
    def __init__(self):
        HTMLParser.__init__(self)
        self.all_data = []
 192
Author: Colin Su,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2013-12-27 03:42:58

También puedes usar class TextParser(HTMLParser, object):. Esto hace TextParser una clase de nuevo estilo, y super() se puede usar.

 25
Author: Valentin Lorentz,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2013-05-11 06:41:55

El problema es que super necesita un object como ancestro:

>>> 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

Al examinar más de cerca uno encuentra:

>>> type(myclass)
classobj

Pero:

>>> class newstyle(object): pass

>>> type(newstyle)
type    

Así que la solución a su problema sería heredar de object así como de HTMLParser. Pero asegúrese de que object sea el último en las clases MRO:

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

>>> myclass().os
True
 18
Author: user2070206,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2018-09-30 20:18:35

Si se mira el árbol de herencia (en la versión 2.6), HTMLParser hereda de SGMLParser que hereda de ParserBase que no hereda de object. Es decir, HTMLParser es una clase de estilo antiguo.

Acerca de su comprobación con isinstance, hice una prueba rápida en ipython:

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

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

Incluso si una clase es de estilo antiguo, sigue siendo una instancia de object.

 17
Author: Some programmer dude,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2012-03-14 09:04:56

La forma correcta de hacerlo será la siguiente en las clases de estilo antiguo que no heredan de'object'

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

class B(A):
    def foo(self, name):
        return A.foo(self) + name
 4
Author: Jacob Abraham,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2014-02-20 10:31:39