super () genera “TypeError: debe ser type, no classobj” para la clase de nuevo estilo

El siguiente uso de super() genera un TypeError: ¿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 () genera TypeError , donde el error se explica por el hecho de que la clase de usuario no es una clase de nuevo estilo. Sin embargo, la clase anterior es una clase de nuevo estilo, ya que hereda del object :

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

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

Usar HTMLParser.__init__(self) 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 estilo nuevo no es equivalente a ser un object . Leí el opuesto muchas veces, de ahí mi confusión (ejemplo de prueba de instancia de clase de nuevo estilo basada en prueba de instancia de object : https://stackoverflow.com/revisions/2655651/3 ).

    Muy bien, es el habitual ” super() no se puede usar con una clase de estilo antiguo”.

    Sin embargo, el punto importante es que la prueba correcta para “¿es esta una instancia de nuevo estilo (es decir, un objeto)?” es

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

    Y no (como en la pregunta):

     >>> isinstance(instance, object) True 

    Para las clases , la prueba correcta “es esta 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 tipo son distintas. Aquí, OldStyle().__class__ es OldStyle , que no hereda del object , mientras que type(OldStyle()) es el tipo de instance , que hereda del 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 clase en sí). Probablemente esta sea la razón por la que OldStyle() es un object : su type() hereda de un object (el hecho de que su clase no hereda de un object no cuenta: las clases de estilo antiguo simplemente construyen nuevos objetos de tipo instance ). Referencia parcial: https://stackoverflow.com/a/9699961/42973 .

    PD: La diferencia entre una clase de nuevo estilo 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 no son tipos, por lo que no pueden ser el tipo de sus instancias).

    super () solo se puede utilizar en las clases de nuevo estilo, lo que significa que la clase raíz necesita heredar de la clase ‘objeto’.

    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 se llame directamente al método init del padre, de esta manera:

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

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

    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 

    En un examen más detallado se encuentra:

     >>> type(myclass) classobj 

    Pero:

     >>> class newstyle(object): pass >>> type(newstyle) type 

    Por lo tanto, la solución a su problema sería heredar tanto del objeto como de HTMLParser. Pero asegúrate de que el objeto llegue el último en las clases MRO:

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

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

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

     En [1]: clase A:
        ...: pasar
        ...: 
    
     En [2]: isinstance (A, objeto)
     Fuera [2]: Verdadero
    

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

    La forma correcta de hacerlo será la siguiente en las clases de estilo antiguo que no heredan de ‘objeto’

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

    FWIW y aunque no soy un gurú de Python, me las arreglé con esto

     >>> 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 (...) 

    Acabo de recibir los resultados del análisis según sea necesario.