Как передача аргументов во вложенные функции работает в фоновом режиме в Python? - PullRequest
1 голос
/ 12 июня 2019

Вот случайная проблема, которую я решал на днях.Учитывая возраст в секундах, нужно рассчитать, сколько лет кому-то будет на конкретной планете.Я пытался динамически добавлять новые методы в свой класс и придумал следующее решение:

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

Поэтому, когда я вызываю lambda и setattr из отдельного метода (закомментированная часть), он работает отличнохорошо.Но когда они вызываются из __init__, при вызове on_mercury, on_mars или других подобных методов используется только последнее значение, neptune.

Я понимаю, что в __init__ оно принимает значениеот замыкания внешней функции и значение planet s изменяется в цикле.Но я не совсем понимаю, что именно происходит в обоих случаях.Вот некоторые вопросы:

  • Это копия переменной планеты, переданная в _add_method?
  • Почему значение, переданное в _add_method, не изменяется, а изменяется при его передачепрямо в петле?

1 Ответ

1 голос
/ 12 июня 2019
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-лямбды к локальным значениям

Таким образом, лямбда-Python содержит только ссылки на внешние переменные.Если значение изменяется, переменная изменяется.

Определив локальную переменную со значением внешней переменной, в этом случае с помощью planet=planet, вы можете связать значение с лямбда-выражением во время определения.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...