using __getitem()__ correctly

Ian Kelly ian.g.kelly at gmail.com
Wed Dec 30 19:31:11 EST 2015


On Wed, Dec 30, 2015 at 3:54 PM, Charles T. Smith
<cts.private.yahoo at gmail.com> wrote:
> On Wed, 30 Dec 2015 13:40:44 -0700, Ian Kelly wrote:
>
>> On Wed, Dec 30, 2015 at 9:58 AM, Charles T. Smith
>>> The problem is that then triggers the __getitem__() method and I don't
>>> know how to get to the attributes without triggering __getattr__().
>>>
>>> It's the interplay of the two that's killing me.
>>
>> The only interplay of the two is what you have written into your class.
>>
>>> In the example, if I have:
>>>
>>>   self.mcc = self.attrs.mcc
>>>
>>>
>>> The crux:
>>>
>>> Then __getattr__() triggers for the mcc.  If I try to use
>>> self.attrs['mcc'] to get it, then that triggers __getitem__().  Okay,
>>> if the key is not an int, I'll go and get it and return it...
>>> unfortunately that triggers __getattr__(), an infinite loop.
>>
>> How precisely are you trying to store these: as an attribute, or as a
>> dict item? If it's supposed to be in the dict, then why is your
>> __getitem__ trying to look up an attribute to begin with?
>
>
>
> I don't understand this distinction between an "attribute" and a "dict item".
> attrdict is a stupid legacy class that inherits from a dictionary.  I think
> the intent was to be faster than a normal class, but I doubt there's any value
> to that.
>
> In any case, I thought that class attributes were, in fact, items of __dict__?

That's correct, but as I said in my previous message, self.attrs and
self.attrs.__dict__ are two different dicts, and you're confusing one
for the other. Maybe this will be illuminating:

>>> class mydict(dict): pass
...
>>> md = mydict()
>>> md['foo'] = 42  # Set an item in md
>>> md['foo']  # 'foo' exists as an item
42
>>> md.foo  # but not as an attribute
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
AttributeError: 'mydict' object has no attribute 'foo'
>>> md.__dict__['foo']  # and it's not in md.__dict__
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
KeyError: 'foo'
>>> md.bar = 43  # Set an attribute on md
>>> md.bar  # 'bar' exists as an attribute
43
>>> md.__dict__['bar']  # and it's in md.__dict__
43
>>> md['bar']  # but it's not in md itself
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
KeyError: 'bar'

And to hopefully drive the point home:

>>> md.items()
[('foo', 42)]
>>> md.__dict__.items()
[('bar', 43)]



> The reason that my __getitem__() is trying to look up an attribute is, I
> think, because this syntax triggers __getitem__ with a key of "mcc":
>
>   return self[name]
>
> Is that assumption wrong?

That assumption is correct, but this still doesn't explain to me why
you want __getitem__ to be looking up attributes, i.e. looking in
self.__dict__ when the expectation is that it would just look in self.

Is the goal here that self.foo and self['foo'] are the same thing? If
so, then you shouldn't need to worry about __getitem__ and __setitem__
at all. Just override your __getattr__ and __setattr__ to store
attributes in self instead of self.__dict__.

> Just to establish the concept, this horrible thing is showing some promise:
>
>   attriter = self.iteritems()
>   for attr in attriter:
>       if attr[0] == name:
>           return attr[1]

This is equivalent to but slower than "return self.get(name)". What
method is this in and what's the rest of the code? It's hard to
analyze your code in any helpful way when you keep changing it.

> (because the subscripts there are on a tuple type)

I don't know what this has to do with anything.

> But I concede I must be doing something fundamentally wrong because this
> assert is triggering:
>     def __getattr__ (self, name):
>         print "attrdict:av:__getattr__: entered for ", name
>         assert name not in self.keys(), "attrdict:__getattr__: who lied?"

When does this trigger? What's calling it? What name is passed in?



More information about the Python-list mailing list