settattr for parent class to use in children

443 views Asked by At

I have a library with one parent and a dozen of children:

# mylib1.py:
#
class Foo(object):
  def __init__(self, a):
    self.a = a


class FooChild(Foo):
  def __init__(self, a, b):
    super(FooChild, self).__init__(a)
    self.b = b

# more children here...

Now I want to extend that library with a simple (but a bit spesific, for use in another approach) method. So I would like to change parent class and use it's children.

# mylib2.py:
#
import mylib1

def fooMethod(self):
  print 'a={}, b={}'.format(self.a, self.b)

setattr(mylib1.Foo, 'fooMethod', fooMethod)

And now I can use it like this:

# way ONE:
import mylib2

fc = mylib2.mylib1.FooChild(3, 4)
fc.fooMethod()

or like this:

# way TWO:
# order doesn't matter here:
import mylib1
import mylib2

fc = mylib1.FooChild(3, 4)

fc.fooMethod()

So, my questions are:

  1. Is this good thing?
  2. How this should be done in a better way?
2

There are 2 answers

0
Ali SAID OMAR On

A common approach is to use mixin

If you want, you could add dynamically How do I dynamically add mixins as base classes without getting MRO errors?.

2
zefciu On

There is a general rule in programming, that you should avoid dependence on global state. Which in other words means that your globals should be if possible constant. Classes are (mostly) globals.

Your approach is called monkey patching. And if you don't have a really really good reason to explain it, you should avoid it. This is because monkey patching violates the above rule.

Imagine you have two separate modules and both of them use this approach. One of them sets Foo.fooMethod to some method. The other - to another. Then you somehow switch control between these modules. The result would be, that it would be hard to determine what fooMethod is used where. This means hard to debug problems.

There are people (e.g. Brandon Craig-Rhodes), who believe that patching is bad even in tests.

What I would suggest is to use some attribute that you would set when instantiating instances of your Foo() class (and its children), that would control the behaviour of your fooMethod. Then the behaviour of this method would depend on how you instantiated the object, not on global state.