__new__ vs __init__

Gabriel Genellina gagsl-py2 en yahoo.com.ar
Jue Nov 15 04:06:38 CET 2007


En Wed, 14 Nov 2007 12:19:44 -0300, Luis Rodrigo Gallardo Cruz  
<rodrigo en nul-unu.com> escribió:

> On Wed, Nov 14, 2007 at 03:58:20PM +0100, tny wrote:
>> Sé que __new__ es llamado antes de que se cree el objeto, y es el
>> encargado de crearlo y devolverlo.
>>
>> Sé que __init__ es llamado después de __new__ para inicializarlo.
>
>> ¿Cuándo se debería emplear __init__ y cuando __new__?
>
> Usa __new__ cuando la creación del objeto no puede hacerse según las
> reglas normales de python. Por ejemplo, una forma (un poco fea en mi
> opinión) de implementar el patrón 'singleton' es que el __new__ de la
> clase, en vez de crear un objeto, regrese la instancia única.
>
> En general, usar __new__ es mágia. Casi siempre es mejor evitarlo.

Uh, porque? No hay nada de magia. Y no tiene que ver con que se salteen  
las reglas normales o no.
La diferencia entre __new__ e __init__ es la que dice tny ahi arriba. Solo  
que hay dos detalles importantes que faltan:
- si __new__ devuelve cualquier otra cosa que no es una instancia de la  
clase pedida, el __init__ no se invoca
- los tipos inmutables (numeros, strings, tuplas...) *NO* invocan __init__  
nunca, sino que la inicializacion se hace completamente dentro del __new__  
(justamente porque son inmutables).

Asi que, por ejemplo, si uno quisiera heredar de tuple para representar un  
punto del plano (x,y), la unica forma es usando __new__:

class Point2D(tuple):
     "Un punto (x,y)"

     def __new__(cls, *args):
         if not args:
             args = (0,0)
         if len(args)!=2:
             raise ValueError, "Point2D requiere dos argumentos: %s" %  
repr(args)
         return super(Point2D, cls).__new__(cls, args)

     x = property(lambda self: self[0])
     y = property(lambda self: self[1])

     def __repr__(self):
         return '%s(%r,%r)' % (type(self).__name__, self.x, self.y)

     def __abs__(self):
         from math import hypot
         return hypot(self.x, self.y)

     def __add__(self, other):
         return type(self)(self.x+other.x, self.y+other.y)

py> P1 = Point2D(30, 40)
py> P2 = Point2D(5, 10)
py> P1+P2
Point2D(35,50)
py> abs(P1)
50.0
py> Point2D()
Point2D(0,0)
py> Point2D(1,2,3)
Traceback (most recent call last):
   File "<stdin>", line 1, in <module>
   File "<stdin>", line 6, in __new__
ValueError: Point2D requiere dos argumentos: (1, 2, 3)

-- 
Gabriel Genellina

------------ próxima parte ------------
_______________________________________________
Lista de correo Python-es 
http://listas.aditel.org/listinfo/python-es
FAQ: http://listas.aditel.org/faqpyes


Más información sobre la lista de distribución Python-es