By using this site, you agree to our updated Privacy Policy and our Terms of Use. Manage your Cookies Settings.
429,570 Members | 999 Online
Bytes IT Community
+ Ask a Question
Need help? Post your question and get tips & solutions from a community of 429,570 IT Pros & Developers. It's quick & easy.

Append a new value to dict

P: n/a
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?
Oct 13 '08 #1
Share this Question
Share on Google+
20 Replies


P: n/a
Pat
Pat wrote:
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.
Oct 13 '08 #2

P: n/a
jdd
On Oct 13, 7:21*am, Pat <P...@junk.comwrote:
Is there a better/easier way to code this in Python than the
obtuse/arcane setdefault code?
foo = {'bar': 'baz'}
foo.update({'quux': 'blah'})
Oct 13 '08 #3

P: n/a
Pat schrieb:
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

Oct 13 '08 #4

P: n/a
Pat wrote:
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.
>>counter['B'] += 1
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:
>>try:
.... 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
Oct 13 '08 #5

P: n/a
>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


Oct 13 '08 #6

P: n/a
jdd:
foo = {'bar': 'baz'}
foo.update({'quux': 'blah'})
That creates a new dict, to throw it away. Don't do that. Use the
standard and more readable syntax:
foo = {...}
foo['quux'] = 'blah'
Bye,
bearophile
Oct 13 '08 #7

P: n/a
On Mon, 13 Oct 2008 14:10:43 +0200, Mathias Frey wrote:
However incrementing a non-existing key throws an exception. So you
either have to use a workaround:
>>try:
... 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
Oct 13 '08 #8

P: n/a
be************@lycos.com a crit :
jdd:
>foo = {'bar': 'baz'}
foo.update({'quux': 'blah'})

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) !-)

Oct 13 '08 #9

P: n/a
At 2008-10-13T13:14:15Z, be************@lycos.com writes:
jdd:
>foo = {'bar': 'baz'}
foo.update({'quux': 'blah'})

That creates a new dict, to throw it away. Don't do that.
I use that if I'm changing many values at once, eg:

foo.update({
'quux': 'blah',
'baz' : 'bearophile',
'jdd' : 'dict',
})

instead of:

foo['quux'] = 'blah'
foo['baz'] = 'bearophile'
foo['jdd'] = 'dict'

because it seems to more clearly indicate what I'm doing, and has fewer
opportunities for types. Still, there is a performance penalty. Running
"my way" 10,000,000 times took 8.7s, and "your way" only took 4.7s. If
you're doing this in an inner loop, that may be significant. 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.
--
Kirk Strauser
The Day Companies
Oct 13 '08 #10

P: n/a
On Oct 13, 9:41 am, Marc 'BlackJack' Rintsch <bj_...@gmx.netwrote:
On Mon, 13 Oct 2008 14:10:43 +0200, Mathias Frey wrote:
However incrementing a non-existing key throws an exception. So you
either have to use a workaround:
>>try:
... 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.
Oct 13 '08 #11

P: n/a
Kirk Strauser wrote:
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

Oct 16 '08 #12

P: n/a
Pat
paul wrote:
Pat schrieb:
>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.
Oct 16 '08 #13

P: n/a
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:
>>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

Regards,
Frank
Oct 22 '08 #14

P: n/a
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
Oct 22 '08 #15

P: n/a
On Wed, 22 Oct 2008 10:12:56 -0700, bearophileHUGS wrote:
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
Oct 22 '08 #16

P: n/a
be************@lycos.com schrieb:
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).
There's nothing implicit in explicitly defining some default behaviour.

Frank

Oct 23 '08 #17

P: n/a
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
Oct 23 '08 #18

P: n/a

Frank Niemeyer wrote:
>
>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:
> >>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

Regards,
Frank
--
http://mail.python.org/mailman/listinfo/python-list

or

if 'B' in counter:
counter['B'] += 1
else:
ocunter['B'] = 1
--
View this message in context: http://www.nabble.com/Append-a-new-v...p20127415.html
Sent from the Python - python-list mailing list archive at Nabble.com.

Oct 23 '08 #19

P: n/a
be************@lycos.com wrote:
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.
Slower than

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

It is not slower than defaultdict which I have compared to
>counter['B'] = counter.get('B', 0) + 1
on a file with 125,000 additions default dict was significantly slower
(only ~40seconds v ~30 secs for get) and used twice the memory.

--
djc
Oct 23 '08 #20

P: n/a
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
Oct 23 '08 #21

This discussion thread is closed

Replies have been disabled for this discussion.