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

Collapse
This topic is closed.
X
X
 
  • Time
  • Show
Clear All
new posts
  • Dave Opstad

    #1

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

    In this snippet:

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

    the bigscaryfunctio n 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
  • Peter Hansen

    #2
    Re: Is there a short-circuiting dictionary "get&qu ot; method?

    Dave Opstad wrote:[color=blue]
    > In this snippet:
    >
    > d = {'x': 1}
    > value = d.get('x', bigscaryfunctio n())
    >
    > the bigscaryfunctio n 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...[/color]

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

    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

    Comment

    • Bill Mill

      #3
      Re: Is there a short-circuiting dictionary "get&qu ot; method?

      Dave,

      On Wed, 09 Mar 2005 09:45:41 -0800, Dave Opstad <opstad@batnet. com> wrote:[color=blue]
      > In this snippet:
      >
      > d = {'x': 1}
      > value = d.get('x', bigscaryfunctio n())
      >
      > the bigscaryfunctio n 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...[/color]

      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 bigscaryfunctio n 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 = bigscaryfunctio n()

      or:

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

      sget(d, 'x', bigscaryfunctio n)

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

      Peace
      Bill Mill
      bill.mill at gmail.com

      Comment

      • bearophileHUGS@lycos.com

        #4
        Re: Is there a short-circuiting dictionary &quot;get&qu ot; method?

        Maybe this can help:

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

        Bearophile

        Comment

        • F. Petitjean

          #5
          Re: Is there a short-circuiting dictionary &quot;get&qu ot; method?

          Le Wed, 09 Mar 2005 09:45:41 -0800, Dave Opstad a écrit :[color=blue]
          > In this snippet:
          >
          > d = {'x': 1}
          > value = d.get('x', bigscaryfunctio n())
          >
          > the bigscaryfunctio n 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...[/color]
          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>[color=blue]
          >
          > Dave[/color]

          Comment

          • Bill Mill

            #6
            Re: Is there a short-circuiting dictionary &quot;get&qu ot; method?

            On 9 Mar 2005 10:05:21 -0800, bearophileHUGS@ lycos.com
            <bearophileHUGS @lycos.com> wrote:[color=blue]
            > Maybe this can help:
            >
            > value = d.get('x', lambda: bigscaryfunctio n())[/color]
            [color=blue][color=green][color=darkred]
            >>> def test(): print 'gbye'[/color][/color][/color]
            ....[color=blue][color=green][color=darkred]
            >>> d = {}
            >>> z = d.get('x', lambda: test())
            >>> z[/color][/color][/color]
            <function <lambda> at 0x008D6870>

            So this seems to be merely an obfuscation of:
            [color=blue][color=green][color=darkred]
            >>> z = d.get('x', test)
            >>> z[/color][/color][/color]
            <function test at 0x008D66B0>

            I just wanted to ask, am I missing something?

            Peace
            Bill Mill
            bill.mill at gmail.com

            Comment

            • Bill Mill

              #7
              Re: Is there a short-circuiting dictionary &quot;get&qu ot; method?

              On 09 Mar 2005 18:13:01 GMT, F. Petitjean <littlejohn.75@ news.proxad.net > wrote:[color=blue]
              > Le Wed, 09 Mar 2005 09:45:41 -0800, Dave Opstad a écrit :[color=green]
              > > In this snippet:
              > >
              > > d = {'x': 1}
              > > value = d.get('x', bigscaryfunctio n())
              > >
              > > the bigscaryfunctio n 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...[/color]
              > 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[/color]

              but:
              [color=blue][color=green][color=darkred]
              >>> d.get('x', (lambda *a: test())())[/color][/color][/color]
              test called
              1

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

              Peace
              Bill Mill
              bill.mill at gmail.com

              Comment

              • Kent Johnson

                #8
                Re: Is there a short-circuiting dictionary &quot;get&qu ot; method?

                F. Petitjean wrote:[color=blue]
                > Le Wed, 09 Mar 2005 09:45:41 -0800, Dave Opstad a écrit :[color=green]
                >>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...[/color]
                >
                > 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[/color]

                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

                Comment

                • Reinhold Birkenfeld

                  #9
                  Re: Is there a short-circuiting dictionary &quot;get&qu ot; method?

                  Dave Opstad wrote:[color=blue]
                  > In this snippet:
                  >
                  > d = {'x': 1}
                  > value = d.get('x', bigscaryfunctio n())
                  >
                  > the bigscaryfunctio n 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...[/color]

                  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 bigscaryfunctio n()

                  d = dict(x=1)
                  value = int(d.get('x', Littletinyproxy ()))


                  Reinhold

                  Comment

                  • Steven Bethard

                    #10
                    Re: Is there a short-circuiting dictionary &quot;get&qu ot; method?

                    Bill Mill wrote:[color=blue]
                    > On 9 Mar 2005 10:05:21 -0800, bearophileHUGS@ lycos.com
                    > <bearophileHUGS @lycos.com> wrote:
                    >[color=green]
                    >>Maybe this can help:
                    >>
                    >>value = d.get('x', lambda: bigscaryfunctio n())[/color]
                    >
                    >[color=green][color=darkred]
                    >>>>def test(): print 'gbye'[/color][/color]
                    > ...[color=green][color=darkred]
                    >>>>d = {}
                    >>>>z = d.get('x', lambda: test())
                    >>>>z[/color][/color]
                    > <function <lambda> at 0x008D6870>
                    >
                    > So this seems to be merely an obfuscation of:
                    >[color=green][color=darkred]
                    >>>>z = d.get('x', test)
                    >>>>z[/color][/color]
                    > <function test at 0x008D66B0>
                    >
                    > I just wanted to ask, am I missing something?[/color]

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

                    STeVe

                    Comment

                    • Michael Spencer

                      #11
                      Re: Is there a short-circuiting dictionary &quot;get&qu ot; method?

                      Dave Opstad wrote:[color=blue]
                      > In this snippet:
                      >
                      > d = {'x': 1}
                      > value = d.get('x', bigscaryfunctio n())
                      >
                      > the bigscaryfunctio n 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[/color]
                      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:
                      [color=blue][color=green][color=darkred]
                      >>> def bigscaryfunctio n():[/color][/color][/color]
                      ... print "scary"
                      ...[color=blue][color=green][color=darkred]
                      >>> d= globals()
                      >>> d.get("key") or bigscaryfunctio n()[/color][/color][/color]
                      scary[color=blue][color=green][color=darkred]
                      >>> d.get("__name__ ") or bigscaryfunctio n()[/color][/color][/color]
                      'LazyDictget'[color=blue][color=green][color=darkred]
                      >>>[/color][/color][/color]

                      Alternatively, you can just write your own getter function:[color=blue][color=green][color=darkred]
                      >>> def lazyget(dict_, key, default):[/color][/color][/color]
                      ... if key in dict_:
                      ... return dict_[key]
                      ... else:
                      ... return default()
                      ...[color=blue][color=green][color=darkred]
                      >>> lazyget(d,"key" ,bigscaryfuncti on)[/color][/color][/color]
                      scary[color=blue][color=green][color=darkred]
                      >>> lazyget(d,"__na me__",bigscaryf unction)[/color][/color][/color]
                      'LazyDictget'[color=blue][color=green][color=darkred]
                      >>>[/color][/color][/color]

                      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


                      Comment

                      • Jeff Epler

                        #12
                        Re: Is there a short-circuiting dictionary &quot;get&qu ot; method?

                        untested

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

                        my_getter(d, 'x', bigscaryfunctio n)
                        my_getter(d, 'y', lambda: scaryinlineexpr esion)

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

                        iD8DBQFCL4SfJd0 1MZaTXX0RAhrjAJ 9hYiHBfdCdVOI2f R41A/zuLlSaZACfeLAs
                        Uhjj/Aiqa80532KKeD0d PYI=
                        =YVEi
                        -----END PGP SIGNATURE-----

                        Comment

                        • Skip Montanaro

                          #13
                          Re: Is there a short-circuiting dictionary &quot;get&qu ot; method?


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

                          Dave> the bigscaryfunctio n 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

                          Comment

                          • Terry Reedy

                            #14
                            Re: Is there a short-circuiting dictionary &quot;get&qu ot; method?


                            "Skip Montanaro" <skip@pobox.com > wrote in message
                            news:16943.1847 5.568383.983546 @montanaro.dynd ns.org...[color=blue]
                            > value = d.get('x') or bsf()
                            >
                            > Of course, this bsf() will get called if d['x'] evaluates to false, not
                            > just
                            > None,[/color]

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

                            tjr



                            Comment

                            • Steve Holden

                              #15
                              Re: Is there a short-circuiting dictionary &quot;get&qu ot; method?

                              Terry Reedy wrote:[color=blue]
                              > "Skip Montanaro" <skip@pobox.com > wrote in message
                              > news:16943.1847 5.568383.983546 @montanaro.dynd ns.org...
                              >[color=green]
                              >> value = d.get('x') or bsf()
                              >>
                              >>Of course, this bsf() will get called if d['x'] evaluates to false, not
                              >>just
                              >>None,[/color]
                              >
                              >
                              > value = (d.get('x') is not None) or bsf() #??
                              >[/color]
                              Unfortunately this will set value to True for all non-None values of
                              d['x']. Suppose d['x'] == 3:
                              [color=blue][color=green][color=darkred]
                              >>> 3 is not None[/color][/color][/color]
                              True[color=blue][color=green][color=darkred]
                              >>>[/color][/color][/color]

                              regards
                              Steve

                              Comment

                              Working...