bcj / attrdict Goto Github PK
View Code? Open in Web Editor NEWA dictionary that allows attribute-style access.
License: MIT License
A dictionary that allows attribute-style access.
License: MIT License
Given that there seems to be continued interest in a closer matching to dict
, I may be convinced that attrdict should provide both an AttrMapping
which reflects current AttrDict
and an AttrDict
that subclasses dict
. Would this be a satisfactory solution for those wishing AttrDict
more-closely reflected dict
?
This would have to be a major-version change since it could easily effect people using this in prod.
The project description ready:
A dictionary that allows attribute-style access.
However, I discovered:
> from attrdict import AttrDict
> a = AttrDict()
> isinstance(a, dict)
False
Is there a reason AttrDict is not extending dict? It also makes it impossible to use AttrDict as a "drop in" replacement in existing APIs.
Consider an API that used to return (or receive) a dict and could be switched to an AttrDict.
The common thing to do if you override __getattr__
is to also override __dir__
like so:
def __dir__(self):
# you may also want to filter the keys to only include the strings
return sorted(set(super(AttrDict, self).__dir__()) + set(self.keys()))
This would make it much easier to use attrdict in IPython and other IDEs as you would gain auto-completion.
Hello bcj,
When i pass {'get': 'example'}
to __init__
method, instance created, but i cant access dict key by getattr
since __init__
method only add no-conflict key as instance property.
But when i user attrdict_instance.property = "example"
, it raise a key error.
The question is : this two place acts not the same way.(__init__
and setattr
)
Dict has key get
but i can't access it(if i haven't read source code , i wouldn't know how can i access it correctly.)
Would __init__
method raises a KeyError to tell user do not use reserved key-name when collision being caught be a better way?
Best wishes:)
If Recursive mode is set, all Sequences will be converted into a list by build
e.g.:
>>> adict = Attrdict({'foo': ({'bar': 'baz'}, {'lorem': 'ipsum'})
>>> isinstance(adict.foo, list)
True
The Sequence's type should be grabbed from the object.
I find I need an immutable mapping fairly often. Would it be reasonable to add an attrdict.FrozenAttrDict
class that supports __hash__
?
I'd like to try to use your project but the licensing has me worried. Is it possible to change to a MIT or apache 2.0 license instead of your personal license?
As the title states, just a list of regular dicts instead of AttrDicts. Here is a workaround.
def deep_attrdict(d: dict) -> AttrDict:
"""Uses recursion to dive deep into the dict
and convert all dict types to type AttrDict. This will
eliminate the bug where pulling a list of dicts
out of an AttrDict results in just a list of dicts.
"""
d = AttrDict(d)
for k, v in d.items():
if isinstance(v, dict):
d[k] = deep_attrdict(v)
elif isinstance(v, list):
for i, item in enumerate(v):
if isinstance(item, dict):
v[i] = deep_attrdict(item)
return d
In [17]: a = AttrDict()
In [18]: a.b = AttrDict()
In [19]: a.b.c = "Hi there"
In [20]: a
Out[20]: AttrDict({'b': AttrDict({})})
In [21]: a.b
Out[21]: AttrDict({})
In [22]: a.b.c
---------------------------------------------------------------------------
AttributeError Traceback (most recent call last)
<ipython-input-22-d34de4f2be62> in <module>()
----> 1 a.b.c
/usr/local/lib/python2.7/dist-packages/attrdict/mixins.pyc in __getattr__(self, key)
80 raise AttributeError(
81 "'{cls}' instance has no attribute '{name}'".format(
---> 82 cls=self.__class__.__name__, name=key
83 )
84 )
AttributeError: 'AttrDict' instance has no attribute 'c'
>>> attr = AttrDict({'foo': {"bar": "molotov"}})
>>> attr.foo
AttrDict({'bar': 'molotov'})
>>> attr.foo.bar = 'update molotov'
>>> attr
AttrDict({'foo': {'bar': 'molotov'}})
>>>
Really like this module.
I have code that uses the old UserDict which has a .data
attribute. My code uses that a lot.
My old code uses stuff like:
theImportantData = UserDict()
.. then initialize stuff and then...
theItem = theImportantData.data["NAME"]
(Don't ask me why I liked it, I just did... )
But when I tried to subclass AttrDict like this
class newAttrDict(AttrDict):
def __init__(self):
self.data = self
to be able to get access to the theImportantData.data
attribute,
I get this:
File "/usr/local/lib/python3.4/site-packages/attrdict/__init__.py", line 115, in __getattr__
if self._default_factory is None or key.startswith('_'):
File "/usr/local/lib/python3.4/site-packages/attrdict/__init__.py", line 115, in __getattr__
if self._default_factory is None or key.startswith('_'):
File "/usr/local/lib/python3.4/site-packages/attrdict/__init__.py", line 115, in __getattr__
if self._default_factory is None or key.startswith('_'):
File "/usr/local/lib/python3.4/site-packages/attrdict/__init__.py", line 115, in __getattr__
if self._default_factory is None or key.startswith('_'):
File "/usr/local/lib/python3.4/site-packages/attrdict/__init__.py", line 115, in __getattr__
if self._default_factory is None or key.startswith('_'):
File "/usr/local/lib/python3.4/site-packages/attrdict/__init__.py", line 115, in __getattr__
if self._default_factory is None or key.startswith('_'):
File "/usr/local/lib/python3.4/site-packages/attrdict/__init__.py", line 115, in __getattr__
if self._default_factory is None or key.startswith('_'):
File "/usr/local/lib/python3.4/site-packages/attrdict/__init__.py", line 115, in __getattr__
if self._default_factory is None or key.startswith('_'):
File "/usr/local/lib/python3.4/site-packages/attrdict/__init__.py", line 115, in __getattr__
if self._default_factory is None or key.startswith('_'):
RuntimeError: maximum recursion depth exceeded while calling a Python object
Anyway thanks for a nice addition to the Dict class.
It might be nice just to have a .data
attribute like the old UserDict, instead of playing around with _mapping
.
Thanks,
Mike
Would be nice to maintain order. Could be an option, or just always inherit from OrderedDict
.
This somewhat relates to #18, but I would like to raise this issue independently.
> dict(a=23) == dict(dict(a=23))
True
> AttrDict(a=23) == AttrDict(dict(a=23))
...
TypeError: __init__() got an unexpected keyword argument 'a'
The **kwargs
based constructor of dict is incredibly helpful.
I'm using AttrDict as the dict type for ConfigParser and found that it doesn't implement the copy()
method, which ConfigParser uses when calling get()
to retrieve a config field.
In my use case I extended AttrDict to implement this method. It would be nice to have this in upstream though.
class AttrDict(attrdict.AttrDict):
def copy(self):
return self._mapping.copy()
Or to patch it only if it doesn't exist:
if not hasattr(AttrDict, "copy"):
setattr(AttrDict, "copy", lambda self: self._mapping.copy())
75196cc broke backward compatibility.
I can no longer use deepcopy from attrdict,
even as a convenient shortcut to avoid further imports.
just wanted to check in on whether the maintainers of this library are present and willing to accept pull requests for the following
Hello everyone,
test = AttrDict({"a": {"b": "c"}})
type(test.a)
<class 'attrdict.AttrDict'>
type(test.get("a"))
<type 'dict'>
Is this behavior wanted or do you think as well that it is something worth changing?
As a workaround I am using: a = test.a if "a" in test else None
Best regards and thank you for this module,
Tim
from attrdict import AttrMap as mp
m=mp({"a":[1,2,3]})
m.a
return (1,2,3)
This sould not happen, right? Would be really good to fix this :-)
Thanks ๐
Issue 4 was the result of not enough testing. Get 100% coverage (plus the badge)
This example is shown:
> a = AttrDict({'foo': {'bar': 'baz'}})
> a.foo.bar
'baz'
> a['foo'].bar
AttributeError: 'dict' object has no attribute 'bar'
This is not the case. a['foo']
is still an AttrDict and a['foo'].bar
will happily return 'baz'
.
In Python 3.5 with attrdict 2.0.0
first_dict = AttrDict()
first_dict.second_list = list()
for i in range(10):
first_dict.second_list.append(i)
print("Debug dict. Attr dict01: '{}'".format(first_dict))
Gives
AttributeError: 'tuple' object has no attribute 'append'
It looks like AttrDict converts empty lists into tuples.
AttrDict is mutable, it would be more correct to extend MutableMapping than Mapping.
Is there a way to use variables in the middle of a dotted phrase?
name = 'foo'
indict = {name: "name's value"}
adict = AttrDict(indict)
print adict.foo # works
print adict.name # fails, with AttributeError, because it's looking for `name` and not `foo`
List are not handled properly
Example:
d = AttrDict({"a": 1, "b": 2, "c": {"d": 3, "e": [4, 5, {"f": 6, "g": 7}]}})
d.c.e[2].f # AttributeError: 'dict' object has no attribute 'f'
_build method can be changed like this:
if isinstance(obj, Mapping):
return obj
if isinstance(obj, list):
return [cls._build() for o in obj]
return obj
Please support deep-copy for creating an exact non-shared copy of the object (or a branch of it).
Preferably as a method of AttrDict, as the dict object.
e.g.
def copy(self, item=None):
if item:
obj = self.get(item, None)
if obj is None:
return None
if type(obj) is list:
return list(obj)
if type(obj) is dict:
return AttrDict2(dict(obj))
return obj
else:
return AttrDict2(dict(self))
The recursive
parameter shouldn't modify the contents of the object.
>>> attrdict.AttrDict({'list': [{'value': 1}, {'value': 2}]}, recursive=False)
AttrDict({'list': [{'value': 1}, {'value': 2}], 'recursive': False})
Let's say I have an AttrDict
object for which my keys should be all numbers. Recursive should not be added to the keys.
>>> ad=attrdict.AttrDict({1: 'one', 2: 'two'}, recursive=False)
>>> ad.keys()
dict_keys([1, 2, 'recursive'])
Hi!
There seem to be two bugs in the examples.
First, this example right off the docs
> attr = AttrDict({'foo': [{'bar': 'baz'}, {'bar': 'qux'}]})
> for sub_attr in attr.foo:
> print(subattr.foo)
'baz'
'qux'
gets me NameError: name 'subattr' is not defined
because of the missing underscore in subattr
but it's also foo
a second time, while it would need to be bar
, i.e. making print(sub_attr.bar)
for the body of the loop.
Further down the docs read:
> adict = AttrDict({'list': [{'value': 1}, {'value': 2}]}, recursive=False)
> for element in adict.list:
> isinstance(element, AttrDict)
False
False
But in ipython (with Python 2.7.13), I get this, instead:
In [29]: adict = AttrDict({'list': [{'value': 1}, {'value': 2}]}, recursive=False)
In [31]: [isinstance(element, AttrDict) for element in adict.list]
Out[31]: [True, True] # ???
I am unsure if recursive=False
is not working as expected or if just the example is broken. Please enlighten me. Thanks!
PS: The second example seems to also miss use of print(...)
for the examples to be consistent to each other.
The mixins.Attr._valid_name
method only allows ASCII attributes. In python 3, unicode attributes are allowed.
Should chained attribute creation be possible?
e.g.,
>>>AttrDict().foo.bar.baz = 1
AttributeError: 'AttrDict' object has no attribute 'foo'
A declarative, efficient, and flexible JavaScript library for building user interfaces.
๐ Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.
TypeScript is a superset of JavaScript that compiles to clean JavaScript output.
An Open Source Machine Learning Framework for Everyone
The Web framework for perfectionists with deadlines.
A PHP framework for web artisans
Bring data to life with SVG, Canvas and HTML. ๐๐๐
JavaScript (JS) is a lightweight interpreted programming language with first-class functions.
Some thing interesting about web. New door for the world.
A server is a program made to process requests and deliver data to clients.
Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.
Some thing interesting about visualization, use data art
Some thing interesting about game, make everyone happy.
We are working to build community through open source technology. NB: members must have two-factor auth.
Open source projects and samples from Microsoft.
Google โค๏ธ Open Source for everyone.
Alibaba Open Source for everyone
Data-Driven Documents codes.
China tencent open source team.