1

I'm trying to dynamically set methods to emulate a numeric object with the code below. But instead, every method is set to the last one in the loop. Why does this happen and how can I convince Python to DWIM?

#!/usr/bin/env python3.5
class Foo(float):
    pass

for tp in ("add", "sub", "mul", "truediv", "floordiv", "mod", "divmod", "pow"):
    methname = "__{:s}__".format(tp)
    print("defining", methname)
    def func(self, other):
        x = getattr(super(Foo, self), methname)(other)
        print("I calculated x={:.3f}!".format(x))
        return x
    func.__name__ = methname
    setattr(Foo, methname, func)

if __name__ == "__main__": # test
    print("Addition", Foo(3) + Foo(3))
    print("Multiplication", Foo(3) * Foo(3))

$ ./dyn.py
defining __add__
defining __sub__
defining __mul__
defining __truediv__
defining __floordiv__
defining __mod__
defining __divmod__
defining __pow__
I calculated x=27.000!
Addition 27.0
I calculated x=27.000!
Multiplication 27.0
gerrit
  • 24,025
  • 17
  • 97
  • 170

1 Answers1

1

The result 27.0 is calculated by 3**3, it is calling the function which is actually stored when calling in methname changed by the last loopiteration.

The variables used in func are referenced to the global variables. Try to put func into another method and pass the loop-variables as parameters, so the local variables in the function containing func now are constant and wouldn't be changed by the loop.

#create function with the given name
# \return the function
def makeFunc(tp):
    methname = "__{:s}__".format(tp)
    print("defining", methname)
    def func(self, other):
        x = getattr(super(Foo, self), methname)(other)
        print("I calculated x={:.3f}!".format(x))
        return x
    func.__name__ = methname
    return func


for tp in ("add", "sub", "mul", "truediv", "floordiv", "mod", "divmod", "pow"):
    func=makeFunc(tp)
    setattr(Foo, func.__name__, func)
cmdLP
  • 1,658
  • 9
  • 19
  • Sure. I figured it out with the linked answer, even though I'm not nesting any functions. Adding `methname=methname` to the signature of `func` did the trick. – gerrit Feb 24 '17 at 00:54