37

Quite often, I find myself wanting a simple, "dump" object in Python which behaves like a JavaScript object (ie, its members can be accessed either with .member or with ['member']).

Usually I'll just stick this at the top of the .py:

class DumbObject(dict):
    def __getattr__(self, attr):
        return self[attr]
    def __stattr__(self, attr, value):
        self[attr] = value

But that's kind of lame, and there is at least one bug with that implementation (although I can't remember what it is).

So, is there something similar in the standard library?

And, for the record, simply instanciating object doesn't work:

>>> obj = object()
>>> obj.airspeed = 42
Traceback (most recent call last):
  File "", line 1, in 
AttributeError: 'object' object has no attribute 'airspeed'

Edit: (dang, should have seen this one coming)… Don't worry! I'm not trying to write JavaScript in Python. The place I most often find I want this is while I'm still experimenting: I have a collection of "stuff" that doesn't quite feel right to put in a dictionary, but also doesn't feel right to have its own class.

4
  • As I said below, you're trying to code around incorrect instincts brought from another language. In Python, we write classes even when we're "experimenting". Try -- stop writing DumbObject, actually write "class Foo" and see that it does not bite. Commented Apr 14, 2010 at 21:36
  • And, as I said below… That is an interesting idea - simply using, eg, class Foo: airspeed = 42 - I hadn't considered that. However, it does have the problem that it doesn't behave like a dictionary (yes, there is dict…), which makes experimentation more difficult. Commented Apr 14, 2010 at 22:27
  • 1
    Looks like something we all want to do with Python :) stackoverflow.com/questions/2466191/… Commented Apr 15, 2010 at 0:45
  • I'm sticking my neck out here but have you considered the use of a key/value store like Redis? It excels as a collection of "stuff", as you put it. The Python client is absurdly easy to use. Commented Apr 15, 2010 at 0:59

11 Answers 11

24

In Python 3.3+ you can use SimpleNamespace, which does exactly what you're looking for:

from types import SimpleNamespace
obj = SimpleNamespace()
obj.airspeed = 42

https://docs.python.org/3.4/library/types.html#types.SimpleNamespace

Sign up to request clarification or add additional context in comments.

2 Comments

Acts exactly like JavaScript Object. This answer should be re-accepted!
TypeError: 'types.SimpleNamespace' object is not subscriptable when trying obj["airspeed"] :(
14

You can try with attrdict:

class attrdict(dict):
    def __init__(self, *args, **kwargs):
        dict.__init__(self, *args, **kwargs)
        self.__dict__ = self

a = attrdict(x=1, y=2)
print a.x, a.y
print a['x'], a['y']

b = attrdict()
b.x, b.y  = 1, 2
print b.x, b.y
print b['x'], b['y']

5 Comments

Should complete your example with, for example, print b['x']. To show that it supports both attribute access methods.
Good point, PreludeAndFugue. I updated the code. Thanks. :)
Changing __dict__?! That's disgusting! But, at the same time, intriguing…
WARNING! This works, but can cause huge memory leaks due to the circular reference. Putting it in a loop fills my 8 GB of RAM in mere seconds. Don't use this for code that actually matters!
@fletom let me guess... you've decided to create a gazillion objects using range in python 2... the memory is not leaking from the attrdicts, it's because range actually creates a list with the numbers in python 2! The garbage collector handles the circular references just fine :) I've just created and discarded 10**9 attrdicts and memory usage didn't even change (did not run till the end, but enough time to see it wasn't leaking). Use xrange and you'll be fine ;)
10

There is no "standard library" with that kind of object, but on ActiveState there is a quite well-known recipe from Alex Martelli, called "bunch".

Note: there's also a package available on pypi called bunch and that should do about the same thing, but I do not know anything about its implementation and quality.

Comments

9

You may be interested in collections.namedtuple

Comments

7

If I understand you correctly, you want an object where you can just dump attributes. If I am correct, all you would have to do is create an empty class. For example:

>>> class DumpObject: pass
... 
>>>#example of usage:
...
>>> a = DumpObject()
>>> a.airspeed = 420
>>> print a.airspeed
420

That's it

1 Comment

Close, but I'd also like to be able to treat it like a dict (I guess that wasn't entirely clear).
3

This method works with nested dictionaries and is quite concise.

Firstly, define an empty class. Instances of this class can be set any attributes.

class holder:
    pass

Next, just use this function.

def dotted(object):

    if isinstance(object, dict):
        parent = holder()

        for key in object:

            child = object.get(key)
            setattr(parent, key, dotted(child))

        return parent

    return object

Example usage.

users = dotted(
{
    'john': {
        'fullname': 'John Doe'
        'age': 24,
        'hobbies': [
            'programming',
            'jogging'
        ]
    }
}

users.john.fullname # 'John Doe'
users.john.age      # 24
users.john.hobbies  # ['programming', 'jogging']

users.john.age = 25
users.john.age      # 25

Comments

1

Well, as far as i can tell (correct me if i wrong) every example here doesn't convert nested dictionaries to a new type. So i made this example:

class DotDictionary(dict):
 
    def __init__(self, init_value):
        if not isinstance(init_value, dict):
            raise TypeError('Expected dict, got {}'.format(type(init_value)))
 
        self._dict_to_self(init_value)
 
    def __getattr__(self, name):
        return self[name]
 
    def __setattr__(self, name, val):
        self[name] = val
 
    def __delattr__(self, name):
        del self[name]
 
    def _dict_to_self(self, dct):
        for key, value in dct.items():
            if isinstance(value, dict):
                value = DotDictionary(value)
            if isinstance(value, (tuple, list)):
                for i, _ in enumerate(value):
                    if isinstance(value[i], dict):
                        value[i] = DotDictionary(value[i])
 
            self[key] = value

ideone

I'm not 100% sure that it works absolutely correct, so no warranty here.

Also maybe it's not pythonic-way so i'm ready to take advices.

Comments

0

I'm not a big fan of that, because you get into the "what if you define 'keys'":

foo = DumbObject()
foo.moshe = 'here'
foo.keys # --> gives a callable, if you'll call it you'll get ['moshe']
foo.keys = 'front-door'
foo.keys # --> oh look, now it's a string 'front-door'

Is "keys" supposed to be a defined method on your object, or is that just happenstance? If it is, then you better define DumbObject as

class DumbObject(object):
    def __init__(self):
        self._dict = {}
    def __getitem__(self, key):
        return self._dict[key]
    __getattr__ = __getitem__
    # Ditto for set
def asDictionary(do):
    return do._dict

But why are you doing it? Are you sure that this is the right answer? Perhaps you should be using the right type of object?

4 Comments

Ah, I hadn't considered that. In that case, there are at least two bugs with the implementation I've used in the past :P
I often like it to experiment with - for data that doesn't really feel "right" in a dictionary, but formalizing it into a class feel overkill.
I think it's your feelings which are the problem -- "formalizing it into a class" is probably not an overkill. It feels like an overkill in languages where classes aren't, at a minimum, a mere "class Foo(object): pass" :-D
That is an interesting idea - simply using, eg, class Foo: airspeed = 42 - I hadn't considered that. However, it does have the problem that it doesn't behave like a dictionary (yes, there is __dict__…), which makes experimentation more difficult.
0
DumbObject = lambda: "function is a first-class object And this sentence isn't necessary :), you could simply put None or Pass"

DumbObject.name = "Dumb"
DumbObject.speak = lambda word: print(word)
DumbObject.tell_name =  lambda : print(f"I am {DumbObject.name} and have no self")

DumbObject.speak("I speak not")
DumbObject.tell_name()

Comments

0

This implementation allows you to arbitrarily nest dicts and SimpleNamespaces, and is compatible with json.dumps, since it inherits from dict.

from types import SimpleNamespace
import json


class DictNamespace(dict):

    def __getattr__(self, key):
        return self[key]

    def __setattr__(self, key, value):
        self[key] = self.construct_namespace(value)

    def __delattr__(self, key):
        del self[key]

    @staticmethod
    def construct_namespace(maybe_dict):
        if isinstance(maybe_dict, dict):
            for key, value in maybe_dict.items():
                if isinstance(value, dict):
                    maybe_dict[key] = DictNamespace(**value)
                elif isinstance(value, SimpleNamespace):
                    maybe_dict[key] = DictNamespace(**value.__dict__)
                else:
                    maybe_dict[key] = value
        return maybe_dict

    def __init__(self, **kwargs):
        super().__init__(**kwargs)
        DictNamespace.construct_namespace(self)


thing = DictNamespace(
    one=1, 
    two=dict(three=3), 
    five=DictNamespace(six=6), 
    eight=SimpleNamespace(nine=9)
)

thing.eleven = 11
print(thing.one)
print(thing.two)
print(thing.two.three)
print(thing.five)
print(thing.five.six)
print(thing.eight)
print(thing.eight.nine)
print(thing["eight"]["nine"])
print(thing["eight"].nine)
print(thing.eleven)
del thing.eleven
assert thing.get("eleven", None) is None
print(json.dumps(thing))

Comments

0

You can use builtin SimpleNamespace but it doesn't solve some issues like:

  • accessing the dictionary both ways - with dot notation and brackets,
  • exploring nested elements of received ready data the way you can access objects in javascript,
  • overriding the native dict method names like keys, items, values etc.

To get full javascript experience and avoid above problems you can use MIT open source library jsify from pypi. Here is an example:

from jsify import jsify, Dict

js_dict = Dict({}, a=1, b=2) # Create sample javascript object
js_dict.c = 3 # You can use dot notation to access/create attributes
js_dict['d'] = 4 # Same as brackets
print(js_dict.a + js_dict['b'] + js_dict.c + js_dict.d) # Output: 10

native_dict = dict(a=1, b=2, c=3, d=4)
js_dict = jsify(native_dict) # You can create a wrap of native objects
# And access them in javascript style
print(js_dict.a + js_dict['b'] + js_dict.c + js_dict.d) # Output: 10

Docs are here: https://citsystems.github.io/jsify/

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.