Skip to content

Instantly share code, notes, and snippets.

@turicas
Created December 22, 2011 16:21
Show Gist options
  • Star 25 You must be signed in to star a gist
  • Fork 5 You must be signed in to fork a gist
  • Save turicas/1510860 to your computer and use it in GitHub Desktop.
Save turicas/1510860 to your computer and use it in GitHub Desktop.
Python class with dict-like get/set item
#!/usr/bin/env python
# coding: utf-8
class AttrDict(object):
def __init__(self, init=None):
if init is not None:
self.__dict__.update(init)
def __getitem__(self, key):
return self.__dict__[key]
def __setitem__(self, key, value):
self.__dict__[key] = value
def __delitem__(self, key):
del self.__dict__[key]
def __contains__(self, key):
return key in self.__dict__
def __len__(self):
return len(self.__dict__)
def __repr__(self):
return repr(self.__dict__)
#!/usr/bin/env python
# coding: utf-8
import unittest
from attrdict import AttrDict
class TestAttrDict(unittest.TestCase):
def test_should_init_with_one_dict(self):
my_dict = AttrDict({'eggs': 42, 'spam': 'ham'})
self.assertEquals(my_dict.eggs, 42)
self.assertEquals(my_dict['eggs'], 42)
self.assertEquals(my_dict.spam, 'ham')
self.assertEquals(my_dict['spam'], 'ham')
def test_should_not_change_values_by_inited_dict(self):
base = {'eggs': 42, 'spam': 'ham'}
my_dict = AttrDict(base)
base['eggs'] = 123
self.assertEquals(my_dict.eggs, 42)
self.assertEquals(my_dict['eggs'], 42)
self.assertEquals(my_dict.spam, 'ham')
self.assertEquals(my_dict['spam'], 'ham')
def test_get_item(self):
my_dict = AttrDict()
my_dict.test = 123
self.assertEquals(my_dict.test, 123)
self.assertEquals(my_dict['test'], 123)
def test_set_item(self):
my_dict = AttrDict()
my_dict['test'] = 123
self.assertEquals(my_dict['test'], 123)
self.assertEquals(my_dict.test, 123)
def test_del_attr(self):
my_dict = AttrDict()
my_dict['test'] = 123
my_dict['python'] = 42
del my_dict['test']
del my_dict.python
with self.assertRaises(KeyError):
temp = my_dict['test']
with self.assertRaises(AttributeError):
temp = my_dict.python
def test_in_should_work_like_in_dict(self):
my_dict = AttrDict()
my_dict['test'] = 123
self.assertIn('test', my_dict)
self.assertNotIn('bla', my_dict)
def test_len_should_work_like_in_dict(self):
my_dict = AttrDict()
my_dict['test'] = 123
my_dict.python = 42
self.assertEquals(len(my_dict), 2)
def test_repr(self):
my_dict = AttrDict()
my_dict['test'] = 123
my_dict.python = 42
self.assertEquals(repr(my_dict),"{'test': 123, 'python': 42}")
def test_equal(self):
my_dict = AttrDict({'test': 123})
my_dict_2 = AttrDict({'test': 123})
self.assertEquals(my_dict, my_dict_2)
@yunake
Copy link

yunake commented Jun 2, 2014

I always used this as an alternative. repr is just nice to have but not really necessary:

class Struct:
    """ Object that acts like a dictionary """
    def __init__(self, **entries):
        self.__dict__.update(entries)

    def __repr__(self):
        args = ['{}={}'.format(k, repr(v)) for (k,v) in vars(self).items()]
        return 'Struct({})'.format(', '.join(args))

@maajdl
Copy link

maajdl commented Dec 29, 2017

Very useful!
I was already using a similar class with get and set item.
The full class is even more useful.

@maajdl
Copy link

maajdl commented Jan 12, 2018

When creating a list comprehension based on a attrdict, the getitem method receives integer keys.
This causes an error, since the attrdict getitem method can't make sense of an integer key.
I don't understand why the list comprehension works like that.
I also don't see the best way to modify attrdict to solve this problem.

@ramAdam
Copy link

ramAdam commented Jan 18, 2019

@yunake
s = Struct ()
s['jam'] = 'jelly' <------------------ fails , how does it act like a dictionary ?

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment