dulrich - 1 year ago 72
Python Question

# Why doesn't this method, redefined at runtime, retain its value?

I'm stuck on something I suspect is quite simple, but I just can't wrap my head around it. I'm trying to create a class with a method that can be redefined on the fly. I want to be able to do this for an arbitrary number of instances, but I'm only showing two here in order to keep things simple.

Here is a MWE of my code:

``````class Foo():
def __init__(self, z):
self.z = z

def f(self, t):
return 0

def f(obj1, obj2, t):
return (obj1.z - obj2.z) * t

a, b = Foo(3), Foo(5)
print(a.f(1), b.f(1)) # --> 0, 0

x, y = a, b
x.f = lambda t: f(x, y, t)
print(a.f(1), b.f(1)) # --> -2, 0

x, y = b, a
x.f = lambda t: f(x, y, t)
print(a.f(1), b.f(1)) # --> 2, 2
``````

Why does the value of
`a.f(1)`
change?

It changes because you are modifying the `x` and `y` global variables, which are referred to by the `f` function you defined:

``````In [2]: a, b = Foo(3), Foo(5)

In [3]: print(a.f(1), b.f(1))
0 0

In [4]: x, y = a, b
...: x.f = lambda t: f(x, y, t)
...: print(a.f(1), b.f(1))
...:
-2 0

In [5]: x, y = b, a

In [6]: print(a.f(1), b.f(1))  # you changed x and y
2 0
``````

After you swap `x` and `y` you have that `a.f` is the function `lambda t: f(x, y, t)` which means it calls `f(b, a, t)` and since `b = Foo(5)` and `a = Foo(3)` you have `5-3 == 2` instead of `-2`.

If you want to fix the value passed by `f` and "unlink" it from the global variables you could use default arguments:

``````In [2]: x, y = a, b
...: x.f = lambda t, x=x, y=y: f(x, y, t)
...:

In [3]: print(a.f(1), b.f(1))
-2 0

In [4]: x, y = b, a

In [5]: print(a.f(1), b.f(1))
-2 0
``````

Since default values are evaluated at definition time, by using `lambda t, x=x, y=y` you end up fixing the values of `x` and `y` as they were when the function was defined, so that when you subsequently swap them this doesn't affect that function.

Recommended from our users: Dynamic Network Monitoring from WhatsUp Gold from IPSwitch. Free Download