class attrdict

Collapse
This topic is closed.
X
X
 
  • Time
  • Show
Clear All
new posts
  • Hallvard B Furuseth

    #1

    class attrdict

    Does this class need anything more?
    Is there any risk of a lookup loop?
    Seems to work...

    class attrdict(dict):
    """Dict where d['foo'] also can be accessed as d.foo"""
    def __init__(self, *args, **kwargs):
    self.__dict__ = self
    dict.__init__(s elf, *args, **kwargs)
    def __repr__(self):
    return dict.__repr__(s elf).join(("att rdict(", ")"))
    >>a = attrdict([(1,2)], a=3, b=4)
    >>a
    attrdict({'a': 3, 1: 2, 'b': 4})
    >>a = attrdict([(1,2)], b=3, c=4)
    >>a
    attrdict({1: 2, 'c': 4, 'b': 3})
    >>a.b
    3
    >>a.d = 5
    >>a['d']
    5
    >>a.e
    Traceback (most recent call last):
    File "<stdin>", line 1, in ?
    AttributeError: 'attrdict' object has no attribute 'e'
    >>a.__getattr __ = 'xyzzy'
    >>a.__getattrib ute__ = 'xyzzy'
    >>a.__setattr __ = 'xyzzy'
    >>a.__delattr __ = 'xyzzy'
    >>a.c
    4
    >>a[1]
    2
    >>del a.c
    >>>
    --
    Hallvard
  • James Stroud

    #2
    Re: class attrdict

    Hallvard B Furuseth wrote:
    Does this class need anything more?
    Is there any risk of a lookup loop?
    Seems to work...
    >
    class attrdict(dict):
    """Dict where d['foo'] also can be accessed as d.foo"""
    def __init__(self, *args, **kwargs):
    self.__dict__ = self
    dict.__init__(s elf, *args, **kwargs)
    def __repr__(self):
    return dict.__repr__(s elf).join(("att rdict(", ")"))
    >
    Strangely enough, this seems okay since an instance of a dict subclass
    object has an empty __dict__ attribute anyway and so you won't be
    unwittingly destroying some behavior.

    James

    Comment

    • Alex Martelli

      #3
      Re: class attrdict

      Hallvard B Furuseth <h.b.furuseth@u sit.uio.nowrote :
      Does this class need anything more?
      Is there any risk of a lookup loop?
      Seems to work...
      >
      class attrdict(dict):
      """Dict where d['foo'] also can be accessed as d.foo"""
      def __init__(self, *args, **kwargs):
      self.__dict__ = self
      dict.__init__(s elf, *args, **kwargs)
      def __repr__(self):
      return dict.__repr__(s elf).join(("att rdict(", ")"))
      The problem is mostly that, given an instance a of attrdict, whether you
      can call (e.g.) a.update(foo) depends on whether you ever set
      a['update'], making the whole program extremely fragile -- a very high
      price to pay for some modest amount of syntax sugar.


      Alex

      Comment

      • MonkeeSage

        #4
        Re: class attrdict

        On Mar 2, 9:25 pm, a...@mac.com (Alex Martelli) wrote:
        The problem is mostly that, given an instance a of attrdict, whether you
        can call (e.g.) a.update(foo) depends on whether you ever set
        a['update'], making the whole program extremely fragile -- a very high
        price to pay for some modest amount of syntax sugar.
        How about something like...

        class attrdict(dict):
        def __init__(self, *args, **kwargs):
        dict.__init__(s elf, *args, **kwargs)
        for k, v in self.items():
        dict.__setattr_ _(self, str(k), v)
        def __setitem__(sel f, k, v):
        dict.__setitem_ _(self, k, v)
        dict.__setattr_ _(self, str(k), v)
        __setattr__ = __setitem__

        Regards,
        Jordan

        Comment

        • Alex Martelli

          #5
          Re: class attrdict

          MonkeeSage <MonkeeSage@gma il.comwrote:
          On Mar 2, 9:25 pm, a...@mac.com (Alex Martelli) wrote:
          The problem is mostly that, given an instance a of attrdict, whether you
          can call (e.g.) a.update(foo) depends on whether you ever set
          a['update'], making the whole program extremely fragile -- a very high
          price to pay for some modest amount of syntax sugar.
          >
          How about something like...
          >
          class attrdict(dict):
          def __init__(self, *args, **kwargs):
          dict.__init__(s elf, *args, **kwargs)
          for k, v in self.items():
          dict.__setattr_ _(self, str(k), v)
          def __setitem__(sel f, k, v):
          dict.__setitem_ _(self, k, v)
          dict.__setattr_ _(self, str(k), v)
          __setattr__ = __setitem__
          Same problem: after x=attrdict(), x.update(foo) will work for a while,
          then suddenly stop working after some innocuous loop such as:
          for bah in yech: x[bah] = 23
          when one of the items in yech just happens to be the word 'update'
          (similar issues with words such as 'get', 'pop', 'clear', etc, etc).

          Miscegenation between attributes and items *inevitably* sucks.


          Alex

          Comment

          • Andrew Coffman

            #6
            Re: class attrdict

            Could you do something like this?

            class attrdict(dict):
            def __getattr__(sel f, attr):
            if self.has_key(at tr):
            return self[attr]
            else:
            message = "'attrdict' object has no attribute '%s'" % attr
            raise AttributeError, message

            If you have a dict item with the same name as a method in the class, you
            won't be able to get to it using syntax sugar, though.

            It doesn't seem that the syntax sugar saves you much typing anyway
            (a.foo vs. a['foo']), but perhaps it seems nicer in some aesthetic sense.

            - Andrew Coffman


            Alex Martelli wrote:
            MonkeeSage <MonkeeSage@gma il.comwrote:
            >
            >
            >>On Mar 2, 9:25 pm, a...@mac.com (Alex Martelli) wrote:
            >>
            >>>The problem is mostly that, given an instance a of attrdict, whether you
            >>>can call (e.g.) a.update(foo) depends on whether you ever set
            >>>a['update'], making the whole program extremely fragile -- a very high
            >>>price to pay for some modest amount of syntax sugar.
            >>
            >>How about something like...
            >>
            >>class attrdict(dict):
            > def __init__(self, *args, **kwargs):
            > dict.__init__(s elf, *args, **kwargs)
            > for k, v in self.items():
            > dict.__setattr_ _(self, str(k), v)
            > def __setitem__(sel f, k, v):
            > dict.__setitem_ _(self, k, v)
            > dict.__setattr_ _(self, str(k), v)
            > __setattr__ = __setitem__
            >
            >
            Same problem: after x=attrdict(), x.update(foo) will work for a while,
            then suddenly stop working after some innocuous loop such as:
            for bah in yech: x[bah] = 23
            when one of the items in yech just happens to be the word 'update'
            (similar issues with words such as 'get', 'pop', 'clear', etc, etc).
            >
            Miscegenation between attributes and items *inevitably* sucks.
            >
            >
            Alex

            Comment

            • Alex Martelli

              #7
              Re: class attrdict

              Andrew Coffman <pragmatic.pyth on@gmail.comwro te:
              Could you do something like this?
              >
              class attrdict(dict):
              def __getattr__(sel f, attr):
              if self.has_key(at tr):
              return self[attr]
              else:
              message = "'attrdict' object has no attribute '%s'" % attr
              raise AttributeError, message
              >
              If you have a dict item with the same name as a method in the class, you
              won't be able to get to it using syntax sugar, though.
              Yes. In __setattr__, you can choose what to do in that case (it doesn't
              seem to make sense to be able to *assign* to foo.get without being able
              to *access* as foo.get whatever you've assigned).
              It doesn't seem that the syntax sugar saves you much typing anyway
              (a.foo vs. a['foo']), but perhaps it seems nicer in some aesthetic sense.
              It would be nice, yes, weren't it for the inevitable irregularity, one
              way or another, caused by the clash between attributes and items.

              If, instead of subclassing dict, one wrapped it (and properly coded just
              a few specialmethods, no ordinary ones), the overall effect could be
              more regular (probably still with some limitation, since the dict does
              need to be kept somewhere, and keys clashing with the special methods'
              names would still have to be forbidden or otherwise specialcased).


              Alex

              Comment

              • MonkeeSage

                #8
                Re: class attrdict

                On Mar 3, 1:29 pm, a...@mac.com (Alex Martelli) wrote:
                It would be nice, yes, weren't it for the inevitable irregularity, one
                way or another, caused by the clash between attributes and items.
                In thinking about it, I think this might fall under 'we're all
                consenting adults here'. I mean, you can shadow the dict constructor
                from the toplevel if you're so inclined (or don't know any better).
                But you could make it harder to accidentally shadow builtin
                attributes:

                from warnings import warn
                class attrdict(dict):
                __builtins__ = dir(dict)
                def __init__(self, *args, **kwargs):
                dict.__init__(s elf, *args, **kwargs)
                for k, v in self.items():
                dict.__setattr_ _(self, str(k), v)
                def __setitem__(sel f, k, v):
                if k in self.__builtins __:
                warn('shadowing builtin attribute: %s' % k,
                RuntimeWarning, 2)
                dict.__setitem_ _(self, k, v)
                dict.__setattr_ _(self, str(k), v)
                __setattr__ = __setitem__

                a = attrdict([(1,2)], a=3, b=4)
                a.update = 'test'

                Regards,
                Jordan

                Comment

                • Alex Martelli

                  #9
                  Re: class attrdict

                  MonkeeSage <MonkeeSage@gma il.comwrote:
                  On Mar 3, 1:29 pm, a...@mac.com (Alex Martelli) wrote:
                  It would be nice, yes, weren't it for the inevitable irregularity, one
                  way or another, caused by the clash between attributes and items.
                  >
                  In thinking about it, I think this might fall under 'we're all
                  consenting adults here'. I mean, you can shadow the dict constructor
                  from the toplevel if you're so inclined (or don't know any better).
                  But you could make it harder to accidentally shadow builtin
                  attributes:
                  >
                  from warnings import warn
                  class attrdict(dict):
                  __builtins__ = dir(dict)
                  def __init__(self, *args, **kwargs):
                  dict.__init__(s elf, *args, **kwargs)
                  for k, v in self.items():
                  dict.__setattr_ _(self, str(k), v)
                  def __setitem__(sel f, k, v):
                  if k in self.__builtins __:
                  warn('shadowing builtin attribute: %s' % k,
                  RuntimeWarning, 2)
                  dict.__setitem_ _(self, k, v)
                  dict.__setattr_ _(self, str(k), v)
                  __setattr__ = __setitem__
                  >
                  a = attrdict([(1,2)], a=3, b=4)
                  a.update = 'test'
                  Besides missing the warning in the __init__, this also has pretty weird
                  behavior whenever subject to a .pop, .update, .setdefault, del of either
                  item or attr, etc, etc: the attributes and items "get out of sync" (and,
                  catching each and every mutating-method to keep the separate dicts of
                  items and attrs in perfect sync is somewhat of a nightmare).

                  If the consenting adults in question WANT totally murky, irregular,
                  buggy behavior hiding under every corner -- why are they using Python in
                  the first place? It's so much easier to make such utter messes in other
                  languages, after all.

                  It's impossible to make something foolproof, because fools are so
                  ingenious; Python tends to keep them at bay by cultural forces (a strong
                  collective bias towards simplicity, regularity, correctness). The
                  neverending quest to confuse attributes and items (inevitably leading to
                  complications, irregularity, and bugs) is better pursued in other
                  languages -- just about ANY other language except Python.


                  Alex

                  Comment

                  • MonkeeSage

                    #10
                    Re: class attrdict

                    On Mar 3, 7:54 pm, a...@mac.com (Alex Martelli) wrote:
                    Besides missing the warning in the __init__, this also has pretty weird
                    behavior whenever subject to a .pop, .update, .setdefault, del of either
                    item or attr, etc, etc: the attributes and items "get out of sync" (and,
                    catching each and every mutating-method to keep the separate dicts of
                    items and attrs in perfect sync is somewhat of a nightmare).
                    Good points. There are many complexities involved, and little actual
                    gain. Saving three or four chars isn't worth all the trouble. I hadn't
                    fully thought-out the situation.

                    Regards,
                    Jordan

                    Comment

                    • Alex Martelli

                      #11
                      Re: class attrdict

                      MonkeeSage <MonkeeSage@gma il.comwrote:
                      On Mar 3, 7:54 pm, a...@mac.com (Alex Martelli) wrote:
                      Besides missing the warning in the __init__, this also has pretty weird
                      behavior whenever subject to a .pop, .update, .setdefault, del of either
                      item or attr, etc, etc: the attributes and items "get out of sync" (and,
                      catching each and every mutating-method to keep the separate dicts of
                      items and attrs in perfect sync is somewhat of a nightmare).
                      >
                      Good points. There are many complexities involved, and little actual
                      gain. Saving three or four chars isn't worth all the trouble. I hadn't
                      fully thought-out the situation.
                      You make a good point. I do like being able to say foo.bar=baz rather
                      than foo['bar']=baz in certain cases -- not so much to save 3 chars, but
                      to avoid excessive punctuation; however, I don't really need this AND
                      all of dict's power at the same time, so, I don't inherit from dict:-).


                      Alex

                      Comment

                      • goodwolf

                        #12
                        Re: class attrdict

                        On Mar 3, 4:25 am, a...@mac.com (Alex Martelli) wrote:
                        Hallvard B Furuseth <h.b.furus...@u sit.uio.nowrote :
                        >
                        Does this class need anything more?
                        Is there any risk of a lookup loop?
                        Seems to work...
                        >
                        class attrdict(dict):
                        """Dict where d['foo'] also can be accessed as d.foo"""
                        def __init__(self, *args, **kwargs):
                        self.__dict__ = self
                        dict.__init__(s elf, *args, **kwargs)
                        def __repr__(self):
                        return dict.__repr__(s elf).join(("att rdict(", ")"))
                        >
                        The problem is mostly that, given an instance a of attrdict, whether you
                        can call (e.g.) a.update(foo) depends on whether you ever set
                        a['update'], making the whole program extremely fragile -- a very high
                        price to pay for some modest amount of syntax sugar.
                        >
                        Alex
                        Then you will prefer something like this:

                        class Namespace(objec t):
                        def __init__(self, __ns={}, **kwargs):
                        if kwargs: __ns.update(kwa rgs)
                        self.__dict__ = __ns

                        Comment

                        • goodwolf

                          #13
                          Re: class attrdict

                          On Mar 4, 1:03 pm, "goodwolf" <Robert.Ka...@g mail.comwrote:
                          On Mar 3, 4:25 am, a...@mac.com (Alex Martelli) wrote:
                          >
                          >
                          >
                          Hallvard B Furuseth <h.b.furus...@u sit.uio.nowrote :
                          >
                          Does this class need anything more?
                          Is there any risk of a lookup loop?
                          Seems to work...
                          >
                          class attrdict(dict):
                          """Dict where d['foo'] also can be accessed as d.foo"""
                          def __init__(self, *args, **kwargs):
                          self.__dict__ = self
                          dict.__init__(s elf, *args, **kwargs)
                          def __repr__(self):
                          return dict.__repr__(s elf).join(("att rdict(", ")"))
                          >
                          The problem is mostly that, given an instance a of attrdict, whether you
                          can call (e.g.) a.update(foo) depends on whether you ever set
                          a['update'], making the whole program extremely fragile -- a very high
                          price to pay for some modest amount of syntax sugar.
                          >
                          Alex
                          >
                          Then you will prefer something like this:
                          >
                          class Namespace(objec t):
                          def __init__(self, __ns={}, **kwargs):
                          if kwargs: __ns.update(kwa rgs)
                          self.__dict__ = __ns
                          oops, there is an error (empty dict is created once).
                          Here corrected one:

                          class Namespace(objec t):
                          def __init__(self, __ns=None, **kwargs):
                          if __ns is None:
                          self.__dict__ = kwargs
                          else:
                          assert len(kwargs) == 0
                          self.__dict__ = __ns

                          If you are familiar with JS then you can simulate JS Object:

                          class JSLikeObject(ob ject):
                          def __init__(self, __ns={}, **kwargs):
                          if kwargs: __ns.update(kwa rgs)
                          self.__dict__ = __ns
                          def __getitem__(sel f, name):
                          return getattr(self, name)
                          def __setitem__(sel f, name, value):
                          setattr(self, name, value)
                          def __delitem__(sel f, name):
                          delattr(self, name)
                          def __iter__(self):
                          return iter(self.__dic t__)
                          def __contains__(se lf, name):
                          return hasattr(self, name)

                          but I don't sagest to use it in real life.

                          Comment

                          • Alex Martelli

                            #14
                            Re: class attrdict

                            goodwolf <Robert.Katic@g mail.comwrote:
                            ...
                            Then you will prefer something like this:
                            >
                            class Namespace(objec t):
                            def __init__(self, __ns={}, **kwargs):
                            if kwargs: __ns.update(kwa rgs)
                            self.__dict__ = __ns
                            I might, if it weren't for the redundant "if" and the horribly buggy
                            interference between separate instances -- which is why I wrote it,
                            almost six years ago and without the bugs, as
                            <http://aspn.activestat e.com/ASPN/Python/Cookbook/Recipe/52308.

                            Not much later, in the printed Cookbook, I also gave some other good
                            ways and explained (as I did in the current thread) why confusing
                            attributes and items, as proposed in most of this thread, is really a
                            bad idea.


                            Alex

                            Comment

                            • goodwolf

                              #15
                              Re: class attrdict

                              class Namespace(objec t):
                              def __init__(self, __ns=None, **kwargs):
                              if __ns is None: #if no dictionary is given
                              self.__dict__ = kwargs #then use kwargs without copying
                              or creating new dict
                              else:
                              assert len(kwargs) == 0
                              self.__dict__ = __ns #else use dictionary without
                              copyng

                              #additional methods for JS like object (ONLY FOR DEMONSTRATION)
                              def __getitem__(sel f, name):
                              return getattr(self, name)
                              def __setitem__(sel f, name, value):
                              setattr(self, name, value)
                              def __delitem__(sel f, name):
                              delattr(self, name)
                              def __iter__(self):
                              return iter(self.__dic t__)
                              def __contains__(se lf, name):
                              return hasattr(self, name)

                              Comment

                              Working...