Сначала просто хочу сказать, что я новичок в python и использую python версии 3.6.5. Я не очень-то согласен с тем, как Python обрабатывает ввод функции как «ссылки на объекты».
Приведенные здесь примеры действительно просты, чтобы показать мою точку зрения.
Это первый код, который я запускаю (неправильное поведение):
integer = 0
def func(t): return integer
class FooClass(object):
def __init__(self, input_function):
self.function = input_function
def give_value(self, index):
print(self.function(index))
foo_object = FooClass(func)
foo_object.give_value(0)
# Output >> 0
integer = 1
foo_object.give_value(0)
# Output >> 1
Я хочу, чтобы мой объект FooClass был полностью независим от внешних изменений.
Это второй код, который я запускаю (правильное поведение):
integer = 0
def step_function(integer_input):
def func(t): return integer_input
return FooClass(func)
class FooClass(object):
def __init__(self, input_function):
self.function = input_function
def give_value(self, index):
print(self.function(index))
foo_object = step_function(integer)
foo_object.give_value(0)
# Output >> 0
integer = 1
foo_object.give_value(0)
# Output >> 0
Я доволен этим, но не понимаю, почему это работает. На самом деле, я боюсь, что моя функция self.function относится к 'integer_input', определенному во время вызова step_function. Так что это относится к доступному пространству памяти, которое можно переопределить (если это имеет смысл в Python).
Заранее спасибо!
@timgeb Хорошо, спасибо, я отредактирую вопрос, чтобы было легче понять. Я вернусь после просмотра видео.
Если вы переходите с другого языка, может быть сложно привыкнуть к модели данных Python. Вы можете найти эту статью полезной: Факты и мифы об именах и ценностях Python, написанную ветераном SO Недом Батчелдером. Более короткий и несколько иной взгляд на эту тему см. В В других языках есть «переменные», в Python - «имена»..
Итак, чтобы объяснить, почему ваши коды работают по-другому: integer
- это имя, которое относится к целому числу. В вашем первом примере вы используете это имя в func
, и это создает замыкание, которое включает ссылку на integer
из области Глобальный и передает ее. Таким образом, все будущие вызовы ссылаются на то, что содержится под именем integer
в глобальной области видимости.
Во втором примере вы создаете новое имя integer_input
для ссылки на integer
и ссылки на integer_input
в func
. Это создает замыкание, которое оборачивает ссылку на integer_input
из области функция и передает ее.
Если бы вы каким-то образом смогли изменить то, на что ссылается integer_input
в вашем прицеле step_function
, вы столкнулись бы с той же проблемой, но это было бы трудно сделать (и практически невозможно сделать непреднамеренно).
Если вы хотите обновить свой первый пример, чтобы он работал так, как вы ожидаете, вы можете вместо этого использовать следующее:
func = (lambda value: lambda t: value)(integer)
Это вручную создает замыкание для отделения названиеinteger
от объект, на которое ссылается integer
, и аналогично вашему второму примеру.
Или, чтобы обновить вашу текущую структуру, чтобы сделать что-то подобное:
def step_function(integer_input):
def inner(t): return integer_input
return inner
func = step_function(integer)
Обратите внимание, что эти два подхода эквивалентны, отличается только синтаксис.
Спасибо! Я отредактировал код с помощью более простого примера, поэтому я не знаю, следует ли мне отменить редактирование или вы можете адаптировать свой ответ к новому коду. Если я понимаю ваш ответ, переменная bar остается в области действия функции, но эта область все еще существует после вызова foo?
Ваш ответ действительно понятен, спасибо! Я знаю, что это не самый чистый способ, но в этом коде есть много вещей, и на данный момент это был самый простой и быстрый способ сделать это для меня. Но я найду способы получше!
@ LA.Dufrene Да, область видимости все еще существует после return FooClass(func)
, потому что FooClass
хранит ссылку на func
, а func
должен знать значение integer_input
, поэтому это заключено в закрытие.
Спасибо за новый ответ. Я прекрасно понимаю, что сейчас происходит!
Мне чрезвычайно сложно понять ваш код, потому что все имена переменных представляют собой бессмысленные буквы (отредактируйте: некоторые из которых вы повторное использование!). Кроме того, не могли бы вы привести более короткий пример, демонстрирующий то же поведение, которое вас раздражает? А пока у меня есть подозрение, что это вам поможет в любом случае.