Volviendo a lo básico, POO en Python ( diferencia entre `__init__` y `__new__`) (parte 10)

Posted on dom 05 febrero 2017 in Tutorial Python • 3 min read

Continuando con la serie de artículos volviendo a lo básico POO, en este artículo se toca el tema de las diferencias entre __init__ y __new__.

Este artículo se baja en un artículo en inglés entendiendo new e init .

Muchos de los que han programado orientado a objetos en Python nunca han usado el método __new__, el que usan como constructor es el __init__.

La realidad es que el método __init__ crea el objeto y luego lo inicializa, no es el constructor como tal, en cambio el método __new__ sólo construye el objeto.

Para este artículo se trabajará con Python 3.

Se tiene la siguiente clase:

class A(object):

    pass

Así que la Clase A hereda de object en Python 3.

Ahora se crea una clase que use __new__ e __init__:

class A(object):


    def __new__(cls):


        print "A.__new__ es llamado"


        return super(A, cls).__new__(cls)





    def __init__(self):


        print "A.__init__ es llamado"





A()

La salida es la siguiente:

A.__new__ es llamado
A.__init__ es llamado

Ahora vamos con un ejemplo donde __init__no será llamado:

class A(object):
    def __new__(cls):
        print "A.__new__ es llamado"

    def __init__(self):
        print "A.__init__ es llamado"

print ("Solo llamando el objeto A() o instanciandolo")

A()
a = A()
print ("*"*40)
print ("Ejecutando un print con el objeto A()")
print (A())

Como se puede ver, en el método __init__ se está ejecutando un print y en el método __new__ también.

La salida es la siguiente:

Solo llamando el objeto A() o instanciandolo
A.__new__ es llamado
A.__new__ es llamado
****************************************
Ejecutando un print con el objeto A()
A.__new__ es llamado
None

Como se puede ver, al llamar a A() o al instanciarlo, sólo se muestra la salida del método __new__ , y el de __init__ no, sólo cuando se ejecuta el print(A()) es que devuelve la salida del método __new__ y devuelve None el método __init__.

Ahora se mostrará la clase con sólo el método __new__ donde se ejecuta un print y retorna un número:

class A(object):

    def __new__(cls):


        print "A.__new__ es llamado"


        return 29


print ("Solo llamando al objeto")


A()


print ("*"*30)


print ("Llamando al objeto desde un print")


print A()

La salida es la siguiente:

Solo llamando al objeto A.new es llamado


Llamando al objeto desde un print A.new es llamado 29

Al sólo llamar al objeto sólo muestra el print, pero al ejecutar el print muestra el print del método y el entero que retorna.

Ahora se hará lo mismo pero con el método init:

class A(object):

def __init__(self):

    print "A.__init__ es llamado"

    return 33

try:

print ("Solo llamando al objeto")

A()

except (TypeError):

print ("Error de tipo")

La salida es la siguiente:

Solo llamando al objeto
A.__init__ es llamado
Error de tipo

En este caso el return devuelve error de tipo. La única forma que en el método __init__ devuelva algo es que sea del tipo None.

Ahora se muestra el uso de una clase alternativa llamada Ejemplo:

class Ejemplo(object):


    def __str__(self):


        return "Ejemplo"


class A(object):


    def __new__(cls):


        return Ejemplo()


class B(object):


    def __new__(cls):


        return super(B, cls).__new__(Ejemplo)


print ("Prueba con A")


print A()


print ("Prueba con B")


print B()

La salida devuelve lo siguiente:

Prueba con A
Ejemplo
Prueba con B
Ejemplo

Como puede verse, hay casos donde es útil utilizar __new__ y otros donde se pueden seguir usando __init__.

El método __new__ es usado en patrones de diseño con python, que será la siguiente serie de artículos que vendrán en este blog.

¡Haz tu donativo! Si te gustó el artículo puedes realizar un donativo con Bitcoin (BTC) usando la billetera digital de tu preferencia a la siguiente dirección: 17MtNybhdkA9GV3UNS6BTwPcuhjXoPrSzV

O Escaneando el código QR desde la billetera:

17MtNybhdkA9GV3UNS6BTwPcuhjXoPrSzV