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

Is there a short-circuiting dictionary "get" method?

P: n/a
In this snippet:

d = {'x': 1}
value = d.get('x', bigscaryfunction())

the bigscaryfunction is always called, even though 'x' is a valid key.
Is there a "short-circuit" version of get that doesn't evaluate the
second argument if the first is a valid key? For now I'll code around
it, but this behavior surprised me a bit...

Dave
Jul 18 '05 #1
Share this Question
Share on Google+
16 Replies


P: n/a
Dave Opstad wrote:
In this snippet:

d = {'x': 1}
value = d.get('x', bigscaryfunction())

the bigscaryfunction is always called, even though 'x' is a valid key.
Is there a "short-circuit" version of get that doesn't evaluate the
second argument if the first is a valid key? For now I'll code around
it, but this behavior surprised me a bit...


try:
value = d['x']
except KeyError:
value = bigscaryfunction()

get() is just a method, and arguments to methods are always
evaluated before being passed to the method, so the short
answer is "no, there is no 'version' of get() that will do
what you want".

-Peter
Jul 18 '05 #2

P: n/a
Dave,

On Wed, 09 Mar 2005 09:45:41 -0800, Dave Opstad <op****@batnet.com> wrote:
In this snippet:

d = {'x': 1}
value = d.get('x', bigscaryfunction())

the bigscaryfunction is always called, even though 'x' is a valid key.
Is there a "short-circuit" version of get that doesn't evaluate the
second argument if the first is a valid key? For now I'll code around
it, but this behavior surprised me a bit...


There is no short-circuit function like you're asking for, because
it's impossible in python. To pass an argument to the 'get' function,
python evaluates the bigscaryfunction before calling 'get'.

(I believe this means that python doesn't have "lazy evaluation", but
the language lawyers may shoot me down on that. Wikipedia seems to say
that it means python doesn't have "delayed evaluation").

Here are two ways to do what you want:

if 'x' in d: value = d['x']
else: value = bigscaryfunction()

or:

def sget(dict, key, func, *args):
if key in dict: return key
else: return func(*args)

sget(d, 'x', bigscaryfunction)

Both methods are untested, but should work with minor modifications.

Peace
Bill Mill
bill.mill at gmail.com
Jul 18 '05 #3

P: n/a
Maybe this can help:

value = d.get('x', lambda: bigscaryfunction())

Bearophile

Jul 18 '05 #4

P: n/a
Le Wed, 09 Mar 2005 09:45:41 -0800, Dave Opstad a écrit :
In this snippet:

d = {'x': 1}
value = d.get('x', bigscaryfunction())

the bigscaryfunction is always called, even though 'x' is a valid key.
Is there a "short-circuit" version of get that doesn't evaluate the
second argument if the first is a valid key? For now I'll code around
it, but this behavior surprised me a bit... def scary():
print "scary called"
return 22

d = dict(x=1)
d.get('x', lambda *a : scary())
# print 1
d.get('z', (lambda *a : scary())())
scary called
22

First (wrong) version :
d.get('z', lambda *a : scary())
<function <lambda> at 0x40598e9c>
Dave

Jul 18 '05 #5

P: n/a
On 9 Mar 2005 10:05:21 -0800, be************@lycos.com
<be************@lycos.com> wrote:
Maybe this can help:

value = d.get('x', lambda: bigscaryfunction())

def test(): print 'gbye' .... d = {}
z = d.get('x', lambda: test())
z <function <lambda> at 0x008D6870>

So this seems to be merely an obfuscation of:
z = d.get('x', test)
z

<function test at 0x008D66B0>

I just wanted to ask, am I missing something?

Peace
Bill Mill
bill.mill at gmail.com
Jul 18 '05 #6

P: n/a
On 09 Mar 2005 18:13:01 GMT, F. Petitjean <li***********@news.proxad.net> wrote:
Le Wed, 09 Mar 2005 09:45:41 -0800, Dave Opstad a écrit :
In this snippet:

d = {'x': 1}
value = d.get('x', bigscaryfunction())

the bigscaryfunction is always called, even though 'x' is a valid key.
Is there a "short-circuit" version of get that doesn't evaluate the
second argument if the first is a valid key? For now I'll code around
it, but this behavior surprised me a bit...

def scary():
print "scary called"
return 22

d = dict(x=1)
d.get('x', lambda *a : scary())

# print 1
d.get('z', (lambda *a : scary())())
scary called
22


but:
d.get('x', (lambda *a: test())())

test called
1

So how is this different than d.get('x', test()) ?

Peace
Bill Mill
bill.mill at gmail.com
Jul 18 '05 #7

P: n/a
F. Petitjean wrote:
Le Wed, 09 Mar 2005 09:45:41 -0800, Dave Opstad a écrit :
Is there a "short-circuit" version of get that doesn't evaluate the
second argument if the first is a valid key? For now I'll code around
it, but this behavior surprised me a bit...


def scary():
print "scary called"
return 22

d = dict(x=1)
d.get('x', lambda *a : scary())
# print 1
d.get('z', (lambda *a : scary())())
scary called
22


So you have to change the code at the point of call depending on whether the requested value is in
the dict? ;)

If you can get this to work I'm sure we can find other applications for such 'smart code' :-)

Kent
Jul 18 '05 #8

P: n/a
Dave Opstad wrote:
In this snippet:

d = {'x': 1}
value = d.get('x', bigscaryfunction())

the bigscaryfunction is always called, even though 'x' is a valid key.
Is there a "short-circuit" version of get that doesn't evaluate the
second argument if the first is a valid key? For now I'll code around
it, but this behavior surprised me a bit...


Well, if the dict only contains ints, here is a dirty hack (but don't
use it instead of the try/except approach):

class Littletinyproxy:
def __int__(self):
return bigscaryfunction()

d = dict(x=1)
value = int(d.get('x', Littletinyproxy()))
Reinhold
Jul 18 '05 #9

P: n/a
Bill Mill wrote:
On 9 Mar 2005 10:05:21 -0800, be************@lycos.com
<be************@lycos.com> wrote:
Maybe this can help:

value = d.get('x', lambda: bigscaryfunction())


def test(): print 'gbye' ...d = {}
z = d.get('x', lambda: test())
z <function <lambda> at 0x008D6870>

So this seems to be merely an obfuscation of:
z = d.get('x', test)
z

<function test at 0x008D66B0>

I just wanted to ask, am I missing something?


Nope that looks right. See "Overuse of lambda" in
http://www.python.org/moin/DubiousPython for discussion of exactly this
mistake.

STeVe
Jul 18 '05 #10

P: n/a
Dave Opstad wrote:
In this snippet:

d = {'x': 1}
value = d.get('x', bigscaryfunction())

the bigscaryfunction is always called, even though 'x' is a valid key.
Is there a "short-circuit" version of get that doesn't evaluate the
second argument if the first is a valid key? For now I'll code around
it, but this behavior surprised me a bit...

Dave

If (and this is a big if) you know that the dictionary contains no values that
evaluate to boolean false, then you can use the short-circuiting 'or' operator:
def bigscaryfunction(): ... print "scary"
... d= globals()
d.get("key") or bigscaryfunction() scary d.get("__name__") or bigscaryfunction() 'LazyDictget'
Alternatively, you can just write your own getter function: def lazyget(dict_, key, default): ... if key in dict_:
... return dict_[key]
... else:
... return default()
... lazyget(d,"key",bigscaryfunction) scary lazyget(d,"__name__",bigscaryfunction) 'LazyDictget'


The optimal choice of whether to "look before you leap" i.e., "if key in dict_"
or simply catch KeyError, depends on the ratio of hits to misses. Google will
turn up some experimental data on this, but, I seem to recall that if more than
10% attempts are misses, then LBYL is faster, because raising the exception is slow
Michael
Jul 18 '05 #11

P: n/a
untested

def my_getter(m, i, f):
try:
return m[i]
except (KeyError, IndexError):
return f()

my_getter(d, 'x', bigscaryfunction)
my_getter(d, 'y', lambda: scaryinlineexpresion)

-----BEGIN PGP SIGNATURE-----
Version: GnuPG v1.2.6 (GNU/Linux)

iD8DBQFCL4SfJd01MZaTXX0RAhrjAJ9hYiHBfdCdVOI2fR41A/zuLlSaZACfeLAs
Uhjj/Aiqa80532KKeD0dPYI=
=YVEi
-----END PGP SIGNATURE-----

Jul 18 '05 #12

P: n/a

Dave> In this snippet:
Dave> d = {'x': 1}
Dave> value = d.get('x', bigscaryfunction())

Dave> the bigscaryfunction is always called, even though 'x' is a valid
Dave> key.

I sometimes use

value = d.get('x') or bsf()

Of course, this bsf() will get called if d['x'] evaluates to false, not just
None, so it won't work in all situations. It may help often enough to be
useful though.

Skip
Jul 18 '05 #13

P: n/a

"Skip Montanaro" <sk**@pobox.com> wrote in message
news:16***********************@montanaro.dyndns.or g...
value = d.get('x') or bsf()

Of course, this bsf() will get called if d['x'] evaluates to false, not
just
None,


value = (d.get('x') is not None) or bsf() #??

tjr

Jul 18 '05 #14

P: n/a
Terry Reedy wrote:
"Skip Montanaro" <sk**@pobox.com> wrote in message
news:16***********************@montanaro.dyndns.or g...
value = d.get('x') or bsf()

Of course, this bsf() will get called if d['x'] evaluates to false, not
just
None,

value = (d.get('x') is not None) or bsf() #??

Unfortunately this will set value to True for all non-None values of
d['x']. Suppose d['x'] == 3:
3 is not None True


regards
Steve

Jul 18 '05 #15

P: n/a
On Fri, 11 Mar 2005 04:12:19 -0500, Steve Holden <st***@holdenweb.com> wrote:
Terry Reedy wrote:
"Skip Montanaro" <sk**@pobox.com> wrote in message
news:16***********************@montanaro.dyndns.or g...
value = d.get('x') or bsf()

Of course, this bsf() will get called if d['x'] evaluates to false, not
just
None,

value = (d.get('x') is not None) or bsf() #??

Unfortunately this will set value to True for all non-None values of
d['x']. Suppose d['x'] == 3:
>>> 3 is not NoneTrue >>>

maybe (untested)
value = ('x' in d and [d['x']] or [bsf()])[0]

then there's always

if 'x' in d: value = d['x']
else: value = bsf()

or

try: value = d['x']
except KeyError: value = bsf()
Regards,
Bengt Richter
Jul 18 '05 #16

P: n/a
Bengt Richter wrote:
then there's always

if 'x' in d: value = d['x']
else: value = bsf()

or

try: value = d['x']
except KeyError: value = bsf()


Its worth remembering that the first of those two suggestions is also
faster than using get, so you aren't losing on speed if you write the code
out in full: choose whichever seems clearest and uses the least contorted
code.

(The second is the fastest of all if the value is found, but a lot slower
if the exception gets thrown.)

C:\Python24\Lib>timeit.py -s "d={}" -s "for k in range(1000): d['x%s'%k]=k"
"value = d.get('x45', 'notfound')"
1000000 loops, best of 3: 0.427 usec per loop

C:\Python24\Lib>timeit.py -s "d={}" -s "for k in range(1000): d['x%s'%k]=k"
"value = d.get('z45', 'notfound')"
1000000 loops, best of 3: 0.389 usec per loop

C:\Python24\Lib>timeit.py -s "d={}" -s "for k in range(1000): d['x%s'%k]=k"
"if 'x45' in d: value=d['x45']" "else: value='notfound'"
1000000 loops, best of 3: 0.259 usec per loop

C:\Python24\Lib>timeit.py -s "d={}" -s "for k in range(1000): d['x%s'%k]=k"
"if 'z45' in d: value=d['z45']" "else: value='notfound'"
1000000 loops, best of 3: 0.131 usec per loop

C:\Python24\Lib>timeit.py -s "d={}" -s "for k in range(1000): d['x%s'%k]=k"
"try: value=d['x45']" "except: value='notfound'"
1000000 loops, best of 3: 0.158 usec per loop

C:\Python24\Lib>timeit.py -s "d={}" -s "for k in range(1000): d['x%s'%k]=k"
"try: value=d['z45']" "except: value='notfound'"
100000 loops, best of 3: 2.71 usec per loop
Jul 18 '05 #17

This discussion thread is closed

Replies have been disabled for this discussion.