开发者

JavaScript-like Object in Python standard library?

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.


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


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']


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.


You may be interested in collections.namedtuple


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


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.


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


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?


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()


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))
0

上一篇:

下一篇:

精彩评论

暂无评论...
验证码 换一张
取 消

最新问答

问答排行榜