Append a new value to dict

P

Pat

I know it's not "fair" to compare language features, but it seems to me
(a Python newbie) that appending a new key/value to a dict in Python is
awfully cumbersome.

In Python, this is the best code I could come up with for adding a new
key, value to a dict

mytable.setdefault( k, [] ).append( v )

In Perl, the code looks like this:

$h{ $key } = $value ;

Is there a better/easier way to code this in Python than the
obtuse/arcane setdefault code?
 
P

Pat

Pat said:
I know it's not "fair" to compare language features, but it seems to me
(a Python newbie) that appending a new key/value to a dict in Python is
awfully cumbersome.

In Python, this is the best code I could come up with for adding a new
key, value to a dict

mytable.setdefault( k, [] ).append( v )

In Perl, the code looks like this:

$h{ $key } = $value ;

Is there a better/easier way to code this in Python than the
obtuse/arcane setdefault code?

Naturally, right after writing my post I found that there is an easier way:

table[ k ] = v

I found that in "Python for Dummies". How apropos.
 
P

paul

Pat said:
I know it's not "fair" to compare language features, but it seems to me
(a Python newbie) that appending a new key/value to a dict in Python is
awfully cumbersome.

In Python, this is the best code I could come up with for adding a new
key, value to a dict

mytable.setdefault( k, [] ).append( v )

In Perl, the code looks like this:

$h{ $key } = $value ;
Whats wrong with:

mytable[key] = value

cheers
Paul
 
M

Mathias Frey

Pat said:
I know it's not "fair" to compare language features, but it seems to me
(a Python newbie) that appending a new key/value to a dict in Python is
awfully cumbersome.

In Python, this is the best code I could come up with for adding a new
key, value to a dict

mytable.setdefault( k, [] ).append( v )

In Perl, the code looks like this:

$h{ $key } = $value ;

There's a huge difference here:

In your Python example you're using a list. In the Perl example you're
using a scalar value.

Is there a better/easier way to code this in Python than the
obtuse/arcane setdefault code?

When just assigning a new key-value-pair there's no problem in Python.
(Just refer to the answers before.) When I switched from Perl to Python
however I commonly ran into this problem:
>>> counter = {}
>>> counter['A'] = 1
>>> counter['A'] += 1
>>> counter['A']
2

Ok - assigning a key-value-pair works fine. Incrementing works as well.
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
KeyError: 'B'

However incrementing a non-existing key throws an exception. So you
either have to use a workaround:
.... counter['B'] += 1
.... except KeyError:
.... counter['B'] = 1

Since this looks ugly somebody invented the setdefault method:
>>> counter['B'] = counter.setdefault('B',0) + 1

And this works with lists/arrays as well. When there's no list yet
setdefault will create an empty list and append the first value.
Otherwise it will just append.

Greetings from Vienna,
mathias
 
T

Tim Chase

In Python, this is the best code I could come up with for
adding a new key, value to a dict

mytable.setdefault( k, [] ).append( v )

Naturally, right after writing my post I found that there is
an easier way:

table[ k ] = v

Just to be clear...these do two VERY different things:
>>> v1=42
>>> table1={}
>>> k='foo'
>>> table1.setdefault(k,[]).append(v1)
>>> table2={}
>>> table2[k]=v1
>>> table1, table2
({'foo': [42]}, {'foo': 42})

Note that the value in the first case is a *list* while the
value in the 2nd case, the value is a scalar. These differ in
the behavior (continuing from above):
>>> v2='Second value'
>>> table1.setdefault(k,[]).append(v2)
>>> table2[k]=v2
>>> table1, table2
({'foo': [42, 'Second value']}, {'foo': 'Second value'})

Note that table1 now has *two* values associated with 'foo',
while table2 only has the most recently assigned value.

Choose according to your use-case. For some of my ETL &
data-processing work, I often want the

mydict.setdefault(key, []).append(value)

version to accrue values associated with a given unique key.

-tkc
 
M

Marc 'BlackJack' Rintsch

However incrementing a non-existing key throws an exception. So you
either have to use a workaround:
... counter['B'] += 1
... except KeyError:
... counter['B'] = 1

Since this looks ugly somebody invented the setdefault method:
counter['B'] = counter.setdefault('B',0) + 1

Nope, for this use case there is the `dict.get()` method:

counter['B'] = counter.get('B', 0) + 1

This assigns only *once* to ``counter['B']`` in every case.

`dict.setdefault()` is for situations where you really want to actually
put the initial value into the dictionary, like with the list example by
the OP.

Ciao,
Marc 'BlackJack' Rintsch
 
B

Bruno Desthuilliers

(e-mail address removed) a écrit :
jdd:

That creates a new dict, to throw it away.

Just to make it clear for the easily confused ones (like me...):
bearophile is talking about the dict passed as an argument to foo.update
- not about the behaviour of dict.update itself (which of course
modifies the dict in place) !-)
 
P

pruebauno

However incrementing a non-existing key throws an exception. So you
either have to use a workaround:
... counter['B'] += 1
... except KeyError:
... counter['B'] = 1
Since this looks ugly somebody invented the setdefault method:
counter['B'] = counter.setdefault('B',0) + 1

Nope, for this use case there is the `dict.get()` method:

counter['B'] = counter.get('B', 0) + 1

This assigns only *once* to ``counter['B']`` in every case.

`dict.setdefault()` is for situations where you really want to actually
put the initial value into the dictionary, like with the list example by
the OP.

Ciao,
Marc 'BlackJack' Rintsch

....and if you are using Python 2.5 or later you can use the
collections module with collections.defaultdict(list) or
collections.defaultdict(int) to do the same thing. I personally find
it easier to read.
 
B

Boris Borcic

Kirk said:
While we're on
the subject, use keyword arguments to dict like:

foo.update(dict(quux='blah', baz='bearophile', jdd='dict'))

was *much* slower, at 11.8s.

Presumably you would save half of that time by writing simply

foo.update(quux='blah', baz='bearophile', jdd='dict')

Cheers, BB
 
P

Pat

paul said:
Pat said:
I know it's not "fair" to compare language features, but it seems to
me (a Python newbie) that appending a new key/value to a dict in
Python is awfully cumbersome.

In Python, this is the best code I could come up with for adding a new
key, value to a dict

mytable.setdefault( k, [] ).append( v )

In Perl, the code looks like this:

$h{ $key } = $value ;
Whats wrong with:

mytable[key] = value

cheers
Paul

mytable[key] = value

is the code that I wound up using. It's obvious now that I know the
answer. Thank you very much to all for your help.

In a earlier question (I can't find the thread in my newsreader), I
asked about an array of dict to dict and someone supplied me with the
answer.

[state]={}
[state][city]={}
['Florida']['Tampa] = 20

It worked out perfectly and the Python code is a billion times easier to
read than the Perl version.
Of all the languages I've learned and used professionally, I'm finding
Python to be one of the best languages to learn and use.

Having Wing IDE Pro has made it a lot easier for me to learn and debug.
It does have a few bugs but they always respond by email within
several hours. Absolutely superb customer support. And no, I'm in no
way affiliated with Wingware except that I'm a satisfied customer.
 
F

Frank Niemeyer

However incrementing a non-existing key throws an exception.

Right. And that's exactly what I would expect, according to the
"principle of least surprise" Python tries to obey. There's simply no
way to increment a non-existent value - not without performing some
obscure implict behind-the-scenes stuff.
So you
either have to use a workaround:
... counter['B'] += 1
... except KeyError:
... counter['B'] = 1

Or you could simply use

if counter.has_key('B'):
counter['B'] += 1
else:
counter['B'] = 1

Regards,
Frank
 
B

bearophileHUGS

Frank Niemeyer:
There's simply no
way to increment a non-existent value - not without performing some
obscure implict behind-the-scenes stuff.

Like importing and using a defaultdict(int).

So you
either have to use a workaround:
 >>> try:
...   counter['B'] += 1
... except KeyError:
...   counter['B'] = 1

Or you could simply use

if counter.has_key('B'):
    counter['B'] += 1
else:
    counter['B'] = 1

Both those are slow. Better to use:

if 'B' in counter:
counter['B'] += 1
else:
counter['B'] = 1

Or with a defaultdict:

counter = defauldict(int)
counter['B'] += 1

Bye,
bearophile
 
M

Marc 'BlackJack' Rintsch

Frank Niemeyer:
There's simply no
way to increment a non-existent value - not without performing some
obscure implict behind-the-scenes stuff.

Like importing and using a defaultdict(int).

So you
either have to use a workaround:
 >>> try:
...   counter['B'] += 1
... except KeyError:
...   counter['B'] = 1

Or you could simply use

if counter.has_key('B'):
    counter['B'] += 1
else:
    counter['B'] = 1

Both those are slow. Better to use:

if 'B' in counter:
counter['B'] += 1
else:
counter['B'] = 1

Or with a defaultdict:

counter = defauldict(int)
counter['B'] += 1

Or:

counter['B'] = counter.get('B', 0) + 1

Ciao,
Marc 'BlackJack' Rintsch
 
B

bearophileHUGS

Marc 'BlackJack' Rintsch:
counter['B'] = counter.get('B', 0) + 1

If you benchmark it, you will find that using the get() method it's
quite slower.

Bye,
bearophile
 
C

chemila66

Frank said:
However incrementing a non-existing key throws an exception.

Right. And that's exactly what I would expect, according to the
"principle of least surprise" Python tries to obey. There's simply no
way to increment a non-existent value - not without performing some
obscure implict behind-the-scenes stuff.
So you
either have to use a workaround:
... counter['B'] += 1
... except KeyError:
... counter['B'] = 1

Or you could simply use

if counter.has_key('B'):
counter['B'] += 1
else:
counter['B'] = 1

Regards,
Frank

or

if 'B' in counter:
counter['B'] += 1
else:
ocunter['B'] = 1
 
B

bearophileHUGS

slais-www:
Slower than
...

Okay, I seen there's a little confusion, I try to say it more clearly.
Generally this is the faster version (faster than the version with
get), especially if you use Psyco:

version 1)
if 'B' in counter:
counter['B'] += 1
else:
counter['B'] = 1

-----------------

Version using a defaultdict:
version 2)
counter['B'] += 1 #

The defaultdict case 2) is intermediate: its relative speed changes on
the basis of the frequency of already present keys. Generally the
difference between 1) and 2) isn't much, so I usually use a
defaultdict 2), that has a nicer syntax, even if it can sometimes be a
little slower than 1).

Versions with has_key or try-except or get aren't fast.

Bye,
bearophile
 
Joined
Nov 17, 2010
Messages
1
Reaction score
0
How do you create a subdictionary? Do you really have to iterate like this?
dict = {}
dict['state'] = {}
dict['state']['city'] = {}
dict['state']['city'] = 'London'

Doing directly:
dict['state']['city'] = 'London'
throws an error: NameError: name 'dict' is not defined

I'm using this from PHP. Or is there another way for associative subarrays in python?
 

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

Forum statistics

Threads
473,769
Messages
2,569,578
Members
45,052
Latest member
LucyCarper

Latest Threads

Top