0

I have a dictionary of classes:

classes = { 'cls1' : Class1, 'cls2' : Class2 }

Here Class1, Class2 are Python classes I've defined, and each of them have methods foo() and bar().

Lets say I have another class called OtherClass. I want to iterate the above dictionary, and dynamically add some methods to the OtherClass as follows:

for k,v in classes.items():
  setattr(OtherClass, 'foo_' + k, lambda x: v().foo())
  setattr(OtherClass, 'bar_' + k, lambda x: v().bar())

This adds the methods foo_cls1, bar_cls1, foo_cls2, and bar_cls2 to OtherClass as expected. But it looks like calls to foo_cls1 and bar_cls1 get dispatched to foo_cls2 and bar_cls2 respectively. That is, the method definitions added later "overwrite" the previously added ones (assuming dictionary iteration order is cls1 followed by cls2). To further elaborate the problem, if I do the following after the above loop:

other = OtherClass()
other.foo_cls2() # Calls Class2.foo() -- Correct
other.foo_cls1() # Also calls Class2.foo() -- Unexpected behavior

other.bar_cls2() # Calls Class2.bar() -- Correct
other.bar_cls1() # Also calls Class2.bar() -- Unexpected behavior

Any idea what's going on, and how to fix it?

Hiranya Jayathilaka
  • 7,180
  • 1
  • 23
  • 34
  • 2
    Has to do with your use of lambda and how this binding occurs: See, e.g., https://stackoverflow.com/questions/7368522/weird-behavior-lambda-inside-list-comprehension – Two-Bit Alchemist Aug 04 '15 at 18:46
  • 1
    Also, why are you monkey-patching classes in this way? There is almost surely a better way to do whatever you're trying. – Two-Bit Alchemist Aug 04 '15 at 18:47
  • 2
    **TL;DR**: `setattr(OtherClass, 'foo_' + k, lambda x, v=v: v().foo())` – jonrsharpe Aug 04 '15 at 18:56
  • Lambda with the default argument trick works. Thanks for the quick responses. As for alternative approaches, what I'm trying to do is have the ability to expand the functionality of OtherClass based on the entries in classes dictionary. This requires adding new methods to OtherClass (it's just the way that class is designed, and I have no control over it). Thoughts? – Hiranya Jayathilaka Aug 04 '15 at 19:16
  • Or do not use lambda: `setattr(OtherClass, 'foo_' + k, v().foo)` – Richard_wth Aug 18 '19 at 06:03
  • After the loop ends, `v` has the same value, and is looked up the same by both `lambda`s. As an aside, the implementation of these monkey-patched methods should probably not create a new instance each time, but instead use the instance upon which they were called. Getting this binding right is tricky; please check out `functools.partialmethod`. – Karl Knechtel Aug 19 '22 at 02:56

0 Answers0