Setdefault bypasses __setitem__

Collapse
This topic is closed.
X
X
 
  • Time
  • Show
Clear All
new posts
  • Ron Garret

    #1

    Setdefault bypasses __setitem__

    Is this a bug or a feature?

    class mydict(dict):
    def __setitem__(sel f, key, val):
    print 'foo'
    dict.__setitem_ _(self, key, val)
    [color=blue][color=green][color=darkred]
    >>> d=mydict()
    >>> d[1]=2[/color][/color][/color]
    foo[color=blue][color=green][color=darkred]
    >>> d.setdefault(2, 3)[/color][/color][/color]
    3

    rg
  • Diez B. Roggisch

    #2
    Re: Setdefault bypasses __setitem__

    Ron Garret wrote:[color=blue]
    > Is this a bug or a feature?
    >
    > class mydict(dict):
    > def __setitem__(sel f, key, val):
    > print 'foo'
    > dict.__setitem_ _(self, key, val)
    >
    >[color=green][color=darkred]
    >>>>d=mydict( )
    >>>>d[1]=2[/color][/color]
    >
    > foo
    >[color=green][color=darkred]
    >>>>d.setdefaul t(2,3)[/color][/color][/color]


    Feature. If it wouldn't bypass __setitem__, how exactly would you make a
    default-item? Using __setitem__ implies a key. So if setdefault
    was implemented as

    def setdefault(self , v):
    self["SOME_DEFAULT_K EY_NAME"] = v

    and later on one writes e.g. a HTML-page with a form input field named
    "SOME_DEFAULT_K EY_NAME" that gets stored in a dict - it would overwrite
    the default value.

    So it has to bypass __setitem__, as otherwise it can't distinguish
    between "real" and the default value - the latter one is not allowed to
    have a key that is in any imaginable way used by the user.

    Diez

    Comment

    • Peter Otten

      #3
      Re: Setdefault bypasses __setitem__

      Diez B. Roggisch wrote:
      [color=blue]
      > Ron Garret wrote:[color=green]
      >> Is this a bug or a feature?
      >>
      >> class mydict(dict):
      >> def __setitem__(sel f, key, val):
      >> print 'foo'
      >> dict.__setitem_ _(self, key, val)
      >>
      >>[color=darkred]
      >>>>>d=mydict ()
      >>>>>d[1]=2[/color]
      >>
      >> foo
      >>[color=darkred]
      >>>>>d.setdefau lt(2,3)[/color][/color]
      >
      >
      > Feature. If it wouldn't bypass __setitem__, how exactly would you make a
      > default-item? Using __setitem__ implies a key. So if setdefault
      > was implemented as
      >
      > def setdefault(self , v):
      > self["SOME_DEFAULT_K EY_NAME"] = v
      >
      > and later on one writes e.g. a HTML-page with a form input field named
      > "SOME_DEFAULT_K EY_NAME" that gets stored in a dict - it would overwrite
      > the default value.
      >
      > So it has to bypass __setitem__, as otherwise it can't distinguish
      > between "real" and the default value - the latter one is not allowed to
      > have a key that is in any imaginable way used by the user.[/color]

      The implementation is certainly a design decision. setdefault() could be
      implemented in terms of __set/getitem__() as

      def setdefault(self , key, value=None):
      try:
      return self[key]
      except KeyError:
      self[key] = value
      return self[key]

      I guess it's not done for performance reasons.

      Peter

      Comment

      • Diez B. Roggisch

        #4
        Re: Setdefault bypasses __setitem__

        > The implementation is certainly a design decision. setdefault() could be[color=blue]
        > implemented in terms of __set/getitem__() as
        >
        > def setdefault(self , key, value=None):
        > try:
        > return self[key]
        > except KeyError:
        > self[key] = value
        > return self[key]
        >
        > I guess it's not done for performance reasons.[/color]

        Nope. What if you changed your default value? Then you'd have to update
        the whole dictionary - but without keeping track of the keys you placed
        the default value under that isn't possible. Which strikes me as
        more-than-marginal overhead - without any advantage (as using
        __setitem__ for the default value isn't something I consider being a
        missing feature...)

        Diez

        Comment

        • Peter Otten

          #5
          Re: Setdefault bypasses __setitem__

          Diez B. Roggisch wrote:
          [color=blue][color=green]
          >> The implementation is certainly a design decision. setdefault() could be
          >> implemented in terms of __set/getitem__() as
          >>
          >> def setdefault(self , key, value=None):
          >> try:
          >> return self[key]
          >> except KeyError:
          >> self[key] = value
          >> return self[key]
          >>
          >> I guess it's not done for performance reasons.[/color]
          >
          > Nope. What if you changed your default value? Then you'd have to update
          > the whole dictionary - but without keeping track of the keys you placed
          > the default value under that isn't possible. Which strikes me as
          > more-than-marginal overhead - without any advantage (as using
          > __setitem__ for the default value isn't something I consider being a
          > missing feature...)[/color]

          Are we talking about the same setdefault()?

          setdefault(...)
          D.setdefault(k[,d]) -> D.get(k,d), also set D[k]=d if k not in D

          There is no per-instance default value just on per call:
          [color=blue][color=green][color=darkred]
          >>> d = {}
          >>> d.setdefault("a ", 1)[/color][/color][/color]
          1[color=blue][color=green][color=darkred]
          >>> d.setdefault("a ", 42)[/color][/color][/color]
          1

          I'm sure there is a misunderstandin g in our conversation, I'm just not able
          to nail it...

          Peter




          Comment

          • Fredrik Lundh

            #6
            Re: Setdefault bypasses __setitem__

            Peter Otten wrote:
            [color=blue]
            > Are we talking about the same setdefault()?
            >
            > setdefault(...)
            > D.setdefault(k[,d]) -> D.get(k,d), also set D[k]=d if k not in D[/color]

            note that it might be spelled "setdefault ", but it should be pronounced
            "get or set".

            </F>



            Comment

            • Duncan Booth

              #7
              Re: Setdefault bypasses __setitem__

              Diez B. Roggisch wrote:
              [color=blue]
              > So if setdefault
              > was implemented as
              >
              > def setdefault(self , v):
              > self["SOME_DEFAULT_K EY_NAME"] = v[/color]

              if setdefault was implemented that way then all current uses of setdefault
              would throw an exception.

              setdefault takes *three* parameters: self, key, value. Once you include the
              key parameter your entire argument implodes.

              Comment

              • Diez B. Roggisch

                #8
                Re: Setdefault bypasses __setitem__

                [color=blue]
                > Are we talking about the same setdefault()?
                >
                >
                > D.setdefault(k[,d]) -> D.get(k,d), also set D[k]=d if k not in D
                >
                > There is no per-instance default value just on per call:[/color]

                Oh. You're right. I was somehow under the impression that setdefault is
                per-instance, so that I can avoid

                d.get(key, default)

                and write

                d[key]

                instead, for all keys, and get no more KeyErrors. But then you are
                right of course.

                Regards,

                Diez

                Comment

                • Diez B. Roggisch

                  #9
                  Re: Setdefault bypasses __setitem__

                  Duncan Booth wrote:[color=blue]
                  > Diez B. Roggisch wrote:
                  >
                  >[color=green]
                  >>So if setdefault
                  >>was implemented as
                  >>
                  >>def setdefault(self , v):
                  >> self["SOME_DEFAULT_K EY_NAME"] = v[/color]
                  >
                  >
                  > if setdefault was implemented that way then all current uses of setdefault
                  > would throw an exception.
                  >
                  > setdefault takes *three* parameters: self, key, value. Once you include the
                  > key parameter your entire argument implodes.[/color]

                  Yup. It does implode, leaving me thunderstruck because of my dumbness.

                  I rarely find things in python strange or named incorrectly, but this is
                  IMHO such a case - setdefault led me to think that using it would set a
                  default value to return for _future_ lookups of non-existant keys. That
                  semantics is known in e.g. ruby or java.

                  I think a better name would be getdefault, or even get_setdefault - in
                  oppposition to the get(key, d) form.

                  But now that this became clear to me... I guess I can live with the name :)

                  Diez

                  Comment

                  • Fredrik Lundh

                    #10
                    Re: Setdefault bypasses __setitem__

                    Diez B. Roggisch wrote:
                    [color=blue]
                    > I rarely find things in python strange or named incorrectly, but this is
                    > IMHO such a case - setdefault led me to think that using it would set a
                    > default value to return for _future_ lookups of non-existant keys. That
                    > semantics is known in e.g. ruby or java.
                    >
                    > I think a better name would be getdefault, or even get_setdefault - in
                    > oppposition to the get(key, d) form.
                    >
                    > But now that this became clear to me... I guess I can live with the name :)[/color]

                    as long as you pronounce it correctly (see my earlier post).

                    </F>



                    Comment

                    Working...