Show HN: Addict – a Python dict whos values can be get and set using attributes
github.com
github.com
from collections import defaultdict
class D(defaultdict):
def __init__(self):
super(D, self).__init__(D)
__getattr__ = defaultdict.__getitem__
__setattr__ = defaultdict.__setitem__
__repr__ = dict.__repr__But yes, the idea of storing everything twice (once as an item and once as an attribute) just so you can unconditionally add defaults in __getattr__ is… bizarre and will break things horribly as soon as you accidentally do something like:
myDict['get'] = 'foo'You are right I dind't notice that before. But looking at the code it seems that the only permitted values are dict and str?!
> But yes, the idea of storing everything twice (once as an item and once as an attribute) I don't get that, you don't store things twice and nothing breaks, at least with the example code I wrote above. Try:
>>> a=D()
>>> a.lol = 2
>>> a['get'] = 'foo'
>>> a
{'get': 'foo', 'lol': 1}
>>> vars(a)
{}Your code is fine - you're not storing everything twice, but he is (in _set_both()).
You're welcome to your opinion, but I don't think it's productive to wield the phrases "pythonic" and "unpythonic" as if they represent an objective truth. If you have a problem with the library (and there are a few angles you could take), say that instead of denouncing the library as blasphemy.
Its a shortcut to dismiss someone's views. Having an opinion rejected with a "import this, line N" is... such an annoyance. Some people treat the damn thing like a strict bible
Unless it is written PEP8 or by Guido, I would never take a "pythonicism" to be nothing more than short-hand for "I think this is (in)consistent with the larger patterns and traditions of Python as I understand them."
from collections import defaultdict
def dd(): return defaultdict(dd)
d = dd()
d['a']['b']['c'] # => defaultdict(dd, {})
d['a']['b']['c'] = 'test'
d['a']['b']['c'] # => 'test'The surprising bit that would stop me dropping this in any real code is that as an end user, it's not immediately clear how the mapping interface should apply to this class. It's all implementation defined but it's not actually defined in the implementation. e.g., Does my Dict() have the key 'a.b.c'? Nope, it only has 'a' and the other keys are managed through some magic. Same.applies to iteration, it might be reasonable to assume that'd be a depth first terminating node iterator but it would actually just iterate the top level of the main Dict instance.
The mutate-on-get makes it very similar to how Matlab does its "structs", I'm mostly undecided on that but at times it can be nice.
Suppose I have an empty defaultdict(list). I do the following:
d = defaultdict(list)
l = d['foo']
According to no-mutate-on-get semantics, we now have an empty list, and d is still empty.
So what happens if I do: l.append('bar')
You might expect the dict to now be populated with {'foo': ['bar']}. However, the dict has no way of knowing (without a large amount of additional tricks) whether the new list that it previously returned had been later modified. So what would actually happen would be nothing, d would remain empty. Worse, now your behaviour is different depending on whether or not the 'foo' key was already present (if it was, you would've gotten a reference to the actual value and mutated it, and d would have changed as a result).As I see it, there are only two sane solutions to this problem: Either immediately store every generated value so any subsequent mutation is saved (the solution used here)...or require that your default values be immutable.
This uses twice as much memory as it needs to (and opens the door to potential inconsistency) by storing everything redundantly in both the attributes dictionary and the underlying dict.
Setting an item with a non-string key (e.g. An int or even unicode) is silently ignored.
myDict[u'foo'] = 'bar' # <- this ought to do something
Setting or deleting an item with the same name as an existing attribute (e.g. the name of a method) will replace or delete that attribute. myDict['get'] = 'foo' # <- should not replace the method myDict.get
This can't be initialised with kwargs or an iterator like an ordinary dict can. class Storage:
pass"Professional", to me, is boring - it's what the pointy haired boss wants you to be because anything remotely outside of the box doesn't conform to their risk-averse norms.
Pythons, on the other hand...
You can be empathetic towards those who struggle with addiction and use the word in a witty or comical fashion. The two are not mutually exclusive.
We're talking one line in a description (and possibly the name, though that's less impactful). Changing it doesn't change the programme. Why do some people get so sensitive about something so trivial and not want to change it?
"Why do some people get so sensitive about something so trivial and not want to change it?"
You're joking, right? You're sensitive about the name of something you didn't create and want it changed but it would be wrong for the author to not want to change it because you're sensitive about it?
Never utter that in front of someone who has serious experience with heroin or the repercussions.
Nevertheless, I was more playing with the fact that the number of drug-named libraries in Ruby was sometimes used for cheap shots at the community.
It always has frustrated me to see that attribute- and dictionary access is so similar but still different. Of course in most cases, there are some good reasons to have it different -- but sometimes there are also good reasons to generalize access to some important values.
So, we have duck typing and car typing. And both together we have a duck-car, because it quacks like a duck and runs like a car.
Quite complete attribute accessible dict, with iteration by full-depth keys. Very useful for human readable output.
I'm reminded of the web designer example here (https://gist.github.com/fmeyer/289467) line 89.
I know that people use github for many things, including tracking their config files, etc. However, this seems to be a post by the owner of the repo. Would you ever, I mean _ever_ be happy if someone required this sort of 'library' as a dependency in a project? I know I would not.
https://github.com/rcarmo/python-utils/blob/master/core.py
Can't really see this as newsworthy, sorry.
Edited to add: didn't mean to sound mean (if you'll pardon the pun), just factual. There are ActiveState recipes out there for this sort of thing since the beginning of time... Also, check my other comments for thoughts on syntax and immutability.
"Neat, I did something similar here: [link]" communicates the same information. If it's not newsworthy it'll fall off the front page and that'll be that.
I'm sure you didn't mean to be hurtful, but `Show HN` posts almost always end up with a broad, critical top comment. Constructive criticism is great but "this is trivial" just seems discouraging.
There are plenty of similar implementations around, in fact, and this is the kind of thing I've also seen as part of ActiveState recipes.
It's dangerous to use because it needs recursive collapsing to a regular dict otherwise it's not safe to pass to many APIs. So yeah, it should definitely not exist in the standard library.
The syntactic sugar is very nice and saves up on oodles of brackets and quotes for nested objects, but if you try to mutate this sort of object you'll eventually run into trouble.
Items and attributes are different, and failing to distinguish can make your code fragile - for example, it looks like Dict().get will return the attribute, unlike Dict()['get']. And if a new python version adds a new method to dict, some of your uses of Dict() will now break.
But item access is ugly when dealing with nested JSON or any number of other things, and that leads to people like me using terrible hacks that ambiguate between the two.
just use `mydict.setdefault()` and `defaultdict` and give up on the whole attr access thing. it's a dict