dulrich - 4 months ago 12

Python Question

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)`

Answer

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.