Here is the random problem I was solving the other day. Given an age in seconds, one must calculate how old someone would be on particular planet. I was trying to add new methods to my class dynamically and came up with this solution:
class MyClass(object):
year_in_seconds_on_earth = 31557600
planets = {
'earth': 1,
'mercury': 0.2408467,
'venus': 0.61519726,
'mars': 1.8808158,
'jupiter': 11.862615,
'saturn': 29.447498,
'uranus': 84.016846,
'neptune': 164.79132
}
def __init__(self, seconds):
self.seconds = seconds
for planet in self.planets:
func = lambda: self._on_planet(planet)
self.__setattr__('on_' + planet, func)
# self._add_method(planet)
# def _add_method(self, planet):
# func = lambda: self._on_planet(planet)
# self.__setattr__('on_' + planet, func)
def _on_planet(self, planet):
return round(self.seconds / self.year_in_seconds_on_earth / self.planets[planet], 2)
print(MyClass(2134835688).on_mercury())
So when I call lambda
and setattr
from the separate method (commented part), it works perfectly fine. But when they are called from __init__
, only the last value, neptune, is used when calling on_mercury
, on_mars
or other similar methods.
I understand that in __init__
it takes the value from the closure of outer function and planet
s value is changed in the loop. But I don't quite understand what exactly is happening in both cases. Here are some questions:
- Is it a copy of planet variable passed to
_add_method
? - Why doesn't the value passed to
_add_method
change, but changes when passed directly in the loop?