Javascript style dot notation for dictionary keys

2019-01-30 15:54发布

I've started to use constructs like these:

class DictObj(object):
    def __init__(self):
        self.d = {}
    def __getattr__(self, m):
        return self.d.get(m, None)
    def __setattr__(self, m, v):
        super.__setattr__(self, m, v)

Update: based on this thread, I've revised the DictObj implementation to:

class dotdict(dict):
    def __getattr__(self, attr):
        return self.get(attr, None)
    __setattr__= dict.__setitem__
    __delattr__= dict.__delitem__

class AutoEnum(object):
    def __init__(self):
        self.counter = 0
        self.d = {}
    def __getattr__(self, c):
        if c not in self.d:
            self.d[c] = self.counter
            self.counter += 1        
        return self.d[c]

where DictObj is a dictionary that can be accessed via dot notation:

d = DictObj()
d.something = 'one'

I find it more aesthetically pleasing than d['something']. Note that accessing an undefined key returns None instead of raising an exception, which is also nice.

Update: Smashery makes a good point, which mhawke expands on for an easier solution. I'm wondering if there are any undesirable side effects of using dict instead of defining a new dictionary; if not, I like mhawke's solution a lot.

AutoEnum is an auto-incrementing Enum, used like this:

CMD = AutoEnum()

cmds = {
    "peek":  CMD.PEEK,
    "look":  CMD.PEEK,
    "help":  CMD.HELP,
    "poke":  CMD.POKE,
    "modify": CMD.POKE,
}

Both are working well for me, but I'm feeling unpythonic about them.

Are these in fact bad constructs?

11条回答
Root(大扎)
2楼-- · 2019-01-30 15:59

With regards to the DictObj, would the following work for you? A blank class will allow you to arbitrarily add to or replace stuff in a container object.

class Container(object):
    pass

>>> myContainer = Container()
>>> myContainer.spam = "in a can"
>>> myContainer.eggs = "in a shell"

If you want to not throw an AttributeError when there is no attribute, what do you think about the following? Personally, I'd prefer to use a dict for clarity, or to use a try/except clause.

class QuietContainer(object):
    def __getattr__(self, attribute):
        try:
            return object.__getattr__(self,attribute)
        except AttributeError:
            return None

>>> cont = QuietContainer()
>>> print cont.me
None

Right?

查看更多
劳资没心,怎么记你
3楼-- · 2019-01-30 16:02

The one major disadvantage of using something like your DictObj is you either have to limit allowable keys or you can't have methods on your DictObj such as .keys(), .values(), .items(), etc.

查看更多
爷、活的狠高调
4楼-- · 2019-01-30 16:04

It's not "wrong" to do this, and it can be nicer if your dictionaries have a strong possibility of turning into objects at some point, but be wary of the reasons for having bracket access in the first place:

  1. Dot access can't use keywords as keys.
  2. Dot access has to use Python-identifier-valid characters in the keys.
  3. Dictionaries can hold any hashable element -- not just strings.

Also keep in mind you can always make your objects access like dictionaries if you decide to switch to objects later on.

For a case like this I would default to the "readability counts" mantra: presumably other Python programmers will be reading your code and they probably won't be expecting dictionary/object hybrids everywhere. If it's a good design decision for a particular situation, use it, but I wouldn't use it without necessity to do so.

查看更多
Luminary・发光体
5楼-- · 2019-01-30 16:04

Don't overlook Bunch.

It is a child of dictionary and can import YAML or JSON, or convert any existing dictionary to a Bunch and vice-versa. Once "bunchify"'d, a dictionary gains dot notations without losing any other dictionary methods.

查看更多
霸刀☆藐视天下
6楼-- · 2019-01-30 16:06

There's a symmetry between this and this answer:

class dotdict(dict):
    __getattr__= dict.__getitem__
    __setattr__= dict.__setitem__
    __delattr__= dict.__delitem__

The same interface, just implemented the other way round...

class container(object):
    __getitem__ = object.__getattribute__
    __setitem__ = object.__setattr__
    __delitem__ = object.__delattr__
查看更多
虎瘦雄心在
7楼-- · 2019-01-30 16:07

It's not bad if it serves your purpose. "Practicality beats purity".

I saw such approach elserwhere (eg. in Paver), so this can be considered common need (or desire).

查看更多
登录 后发表回答