0

I'm using a Javascript-style dictionary as discussed here. Implementation:

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

I've used this structure for some time with no issues, but recently needed a dictionary with a hyphenated key, like this:

    foo = DotDict()
    foo.a = 'a'        #Business as usual
    foo.a-b = 'ab'     #Broken

Assigning to foo.a-b results in:

SyntaxError: can't assign to operator

This breaks because the '-' is seen as a minus operation and not as part of the key name. Is there another way to create a dictionary with dot-style member access?

smci
  • 32,567
  • 20
  • 113
  • 146
knite
  • 6,033
  • 6
  • 38
  • 54
  • 1
    it's possible to do it with setattr/getattr hacks, but you don't want to do it, and people that will read your code don't want you to do it neither. (and of course you won't get dot style member access) – zmo May 21 '12 at 10:43
  • 1
    -1 for this not being valid injavascript either. – jsbueno May 21 '12 at 14:35

6 Answers6

6

a-b is not a valid identifier in Python. Valid identifiers can consist of letters, numbers and underscores (_). Any other character is not legal in variable name.

Is there another way to create a dictionary with dot-style member access?

There is no way of having dot-style access with keys that are not valid identifiers.

vartec
  • 131,205
  • 36
  • 218
  • 244
  • it will be seen to python's interpreter as `((foo.a)-(b)) = ('ab')` which is illegal, because an expression can't be a lvalue. – zmo May 21 '12 at 10:40
5

I'm not sure why you are surprised. In JavaScript, the same expression also throws an error:

>>> foo.a-b = 'ab'
ReferenceError: invalid assignment left-hand side

So, just as in JavaScript, fall back to:

>>> foo['a-b'] = 'ab'
Dzinx
  • 55,586
  • 10
  • 60
  • 78
2

That is not possible in a direct way. Best way would be to allow for mixed access - as you already do - and use it. Or, alternatively, use setattr()/getattr(), as Not_a_Golfer suggests.

Community
  • 1
  • 1
glglgl
  • 89,107
  • 13
  • 149
  • 217
1

well, it's sorta possible, but you really don't want to be doing that.

>>> class foo():
...    pass
... 
>>> f = foo()
>>> setattr(f, 'foo-bar', 1)
>>> getattr(f, 'foo-bar')
1
Not_a_Golfer
  • 47,012
  • 14
  • 126
  • 92
1

a-b is not a valid identifier but is interpreted as an expression using the operator -. You can set such fields though: setattr(foo, 'a-b', 'ab')

Alfe
  • 56,346
  • 20
  • 107
  • 159
-1

It may not fit the idiom you're trying to achieve, but if you were to replace the hyphen with an underscore, the code would work:

foo.a_b = 'ab'
LAK
  • 961
  • 7
  • 18