Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Using __iadd__ in a lambda?

I was trying to solve this little challenge:

You want a defaultdict that uses a different value every time a non-existent key is looked up, i.e. a counter; 1 for the first non-existent key, 2 for the second, etc.

I was able to solve it like:

from collections import defaultdict

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

s=defaultdict(f)

The bonus was to try to do this in one line.

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

SyntaxError: invalid syntax _________^

Is there a way to do this in a lambda? Update the mutable default arg and return?

like image 911
bison Avatar asked Dec 08 '22 12:12

bison


1 Answers

There are two problems:

  1. ... += ... is a statement, not an expression. You could call __iadd__ explicitly, except ...
  2. int doesn't define __iadd__; a[0] += 1 is just implemented as a[0] = a[0] + 1.

What you need to do is call __setitem__ explicitly to update the 0th element of the default list value. That returns None, not a[0], so you'll also need an or expression to return the value of a[0] (since 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

(I'd write defaultdict(itertools.count(0).__next__) myself.)

like image 66
chepner Avatar answered Dec 19 '22 07:12

chepner