【问题标题】:How passing arguments to nested functions work in the background in python?在 python 的后台如何将参数传递给嵌套函数?
【发布时间】:2019-10-26 22:59:26
【问题描述】:

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

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 的值在循环中被更改。但我不太明白这两种情况到底发生了什么。以下是一些问题:

  • 它是传递给_add_method 的行星变量的副本吗?
  • 为什么传递给_add_method的值没有变化,而直接在循环中传递时会发生变化?

【问题讨论】:

  • @Sayse 他们是,但他们都应该有不同的论点。 lambdas 中的 python 参数绑定一定有什么可疑之处。
  • @MT13 你用调试器运行过吗?
  • 我认为这个线程密切相关:stackoverflow.com/questions/10452770/…

标签: python python-3.x lambda nested-function


【解决方案1】:
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。

【讨论】:

  • 感谢您的回答。我阅读了您提供的链接,并进一步阅读了有关早期和晚期绑定的信息。现在很清楚为什么它只打印最后一个值。那么调用该方法呢?据我了解,当我调用 _add_method 时,它还会将当前值绑定到该方法,就像它与解决方案中的默认值一样,并且在该函数的范围内它会保留该值,对吗?
  • 问题是变量planet__init__函数中只定义了一次,放在lambda函数中并不会创建第二个变量。一旦 planet 引用指向下一次迭代的新字符串,就会直接影响 lambda。在 lambda 中重新定义它会创建一个指向同一字符串的新变量,因此不受 for 变量的实际值的影响。使用变量作为参数做同样的事情。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2021-11-18
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多