2012-04-05 20 views
6

Creo un python-packages/MyLibPackage que importaré en mis proyectos.Cómo sobrescribir una clase python importada para todas las llamadas

MyLibPackage.____init____.py incluye mymodiciation.py. Además, la carpeta MyLibPackage contiene otro archivo: base_classes.py (= proyecto externo)

mymodiciation.py imports "from base_classes import *".

Objetivo: Puedo importar MyLibPackage que tiene todas las clases de base_classes (= proyecto externo). Y si tengo que modificar algunas clases o funciones, puedo sobrescribir esto en mymodiciation.py. Funciona pero tengo un problema. Por ejemplo:

que sobrescribir esta clases en mymodiciation.py:

class Bookcollection(Bookcollection): 
    new_member = "lalala" 


class user(user): 
    def get_books(self): 
     return Bookcollection() 

si lo hago:

from MyLibPackage import * 
x = user() 
books = x.get_books() 

entonces el objeto Bookcollection tiene la propiedad "new_member". ¡Bueno! Pero si he de hacer esto:

from MyLibPackage import * 
x = shelf() #this class is not overwritten and used also the object "Bookcolelction" 
books = x.get_books() 

entonces el objeto Bookcollection NO tiene la "new_member" propiedad porque se instancia con MyLibPackage.base_classes.Bookcollection y no con mi MyLibPackage.mymodiciation.Bookcollection clase sobrescribe

¿Cómo puedo decir: si sobrescribo una clase en mymodiciation entonces MyLibPackage tiene que usar esto aunque cuando la llamada proviene de MyLibPackage.base_classes.shelf (get_books).

+0

si se enreda en cadenas de herencia, entonces tal vez debería buscar otras soluciones. – jldupont

+0

¡¿Tal vez podría intentar trabajar con ganchos ?! –

Respuesta

11

Lo que quiere hacer es llamar "parche de monos", y tiene poco que ver con la orientación de objetos.

Python lo admite, pero usted tiene el control de todas sus clases, debe revisar seriamente su proyecto para comprobar si realmente lo necesita.

Quizás usando un marco como Zope Component Architecture, que le permite marcar clases con interfaces, y proporciona objetos de adaptador para que pueda usar limpiamente un objeto como si tuviera alguna interfaz que no estaba diseñada para tener en primer lugar, será una mejor idea

Dicho esto, lo que está pidiendo es cambiar la clase, en el otro módulo, donde está, para que los cambios sean visibles para todos los demás módulos.

Hace exactamente eso: cambie la clase en el módulo al que pertenece. En Python se puede hacer simplemente atribuyendo su nueva clase al nombre deseado, en el módulo de origen:

import base_classes 

class Bookcollection(base_classes.Bookcollection): 
    new_member = "lalala" 

base_classes.Bookcollection = Bookcollection 

(Se recomienda encarecidamente evitar "de x import *" en cualquier proyecto más grande que la única secuencia de comandos - en este caso, tenía 2 variables con el mismo nombre y diferentes significados en todo su código: la clase base y la clase heredada, por ejemplo. Los espacios de nombres de Python le permiten evitar eso).

Por lo tanto, esto cambiará la clase Bookcollection en el módulo base_class - PERO solo para el código que lo hará referencia desde este punto y en su cadena de ejecución. Si la clase "x" en su ejemplo, se define en el módulo "base_classes", o se define antes de que se importe "MyModule", obtendrá una referencia a la antigua clase "Bookcollection".

Como puede ver, puede convertirse rápidamente en un desastre, y si realmente elige este enfoque, la única manera de mantener su proyecto incluso utilizable, es tener pruebas de unidad para verificar que todas las clases que desea reparar, en realidad están parcheados Incluso el orden de importación de los módulos marcará la diferencia, como puede ver. Si tiene lugar en las pruebas, se romperán si realiza las importaciones en un orden que rompa el parche de su mono.

Si solo necesita agregar y reemplazar cosas en una clase existente, puede parchear mono la clase para reemplazar sus componentes, en lugar de parchear el módulo en el que se encuentra para reemplazar la clase. De esta manera, el orden de importación de módulos no importa mucho - que afectará incluso a las instancias existentes de esa clase:

import base_classes 

base_classes.Bookcollection.new_member = "lalala" 

def new_bookcol_method(self): 
     pass 

# to replace or register a method in the other class: 
base_classes.Bookcollection.__dict__["old_bookcol_method"] = new_bookcol_method 

Esto le dará un comportamiento más consistente que intentar asignar una nueva clase (que es un objeto en sí mismo) al mismo nombre en el módulo original.

En general, debe hacer como @jamesj sugiere en su respuesta, y usar clases distintas, o si necesita el comportamiento dinámico, use un marco de mantenimiento para eso, como Zope Component Architecture. Y cualquiera que sea el enfoque que tome, haga pruebas de unidades de escritura.

+0

Gracias por esto. Resolvió un problema que tuve! – ehambright

1

Tener clases con el mismo nombre generalmente es una mala idea ya que se producen conflictos de espacios de nombres. Yo recomendaría cambiar su MyLibPackage.base_classes.Bookcollection a MyLibPackage.base_classes.BaseBookcollection o similar. Esto debería funcionar como se esperaba.

+0

Pero, ¿cómo debería funcionar cuando tengo la necesidad de cambiar solo algunas piezas de una clase base? Por ejemplo, una función. El resto de base_class.py funciona con sus espacios de nombres. Las llamadas en el resto de las clases base no conocen mis nuevas clases derivadas –

+0

Ok, luego cambian el nombre de su clase derivada a BookcollectionImproved o hacen algo como 'from base_classes import Bookcollection as BaseBookcollection'! – phobie

Cuestiones relacionadas