python 如何在后台向嵌套函数传递参数?

How passing arguments to nested functions work in the background in python?

这是我前几天解决的随机问题。给定以秒为单位的年龄,必须计算某人在特定星球上的年龄。我试图动态地向我的 class 添加新方法并想出了这个解决方案:

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

所以当我从单独的方法(注释部分)调用 lambdasetattr 时,它工作得很好。但是当它们从 __init__ 调用时,在调用 on_mercuryon_mars 或其他类似方法时只使用最后一个值 neptune。

我知道在 __init__ 中它从外部函数的闭包中获取值并且 planets 值在循环中被改变。但我不太明白这两种情况到底发生了什么。以下是一些问题:

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 planet=planet: 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())

可以在此处阅读有关其工作原理的详细信息: Python lambda's binding to local values

总而言之,python lambda 只包含对外部变量的引用。如果值改变,变量也会改变。

通过使用外部变量的值定义局部变量,在本例中为 planet=planet,您可以在定义时将值绑定到 lambda。