62

I've got a class, located in a separate module, which I can't change.

from module import MyClass

class ReplaceClass(object)
  ...

MyClass = ReplaceClass

This doesn't change MyClass anywhere else but this file. However if I add a method to the class like this

def bar():
   print 123

MyClass.foo = bar

it will work, but the foo method will be available everywhere else as well.

How can I replace the class completely instead of replacing the method?

0

4 Answers 4

82
import module
class ReplaceClass(object):
    ....
module.MyClass = ReplaceClass
Sign up to request clarification or add additional context in comments.

5 Comments

+1 for being faster. just be sure that this runs before any imports that use module.MyClass
Being faster doesn't make more correct, but I don't really think this needs additional explanation anyway. The difference should be pretty obvious once you see how to do it.
Make sure you're the "first" to import the class. Already made references to the "old" class will not be replaced!
Thank you so much! Ivo made the point that actually was my mistake. I was not paying attention to the order of inclusion, which doesn't seam to matter while overloading or adding methods to the class, since they are called only when used.
will this affect only within this file or change the behaviour globally
42

Avoid the from ... import (horrid;-) way to get barenames when what you need most often are qualified names. Once you do things the right Pythonic way:

import module

class ReplaceClass(object): ...

module.MyClass = ReplaceClass

This way, you're monkeypatching the module object, which is what you need and will work when that module is used for others. With the from ... form, you just don't have the module object (one way to look at the glaring defect of most people's use of from ...) and so you're obviously worse off;-);

The one way in which I recommend using the from statement is to import a module from within a package:

from some.package.here import amodule

so you're still getting the module object and will use qualified names for all the names in that module.

4 Comments

+1 but note module.MyClass=, though it will replace the class in the module, won't replace the class in any other modules that have already done from module import MyClass. They'll have their own separate copies. Monkey-patching is bad mmkay; from module import is also bad mmkay; both at the same time is a recipe for stuff-going-wrong.
@bobince, absolutely! If other parts of the code also use my detested from module import MyClass, finding and monkeying around with all of them is somewhere between "a mess" and "impossible" (gc.get_referrers can sometimes help... but not always!-). Yeah, they're both bad practice, but sometimes monkeypatching CAN be the least of evils, while creating "artificial barenames" with the from statement can always be dispensed with.
Can I add these strings to init.py to make sure that this replacement happens first?
So you are saying that it is impossible to monkeypatch from module import MyClass?
18

I am but an egg . . . . Perhaps it is obvious to not-newbies, but I needed the from some.package.module import module idiom.

I had to modify one method of GenerallyHelpfulClass. This failed:

import some.package.module

class SpeciallyHelpfulClass(some.package.module.GenerallyHelpfulClass): 
    def general_method(self):...

some.package.module.GenerallyHelpfulClass = SpeciallyHelpfulClass

The code ran, but didn't use the behaviors overloaded onto SpeciallyHelpfulClass.

This worked:

from some.package import module

class SpeciallyHelpfulClass(module.GenerallyHelpfulClass): 
    def general_method(self):...

module.GenerallyHelpfulClass = SpeciallyHelpfulClass

I speculate that the from ... import idiom 'gets the module', as Alex wrote, as it will be picked up by other modules in the package. Speculating further, the longer dotted reference seems to bring the module into the namespace with the import by long dotted reference, but doesn't change the module used by other namespaces. Thus changes to the import module would only appear in the name space where they were made. It's as if there were two copies of the same module, each available under slightly different references.

2 Comments

+1 to this because I find it more helpful to talk about extending the class that you want to patch then replacing it, rather than just extending Object and replacing it.
"Speculating further, ..." Heads up that basically everything after this point is wrong. While it is possible to accidentally create two versions of a module, this requires different qualified names; the qualified names here are identical, even if they are broken apart differently. The answer could be improved by editing it to remove the tangent on deep imports, as the advice on subclassing itself is sound and sets the answer apart from others.
1
import some_module_name

class MyClass(object): 
     ... #copy/paste source class and update/add your logic

some_module_name.MyClass = MyClass

Its preferable not to change the name of class while replacing, because somehow someone may have referenced them using getattr - which will result in fail like below

getattr(some_module_name, 'MyClass') --> which will fail if you have replaced MyClass by ReplaceClass !

4 Comments

1) this will only take effect in the module performing the import: adding as foo does nothing to help. 2) your argument about getattr is not just confusing (because of your inconsistent use of names) but wrong as well and confuses the name of the class with the class itself. if I import somemodule; somemodule.someclass = someotherclass, then at a later point, in another module, getattr(somemodule, 'someclass') will return someotherclass which is exactly what OP wants. 3) copy-paste programming is ugly and error-prone.
@AaronMcSmooth, Agree !, now I've edited the answer, but as OP wants to replace the whole class,so I wrote copy/paste and add/update logic, because its not possible to inherit that original class as we're gonna replace it and if OP wants to update the logic only for few lines then rest code he needs to get from original source !
now your solution is the same as the two that have already been presented with the added blemish of a (still!) incorrect statement about getattr Also, why can't we inherit from a class that we're about to 'replace'? class A(object):pass; class B(A): pass; A = B; a = A().
@shajapan: what recursion? This seems to work with no problem. Is there some problem I am not aware of?

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.