How to overwrite imported python class for all calls

I am creating python-packages / MyLibPackage which I will import into my projects.

MyLibPackage.____init____.py includes mymodiciation.py. In addition, the MyLibPackage folder contains another file: base_classes.py (= external project)

mymodiciation.py imports " from base_classes import * ".

Purpose: I can import MyLibPackage, which has all the classes from base_classes (= external project). And if I need to modify some classes or functions, I can rewrite this in mymodiciation.py. It works, but I have a problem. For instance:

I am rewriting these classes in mymodiciation.py:

 class Bookcollection(Bookcollection): new_member = "lalala" class user(user): def get_books(self): return Bookcollection() 

if:

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

then the Bookcollection object has the property "new_member". Good! But if I do this:

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

then the Bookcollection object does NOT have the property "new_member" because it is an instance with MyLibPackage.base_classes.Bookcollection, and not with my rewritten class MyLibPackage.mymodiciation.Bookcollection

How can I say: if I overwrite the class in mymodiciation, then MyLibPackage should use this, although when calling cames from MyLibPackage.base_classes.shelf (get_books).

+12
source share
2 answers

What you want to do is called β€œfix monkeys” and has nothing to do with object orientation.

Python supports it, but you have control over all your classes, you should seriously review your project to see if you really need it.

Maybe it’s better to use an infrastructure such as Zope Component Architecture, which allows you to mark classes with interfaces and provides adapter objects so that you can easily use one object, because it has some interface for which it was not originally intended.

However, you ask to change the class in another module, where it is located - so that the changes are visible to all other modules.

You do just that: change the class in the module to which it belongs. In Python, this can be done by simply giving your new class the name you want in the origin module:

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

(For such things to work, you must avoid "from x import *" in any project exceeding one script - in this case you had 2 variables with the same name and different values ​​in the code: the base class and the inherited class, for example. Namespaces Python avoids this).

Thus, this will change the Bookcollection class in the base_class module - BUT only for the code that will refer to it from now on and on in your run chain. If the class "x" in your example is defined in the module "base_classes" or otherwise defined before importing "MyModule", it will receive a link to the old class "Bookcollection".

As you can see, this can quickly turn into a mess, and if you really choose this approach, the only way to keep your project even usable is to do unit tests to make sure that all the classes you want to patch are really patched. As you can see, even the import order of the modules will matter. If you have room for tests, they will be damaged if you import in an order that violates your monkey fixes.

If you just need to add and replace things in an existing class, you can monkeys fix the class itself to replace its components, instead of securing the module it is in to replace the class. Thus, the import order of the modules will not matter much - it will even affect existing instances of this class:

  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 

This will give you more consistent behavior than trying to assign a new class (which is itself an object) to the same name in the source module.

In general, you should either do as @jamesj prompts in your answer and use different classes, or, if you need dynamic behavior, use a supported environment for this, for example Zope Component Architecture. And whatever approach you choose, write unit tests.

+19
source

Having classes with the same name is usually a bad idea, as you get namespace conflicts. I would recommend changing your MyLibPackage.base_classes.Bookcollection to MyLibPackage.base_classes.BaseBookcollection or similar. This should work as expected.

+1
source

Source: https://habr.com/ru/post/912486/


All Articles