2012-06-14 14 views
6

Tengo varios objetos de diferentes tipos (diferentes nombres de funciones, diferentes firmas) y los ajusto para tener una forma común de acceder a ellos desde diferentes funciones. En resumen, hay un despachador que toma los objetos que quiero parchar y, dependiendo del tipo de objeto, llama a un parche diferente. Un parche añadirá métodos para el objeto:Agregando clase base al objeto existente en python

def patcher_of_some_type(object): 

    def target(self, value): 
     # do something and call self methods 

    object.target = types.MethodType(target, object) 

    # many more of these 

A medida que el programa crece más complicado hacer una envoltura alrededor del objeto (o la clase de objeto) parece ser mejor idea. Algunos parches comparten un código común o están interrelacionados. Pero no controlo la creación de objetos, ni la creación de clases. Solo obtengo los objetos. E incluso si pudiera hacer eso, solo quiero envolver (o parchar) ciertos objetos, no todos.

Una solución podría ser agregar una clase base a un objeto existente, pero no estoy seguro de cuán sostenible y segura es esta. ¿Hay alguna otra solución?

+0

Así que la las clases no están definidas de usted? – dav1d

+0

No, las clases están definidas en una biblioteca que no controlo. Podría crear wrappers de todas las clases, pero esto solo funcionará para los objetos que instalé, no para los creados por la biblioteca. Si parcheo la biblioteca, sucede lo contrario: todos los objetos están parcheados y solo quiero parchar algunos de ellos. Finalmente, si envuelvo algunos objetos, necesitaría mantener mi propia estructura de datos para referirme a ellos. Es por eso que ahora estoy parcheando los objetos que quiero. – Hernan

+1

Definiría una clase contenedora para contener los objetos. La necesidad de tener diferentes tipos de envoltorios que comparten un código común se puede manejar teniendo una clase de envoltura base y obteniendo versiones especializadas de ella. Cosas básicas de OOP ... – martineau

Respuesta

9

modificar dinámicamente un tipo de objeto es razonablemente seguro, ya siempre que la clase base extra sea compatible (y obtendrá una excepción si no es así). La forma más sencilla de añadir una clase base es con el 3-argumento type constructor:

cls = object.__class__ 
object.__class__ = cls.__class__(cls.__name__ + "WithExtraBase", (cls, ExtraBase), {}) 
+1

usando type (y por favor solo use 'type' en lugar de' object .__ class __.__ class__', que por cierto falla con las clases antiguas!) Solo es necesario si necesita establecer el nombre de la clase, de lo contrario solo puede hacer 'clase AnyNameHere (cls, ExtraBase): pass' que es más legible. – l4mpi

0

Para agregar una clase a otra clases de bases, que podría hacer algo como esto:

def pacher_of_some_type(object): 

    #this class holds all the stuff you want to add to the object 
    class Patch(): 
     def target(self, value): 
      #do something 

    #add it to the class' base classes 
    object.__class__.__bases__ += (Patch,) 

Pero esto afectará a todos los objetos de esta clase ya que en realidad cambia la clase en sí, que no parece se lo que quieras ... si lo que desea es parchear una instancia, podría subclase de la clase original y cambiar la clase de los casos de forma dinámica:

class PatchedClass(object.__class__): 
    def target(self, value): 
     #do whatever 

object.__class__ = PatchedClass 

En términos de seguridad y capacidad de gestión, diría que añadir dinámicamente una la clase base es tan mantenible como la adición dinámica de alguna función ns, y más seguro porque es menos probable que sobrescriba accidentalmente algunas funciones internas de la clase original porque la nueva clase base se agrega al final de la tupla, lo que significa que se utiliza por última vez para la resolución del nombre. Si realmente desea sobrescribir los métodos, agréguelos al principio de la tupla de las clases base (por supuesto, esto no se aplica al enfoque de las subclases). No me pregunte si se cambia dinámicamente la clase de una instancia es seguro en absoluto, pero en un ejemplo trivial que no parece ser un problema:

class A(): 
    def p(self): print 1 

class B(): 
    def p(self): print 2 

a = A() 
a.p() #prints 1 
a.__class__ = B 
a.p() #prints 2 
2

Basado en un comment en otra respuesta, esta es otra manera de arreglar la clase base:

class Patched(instance.__class__, ExtraBase): 
    pass 

instance.__class__ = Patched 
+1

¡Esto es realmente bueno! Sin embargo, parece cambiar el nombre de la clase a Patched. Puede solucionarlo agregando una línea: 'Patched .__ name__ = instance .__ class __.__ name__'. – paulmelnikow

Cuestiones relacionadas