2011-02-14 21 views
11

Tengo un método en uno de mis objetos que devuelve una nueva instancia de esa misma clase. Intento descubrir la forma más idiomática de escribir este método de manera que genere un nuevo objeto del mismo tipo sin duplicar el código.Idiomatic Python para generar un nuevo objeto desde una clase

Dado que este método utiliza datos de la instancia, mi primer paso es:

class Foo(object): 
    def get_new(self): 
     data = # Do interesting things 
     return Foo(data) 

Sin embargo, si subclase de Foo y no anula las get_new, llamando get_new en SubFoo devolvería un Foo! Por lo tanto, podría escribir un classmethod:

class Foo(object): 

    @classmethod 
    def get_new(cls, obj): 
     data = # Munge about in objects internals 
     return cls(data) 

Sin embargo, los datos que estoy accediendo es específico del objeto, por lo que parece romper la encapsulación para que esto no sea un método "normal" (sin decoración). Además, debe llamarlo como SubFoo.get_new(sub_foo_inst), lo que parece redundante. Me gustaría que el objeto simplemente "sepa" qué tipo devolver, ¡del mismo tipo que él!

Supongo que también es posible agregar un método de fábrica a la clase y anular el tipo de devolución en todas partes, sin duplicar la lógica, pero eso parece poner mucho trabajo en las subclases.

Entonces, mi pregunta es, ¿cuál es la mejor manera de escribir un método que proporcione flexibilidad en el tipo de clase sin tener que anotar el tipo por todas partes?

+0

classmethods mismos no son muy idiomáticos en Python; las fábricas pueden ser funciones simples que viven en el mismo módulo que la clase. –

+1

@Adam: los métodos de clase tienen sus usos (por ejemplo, cuando se usan variables de clase y no se quiere romper la herencia). Los métodos estáticos son mucho peores. – delnan

Respuesta

16

Si desea hacerlo más flexible para la creación de subclases, puede simplemente usar el self.__class__ atributo especial:

class Foo(object): 
    def __init__(self, data): 
     self.data = data 

    def get_new(self): 
     data = # Do interesting things 
     return self.__class__(data) 

Tenga en cuenta que utilizando el enfoque @classmethod le impide acceder a los datos dentro de una misma instancia, sacándolo como una solución viable en casos donde #Do interesting things se basa en datos almacenados dentro de una instancia.

Para Python 2, no recomiendan el uso de type(self), ya que esto devolverá un valor apropiado para classic classes (es decir, los que no subclase de la base object):

>>> class Foo: 
...  pass 
... 
>>> f = Foo() 
>>> type(f) 
<type 'instance'> 
>>> f.__class__ # Note that the __class__ attribute still works 
<class '__main__.Foo'> 

Para Python 3, esto no es tanto de un problema, como todas las clases se derivan de object, sin embargo, creo que self.__class__ se considera el modismo más pitónico.

+0

¡Perfecto! Eso es exactamente lo que estaba buscando. Esto es solo la semana 2 de usar seriamente Python, así que aún no estoy totalmente al tanto de todas las piezas y cómo se usan. –

+1

Usando 'type (self)', como möter sugiere, hace esencialmente lo mismo, pero es ligeramente más limpio en mi humilde opinión. Pero tal vez esa es solo mi aversión por cualquier cosa con cuatro guiones bajos. – Thomas

+2

@Thomas: en realidad, no recomiendo usar 'type()', y he agregado una explicación de por qué a mi respuesta. – gotgenes

2

Puede usar el 'tipo' incorporado.

type(instance) 

es la clase de esa instancia.

Cuestiones relacionadas