Welcome, guest | Sign In | My Account | Store | Cart

Sometimes accessing dictionary items as if they were member variables can be convenient.

Python, 22 lines
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
class attrdict(dict):
    """A dict whose items can also be accessed as member variables.

    >>> d = attrdict(a=1, b=2)
    >>> d['c'] = 3
    >>> print d.a, d.b, d.c
    1 2 3
    >>> d.b = 10
    >>> print d['b']
    10

    # but be careful, it's easy to hide methods
    >>> print d.get('c')
    3
    >>> d['get'] = 4
    >>> print d.get('a')
    Traceback (most recent call last):
    TypeError: 'int' object is not callable
    """
    def __init__(self, *args, **kwargs):
        dict.__init__(self, *args, **kwargs)
        self.__dict__ = self

See the docstring for examples, notably the danger of overriding methods. I would expect this to work on Python 2.2+ but I've only tested it on 2.3.

4 comments

aspn 16 years, 11 months ago  # | flag

Bad idea because... Member storage is as easely archieved with an empty class, without the hazards of dressing up like a dict but not beeing one.

All these members are belonging to a dict:

__class__, __cmp__, __contains__, __delattr__, __delitem__, __dict__, __doc__, __eq__, __ge__, __getattribute__, __getitem__, __gt__, __hash__, __init__, __iter__, __le__, __len__, __lt__, __module__, __ne__, __new__, __reduce__, __reduce_ex__, __repr__, __setattr__, __setitem__, __str__, __weakref__, clear, copy, fromkeys, get, has_key, items, iteritems, iterkeys, itervalues, keys, pop, popitem, setdefault, update and values.

Can you be sure that you can keep all in your head? Much more make sure that none of your perhaps external data isn't using any of these keys. Worst kind of errors are those which pop up and say at some point "TypeError: 'str' object is not callable" because somewhere within your zigillion of kv pairs there's an error.

You're providing two near and similar interfaces to modify the same data, that that's against "preferably one way" of doing things.

Messing with __dict__ is not guaranteed to work in the future as it's an implementation detail that's stated as beeing subject to change by the language-devs if need be.

Confusing and intermixing storage-semantics with member-semantics bears bad code.

Using members for data collections from outside of the class code can be used in a good manner, but is more often associated with bad code ( nothing that couldn't be remedied in a normal class however with automatic setter/getter methods and accepting the members as interfaces )

robert stone 16 years, 11 months ago  # | flag

Alternate Implementation. Here's an alternate implementation (not really the same thing, but similar):

class AttrDict(dict):
    def __init__(self, *args, **kwargs):
        dict.__init__(self, *args, **kwargs)
    def __getattr__(self, name):
        return self[name]

In this one you can read dict entries as attributes, but attributes and dict entries are not the same thing. So, this one has the advantage that dict entries won't mask attributes, but the disadvantage that you can't read attributes using the dict subscript (d[k]) notation.

You could more fully emulate the original by defining __getitem__(), __setitem__(), etc. (container special methods.)

Ben Last 16 years, 11 months ago  # | flag

It's that wheel again... This idea keeps cropping up; perhaps (as one commenter says) it's a bad mixing of semantics, but it's also extremely useful on many occasions. I wrote an implementation before finding one already in Zope:

http://www.livejournal.com/users/benlast/12301.html

Wavy Davy 16 years, 7 months ago  # | flag

Do you need key access? Rather than a confusing mash of both objects and dicts, why not just add iteration over the attibutes? Leave setting and getting alone.

TBH, I don't want a dict object, I just want an object with named attributes, but I want to be able to iterate over those attributes. I can test for existance of an attribute with a has_attr function. You can just map the desired functions across to the __dict__ object.

eg.

class iterobject(object):

    keys       = self.__dict__.keys
    values     = self.__dict__.values
    items      = self.__dict__.items
    iterkeys   = self.__dict__.iterkeys
    itervalues = self.__dict__.itervalues
    iteritems  = self.__dict__.iteritems
    __iter__   = self.__dict__.__iter__

    def has_attr(self, attr):
        return self.__dict__.has_key(attr)
Created by Jimmy Retzlaff on Mon, 3 Jan 2005 (PSF)
Python recipes (4591)
Jimmy Retzlaff's recipes (6)

Required Modules

  • (none specified)

Other Information and Tasks