Cómo sobrescribir una clase de python importada para todas las llamadas

Creo un paquete / MyLibPackage de python que importaré en mis proyectos.

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

mymodiciation.py importa ” from base_classes import * “.

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

Sobrescribo estas 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 hago 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 propiedad “new_member” porque está instanciado con MyLibPackage.base_classes.Bookcollection y no con mi clase sobrescrita MyLibPackage.mymodiciation.Bookcollection

¿Cómo puedo decir? Si sobrescribo una clase en mymodiciation, MyLibPackage tiene que usar esto, aunque cuando la llamada se llame desde MyLibPackage.base_classes.shelf (get_books).

Lo que desea hacer se llama “parches de mono” y tiene poco que ver con la orientación de objetos.

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

Tal vez sea una mejor idea usar un marco como la Arquitectura de componentes Zope, que le permite marcar clases con interfaces, y proporciona objetos de adaptador para que pueda usar limpiamente un objeto que tenga una interfaz que no fue diseñada para tener en primer lugar.

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.

Haz exactamente eso: cambia 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 “desde x importar *” en cualquier proyecto más grande que un solo script; en este caso, tenía 2 variables con el mismo nombre y diferentes significados en todo el código: la clase base y la clase heredada, por ejemplo). Ejemplo. Los espacios de nombres de Python le permiten evitar eso).

Por lo tanto, esto cambiará la clase de colección de libros en el módulo base_class, PERO solo para el código que hará referencia a partir de 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 de lo contrario 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 forma de mantener su proyecto incluso utilizable, es hacer pruebas unitarias para verificar que todas las clases que desea parchear, en realidad son parcheadas. Incluso el orden de importación de los módulos marcará la diferencia, como puede ver. Si tiene un lugar de pruebas, se interrumpirán si realiza importaciones en un orden que rompe su parche de mono.

Si solo necesita agregar y reemplazar elementos en una clase existente, puede parchear la clase en sí para reemplazar sus componentes, en lugar de parchear el módulo en el que está para reemplazar la clase. De esta manera, el orden de importación de los módulos no importará mucho, 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 lo que @jamesj sugiere en su respuesta, y usar clases distintas, o si necesita el comportamiento dynamic, use un marco que se pueda mantener para eso, como Zope Component Architecture. Y sea cual sea el enfoque que tomes, escribe pruebas unitarias.

Tener clases con el mismo nombre suele ser una mala idea, ya que se producen conflictos de espacio de nombres. Recomendaría cambiar su MyLibPackage.base_classes.Bookcollection a MyLibPackage.base_classes.BaseBookcollection o similar. Esto debería funcionar como se esperaba.