59

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'll add a method like this

def bar():
   print 123

MyClass.foo = bar

this will work and foo method will be available everywhere else.

How do I replace the class completely?

Mechanical snail
  • 29,755
  • 14
  • 88
  • 113
ZooKeeper
  • 685
  • 1
  • 6
  • 7

4 Answers4

77
import module
class ReplaceClass(object):
    ....
module.MyClass = ReplaceClass
Glenn Maynard
  • 55,829
  • 10
  • 121
  • 131
  • 3
    +1 for being faster. just be sure that this runs before any imports that use `module.MyClass` – aaronasterling Sep 21 '10 at 23:32
  • 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. – Glenn Maynard Sep 21 '10 at 23:37
  • 21
    Make sure you're the "first" to import the class. Already made references to the "old" class will not be replaced! – Ivo van der Wijk Sep 22 '10 at 06:20
  • 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. – ZooKeeper Sep 22 '10 at 20:30
  • will this affect only within this file or change the behaviour globally – E_g Aug 11 '21 at 13:34
37

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.

Alex Martelli
  • 854,459
  • 170
  • 1,222
  • 1,395
  • 2
    +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 Sep 21 '10 at 23:43
  • 3
    @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. – Alex Martelli Sep 21 '10 at 23:49
  • Can I add these strings to __init__.py to make sure that this replacement happens first? – uuu777 Apr 29 '20 at 19:33
17

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.

chernevik
  • 3,922
  • 9
  • 41
  • 55
  • 8
    +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. – Justin Force Aug 30 '12 at 21:17
2
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 !

shahjapan
  • 13,637
  • 22
  • 74
  • 104
  • 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. – aaronasterling Sep 22 '10 at 05:49
  • @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 ! – shahjapan Sep 22 '10 at 06:27
  • 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()`. – aaronasterling Sep 22 '10 at 06:53
  • @shajapan: what recursion? This seems to work with no problem. Is there some problem I am not aware of? – Tom Jul 31 '12 at 10:16