Search
Write a publication
Pull to refresh

Comments 15

Лямбду можно и более простым способом починить - через сохранение в ней текущего значения i. Хотя синтаксис этого и выглядит диковато:functions.append(lambda x,i=i: x + i)

Да, вы правы, это решение действительно проще фабрики и как раз полагается на способ работы со значениями по умолчанию. Я как-то упустил этот момент, спасибо!

выглядит диковато

Ну не обязательно же одинаковое имя использовать.

В Питоне это сплошь и рядом, когда при вызове функции её именованному аргументу присваивают значение из переменной с таким же именем, как у этого аргумента. Дело привычки к языку просто. Зачем плодить сущности и выдумывать другие имена? )

И получаем функцию, которая может принимать два аргумента. Повышаем ошибкоопасность.

По части диких синтаксисов - если лень писать именованную функцию высшего порядка, то можно и анонимную (или просто по месту). Лямбда, возвращающая лямбду.

(lambda i : (lambda x : x + i))(i)

Я, кстати, до этого даже и не думал, что это обычный дефолтный аргумент функции, не смог сложить 2+2, спасибо за просвещение )

А ещё вместо кучи лямбд можно создать нормальную функцию и сделать ей каррирование (частичный 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)

Мощный пример, хорошая иллюстрация

Видимо компилятор для питона не выйдет никогда 🤣🤣

Насколько же в Си всё чётко и понятно! )))

В C - может быть, а вот в C++ уже чёрт ногу сломит )

Sign up to leave a comment.