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 dict
s and SimpleNamespace
s, 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))
精彩评论