Comments 15
Лямбду можно и более простым способом починить - через сохранение в ней текущего значения i
. Хотя синтаксис этого и выглядит диковато:functions.append(lambda x,i=i: x + i)
Да, вы правы, это решение действительно проще фабрики и как раз полагается на способ работы со значениями по умолчанию. Я как-то упустил этот момент, спасибо!
выглядит диковато
Ну не обязательно же одинаковое имя использовать.
И получаем функцию, которая может принимать два аргумента. Повышаем ошибкоопасность.
По части диких синтаксисов - если лень писать именованную функцию высшего порядка, то можно и анонимную (или просто по месту). Лямбда, возвращающая лямбду.
(lambda i : (lambda x : x + i))(i)
А ещё вместо кучи лямбд можно создать нормальную функцию и сделать ей каррирование (частичный apply) с помощью functools.partial, типа
def adder(x: int, i: int):
return x+i
funcs = [partial(adder, i=i) for i in range(3)]
# или даже с лямдой
adder = lambda x, i: x + i
funcs = [partial(adder, i=i) for i in range(3)]
Уникальные лямбды с немедленным связыванием текущего значения можно рожать вот так:
for i in range(3):
functions.append(
(lambda the_i :
(lambda x : x + the_i)
)(i)
)
Для полноты примера надо было бы вот такое сделать
def outer():
def inner():
fs = []
for I in range(3):
fs.append((lambda i: (lambda x : [x,i,I,O,G]))(I))
I = 'innervar'
return fs
fs = inner()
O = 'outervar'
return fs
fs = outer()
G = 'globalvar'
O = 'hacked' # введём глобальные переменные
I = 'hacked' # в ТЩЕТНОЙ надежде на конфликт
i = 'hacked' # с именами внутри лямбды
print([f('xxx') for f in fs])
del G
print([f('xxx') for f in fs]) # NameError: name 'G' is not defined
выведет
[['xxx', 0, 'innervar', 'outervar', 'globalvar'],
['xxx', 1, 'innervar', 'outervar', 'globalvar'],
['xxx', 2, 'innervar', 'outervar', 'globalvar']]
Пояснение:
I обнаружена транслятором питона как локальная переменная, будет связана с контекстом функции inner.
O - нелокальная переменная (можно не писать nonlocal, поскольку мы не присваиваем, а значит, неоднозначности синтаксиса нет), будет связана с контекстом функции outer
G по умолчанию - глобальное имя, будем искать его каждый раз ровно в момент обращения
i - локальная переменная-аргумент внешней лямбды, будет связана с контекстом её вызова (куда подставлено конкретное значение I)
Видимо компилятор для питона не выйдет никогда 🤣🤣
Насколько же в Си всё чётко и понятно! )))
5 листингов для лучшего понимания Python