¿Por qué mi clase no se inicializa con "def __int__" o "def _init_"? ¿Por qué recibo un error de tipo "no acepta argumentos" o un error de atributo?

Resuelto Karl Knechtel asked hace 2 años • 1 respuestas

Si su pregunta se cerró como un duplicado de esto, es porque tenía un ejemplo de código que incluía algo como:

class Example:
    def __int__(self, parameter):
        self.attribute = parameter

o:

class Example:
    def _init_(self, parameter):
        self.attribute = parameter

Cuando posteriormente intenta crear una instancia de la clase, se produce un error:

>>> Example("an argument")
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
TypeError: Example() takes no arguments

(En algunas versiones de Python, el error puede decir TypeError: object.__new__() takes no parameters.)

Alternativamente, a las instancias de la clase parece que les faltan atributos:

>>> class Example:
...     def __int__(self): # or _init_
...         self.attribute = 'value'

>>> Example().attribute
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
AttributeError: 'Example' object has no attribute 'attribute'

Quizás también se pregunte: ¿qué significan estos mensajes de excepción y cómo se relacionan con el problema? ¿Por qué no ocurrió ningún problema antes, por ejemplo, con la definición de clase misma? ¿De qué otra manera podría manifestarse el problema? ¿Cómo puedo protegerme contra este problema en el futuro?


Esta es una pregunta canónica artificial creada específicamente para evitar dos de los errores tipográficos más comunes en el código escrito por nuevos programadores de Python. Si bien las preguntas causadas por un error tipográfico normalmente se cierran por ese motivo, hay algunas cosas útiles que explicar en este caso, y tener un objetivo duplicado permite cerrar las preguntas más rápido. He intentado diseñar la pregunta para que sea fácil de buscar.

Consulte también TypeError: __init__() debería devolver Ninguno, no 'int' para el problema opuesto: escribir __init__en lugar de __int__intentar crear una clase que se pueda convertir a un número entero.

Karl Knechtel avatar Jun 03 '22 04:06 Karl Knechtel
Aceptado

Esto se debe a que el código tiene un error tipográfico simple : en su lugar, el método debería tener un nombre __init__; tenga en cuenta la ortografía y tenga en cuenta que hay dos guiones bajos en cada lado.

¿Qué significan los mensajes de excepción y cómo se relacionan con el problema?

Como se podría adivinar, a TypeErrores un error que tiene que ver con el tipo de algo. En este caso, el significado es un poco forzado: Python también usa este tipo de error para llamadas a funciones donde los argumentos (las cosas que pones en el medio ()para llamar a una función, constructor de clase u otro invocable ) no se pueden asignar correctamente a los parámetros. (las cosas que pones ()al escribir una función usando la defsintaxis).

En los ejemplos donde TypeErrorocurre a, el constructor de clase for Exampleno acepta argumentos. ¿Por qué? Porque utiliza el objectconstructor base , que no acepta argumentos. Eso es simplemente seguir las reglas normales de herencia: no hay ningún definido localmente, por lo que se usa __init__el de la superclase, en este caso .object

De manera similar, an AttributeErrores un error que tiene que ver con un atributo de algo. Esto es bastante sencillo: la instancia de Exampleno tiene ningún .attributeatributo, porque el constructor (que, nuevamente, proviene objectdebido al error tipográfico) no estableció ninguno.

¿Por qué no ocurrió ningún problema antes, por ejemplo, con la definición de clase misma?

Porque el método con un nombre escrito incorrectamente sigue siendo sintácticamente válido. SyntaxErrorSolo se pueden detectar errores de sintaxis (reportados como ) antes de que se ejecute el código. Python no asigna ningún significado especial a los métodos nombrados _init_(con un guión bajo a cada lado), por lo que no le importa cuáles sean los parámetros. Si bien __int__se usa para convertir instancias de la clase a enteros y no debería tener ningún parámetro además self, sigue siendo sintácticamente válido.

Es posible que su IDE pueda advertirle sobre un __int__método que toma parámetros sospechosos (es decir, cualquier cosa además de self). Sin embargo, a) eso no resuelve completamente el problema (ver más abajo), y b) el IDE podría haberlo ayudado a hacerlo mal en primer lugar (al hacer una mala sugerencia de autocompletar).

El _init_error tipográfico parece ser mucho menos común hoy en día. Supongo que la gente solía hacer esto después de leer códigos de ejemplo de libros con una composición tipográfica deficiente.

¿De qué otra manera podría manifestarse el problema?

En el caso de que una instancia se cree correctamente (pero no se inicialice correctamente), cualquier tipo de problema podría ocurrir más adelante (dependiendo de por qué se necesitaba una inicialización adecuada). Por ejemplo:

BOMB_IS_SET = True

class DefusalExpert():
    def __int__(self):
        global BOMB_IS_SET
        BOMB_IS_SET = False
    def congratulate(self):
        if BOMB_IS_SET:
            raise RuntimeError("everything blew up, gg")
        else:
            print("hooray!")

>>> d = DefusalExpert()
>>> d.congratulate()
Traceback (most recent call last):
  ...
RuntimeError: everything blew up, gg

Si tiene la intención de que la clase sea convertible a un número entero y también escribió __int__deliberadamente, la última tendrá prioridad:

class LoneliestNumber:
    def __int__(self):
        return 1
    def __int__(self): # was supposed to be __init__
        self.two = "can be as bad"

>>> int(LoneliestNumber())
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
TypeError: __int__ returned non-int (type NoneType)

¿Cómo podría protegerme contra el problema en el futuro?

No existe una fórmula mágica. Creo que ayuda un poco tener la convención de poner siempre __init__(y/o __new__) como el primer método en una clase, si la clase lo necesita. Sin embargo, nada sustituye a la revisión ni a la formación.

Karl Knechtel avatar Jun 02 '2022 21:06 Karl Knechtel