How to say $a=$b->{"A"} ||={} in Python?

S

Steve Holden

Carl said:
In perl it is just one line: $a=$b->{"A"} ||={}.
a = b.setdefault('A', {})
This combines all two actions together:
- Sets b['A'] to {} if it is not already defined
- Assigns b['A'] to a
More info on dict methods here:
http://docs.python.org/lib/typesmapping.html
No, this has already been proposed and discarded. The OP does NOT
want this, because it always generates an empty {} whether it is
needed or not. Not really a big hardship, but if the default value
were some expensive-to-construct container class, then you would be
creating one every time you wanted to reference a value, on the chance
that the key did not exist.

Carl Banks' post using defaultdict is the correct solution. The
raison d'etre for defaultdict, and the reason that it is the solution
to the OP's question, is that instead of creating a just-in-case
default value every time, the defaultdict itself is constructed with a
factory method of some kind (in practice, it appears that this factory
method is usually the list or dict class constructor). If a reference
to the defaultdict gives a not-yet-existing key, then the factory
method is called to construct the new value, that value is stored in
the dict with the given key, and the value is passed back to the
caller. No instances are created unless they are truly required for
initializing an entry for a never-before-seen key.


When I made my response, it occurred to me that Python could be
improved (maybe) if one could overload dict.get() to use a factory,
like so:

b = {}
a = b.get(k,factory=dict)
a['A'] = 1

That's a slight improvement (maybe) over defaultdict since it would
still allow the same dict to have the membership check in other
places. I'm not so sure overloading get to let it modify the dict is
a good idea, though.

Actually, it'd probably be fairly uncontroversial to add a factory
keyword to dict.setdefault(). At least insofar as setdefault is
uncontroversial.
Well it's uncontroversial enough to have made it into the distribution,
which represents a tacit admission by Guido that the .get() method alone
didn't implement his full vision.

The python-dev thread that discussed the feature before implementation
(as so often is the case) exercised many of the possible design paths,
and would be a useful read. [Damn, now I have to
well-known-search-engine it]. Aah, right - I'd forgotten how long it
took to get it right. This would be a suitable starting-point - it's the
beginning of the /third/ round of discussion:

http://mail.python.org/pipermail/python-dev/2006-February/061485.html

Many alternatives were discussed, and my memory at this distance is that
Guido had good reasons for choosing the exact API he did for defaultdict.

regards
Steve
--
Steve Holden +1 571 484 6266 +1 800 494 3119
Holden Web LLC/Ltd http://www.holdenweb.com
Skype: holdenweb http://del.icio.us/steve.holden
--------------- Asciimercial ------------------
Get on the web: Blog, lens and tag the Internet
Many services currently offer free registration
----------- Thank You for Reading -------------
 
N

Nick Craig-Wood

beginner said:
beginner said:
I'd like to do the following in more succint code:
if k in b:
a=b[k]
else:
a={}
b[k]=a

b.setdefault(k, a)

I am afraid it is not the same. b.setdefault(k, {}) will always create
an empty dict, even if k is in b

That is certainly true, but does it matter? You waste a very small
amount of time creating a dict you don't use.

$ python -m timeit '{}'
1000000 loops, best of 3: 0.247 usec per loop

On my machine 250 ns gets you a new dict...
 
N

Nick Craig-Wood

Paul McGuire said:
Carl Banks' post using defaultdict is the correct solution. The
raison d'etre for defaultdict, and the reason that it is the solution
to the OP's question, is that instead of creating a just-in-case
default value every time, the defaultdict itself is constructed with a
factory method of some kind (in practice, it appears that this factory
method is usually the list or dict class constructor). If a reference
to the defaultdict gives a not-yet-existing key, then the factory
method is called to construct the new value, that value is stored in
the dict with the given key, and the value is passed back to the
caller. No instances are created unless they are truly required for
initializing an entry for a never-before-seen key.

I think that if you truly want to emulate a perl hash then you would
want this which does the above but recursively.

from collections import defaultdict

class hash(defaultdict):
def __init__(self):
defaultdict.__init__(self, hash)

D=hash()

D[1][2][3][4]=5
D[1][4][5]=6

print D
 
P

Paddy

I think that if you truly want to emulate a perl hash then you would
want this which does the above but recursively.

from collections import defaultdict

class hash(defaultdict):
def __init__(self):
defaultdict.__init__(self, hash)

D=hash()

D[1][2][3][4]=5
D[1][4][5]=6

print D
Nick,
I thought I'd save your hash implementation away in my bag of tricks:


# File: autovivifying_dict.py
from collections import defaultdict
class hash(defaultdict):
""" Used like a dict except sub-dicts automagically created as
needed
Based on: http://groups.google.com/group/comp.lang.python/msg/f334fbdafe4afa37
>>> D=hash()
>>> D[1][2][3][4]=5
>>> D[1][4][5]=6
>>> D hash({1: hash({2: hash({3: hash({4: 5})}), 4: hash({5: 6})})})
>>> hash({1: hash({2: hash({3: hash({4: 5})}), 4: hash({7: 8})})}) hash({1: hash({2: hash({3: hash({4: 5})}), 4: hash({7: 8})})})
>>>

"""
def __init__(self, *a, **b):
defaultdict.__init__(self, hash, *a, **b)
def __repr__(self):
return "hash(%s)" % (repr(dict(self)),)

def _test():
import doctest
doctest.testmod()

if __name__ == "__main__":
_test()



- Paddy.
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Members online

No members online now.

Forum statistics

Threads
474,432
Messages
2,571,680
Members
48,796
Latest member
Greg L.

Latest Threads

Top