Используете __iadd__ в лямбде? - PullRequest
4 голосов
/ 01 мая 2019

Я пытался решить эту маленькую задачу:

Вы хотите defaultdict, который использует другое значение каждый раз, когда ищется несуществующий ключ, то есть счетчик;1 для первого несуществующего ключа, 2 для второго и т. Д.

Я смог решить его следующим образом:

from collections import defaultdict

def f(a=[0]):
    a[0]+=1
    return a[0]

s=defaultdict(f)

Бонус должен был попытаться сделать этов одну строку.

s=defaultdict(lambda a=[0]: (a[0]+=1))

SyntaxError: неверный синтаксис _________ ^

Есть ли способ сделать это в лямбда-выражении?Обновить изменяемый аргумент по умолчанию и вернуть?

Ответы [ 5 ]

6 голосов
/ 01 мая 2019

Есть две проблемы:

  1. ... += ... это утверждение, а не выражение. Вы можете вызвать __iadd__ явно, кроме ...
  2. int не определяет __iadd__; a[0] += 1 просто реализован как a[0] = a[0] + 1.

Вам нужно явно вызвать __setitem__, чтобы обновить 0-й элемент списка по умолчанию. Возвращает None, а не a[0], поэтому вам также понадобится выражение or для возврата значения a[0]None or x == x):

>>> s = defaultdict(lambda a=[0]: a.__setitem__(0, a[0] + 1) or a[0])
>>> s[6]
1
>>> s[9]
2
>>> s[6]
1
>>> s[8]
3

(я бы написал defaultdict(itertools.count(0).__next__) сам.)

3 голосов
/ 01 мая 2019

operator на помощь:

d = defaultdict(lambda a = [0]: operator.setitem(a, 0, a[0] + 1) or a[0])
2 голосов
/ 01 мая 2019

Хотя есть много способов решить эту проблему в одной строке, я вижу много вопросов относительно того, можно ли здесь использовать operator.iadd.

Не может.

Конечно, это появится , что

s = defaultdict(lambda a=[0]: operator.iadd(a[0], 1))

будет работать, но это не так.

Причина в официальных документах

Многие операции имеют версию «на месте».Ниже перечислены функции, обеспечивающие более простой доступ к операторам на месте, чем обычный синтаксис;например, оператор x + = y эквивалентен x = operator.iadd (x, y).Другой способ выразить это - сказать, что z = operator.iadd (x, y) эквивалентно составному выражению z = x;z + = y.

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

Для неизменяемых целей, таких как строки, числа и кортежи, обновленное значение вычисляется, но не присваивается обратно входной переменной:

>>> a = 'hello'
>>> iadd(a, ' world')
'hello world'
>>> a
'hello'

Для изменяемых целей, таких как списки и словари, метод на месте выполнит обновление, поэтому последующее присваивание не требуется.

Поскольку int объекты являются неизменяемыми, вы не можете обновить a[0] с помощью iadd.

(я знаю, что это было сказано в других ответах; я просто подумал, что определенный ответ со ссылкой на документы по Python может быть полезен.)

2 голосов
/ 01 мая 2019

Значит ли импорт как часть одной строки?

Если нет, вы можете использовать itertools.count

from itertools import count
d = defaultdict(count().__next__)

Если они это сделают, вы можете использовать слегка уродливый хак

d = defaultdict(__import__('itertools').count().__next__)
1 голос
/ 01 мая 2019

Как насчет использования изменяемого типа, такого как список, для отслеживания количества?

s = defaultdict(lambda a=[] : len(a.__iadd__([1])))

Чтобы уменьшить использование памяти:

s = defaultdict(lambda a=[0] : a.__iadd__([a.pop() + 1])[0])
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...