mrdunk
mrdunk

Reputation: 102

Strange interaction between __setitem__ and __get__

Can anyone explain the following strangeness?

This works as expected:

duncan@duncan:~/Working/wtf$ python -V
Python 2.7.3

duncan@duncan:~/Working/wtf$ cat test_without_get.py 
class B(object):
    def __setitem__(self, key, value):
        print "__setitem__"

class A(object):
    b = B()

a = A()
a.b
a.b[0] = 1

duncan@duncan:~/Working/wtf$ python test_without_get.py 
__setitem__

But here __setitem__ breaks and appears to call __get__ in it's place:

duncan@duncan:~/Working/wtf$ cat test_with_get.py 
class B(object):
    def __setitem__(self, key, value):
        print "__setitem__"
    def __get__(self, instance, owner):
        print "__get__"

class A(object):
    b = B()

a = A()
a.b
a.b[0] = 1

duncan@duncan:~/Working/wtf$ python test_with_get.py
__get__
__get__
Traceback (most recent call last):
  File "test_with_get.py", line 12, in <module>
    a.b[0] = 1
TypeError: 'NoneType' object does not support item assignment

does any one have any insight int why this happens and how to work around it?


Further experimentation:

as suggested by ignacio-vazquez-abrams@ , if __get__ returns an object, things work as expected:

duncan@dunksbox:~/Working/wtf$ python -V
Python 2.7.3
duncan@dunksbox:~/Working/wtf$ cat test_with_get_working.py 
class B(object):
    def __setitem__(self, key, value):
        print "__setitem__"

    def __get__(self, instance, owner):
        print "__get__"
        return self

class A(object):
    b = B()

a = A()
a.b
a.b[0] = 1

duncan@dunksbox:~/Working/wtf$ python ./test_with_get_working.py 
__get__
__get__
__setitem__

but only if you are careful about what __get__ returns:

duncan@dunksbox:~/Working/wtf$ cat test_with_get_notworking.py 
class B(object):
    def __setitem__(self, key, value):
        print "__setitem__"

    def __get__(self, instance, owner):
        print "__get__"
        return [1]

class A(object):
    b = B()

a = A()
a.b
a.b[0] = 1

duncan@dunksbox:~/Working/wtf$ python ./test_with_get_notworking.py 
__get__
__get__

Upvotes: 0

Views: 53

Answers (1)

Ignacio Vazquez-Abrams
Ignacio Vazquez-Abrams

Reputation: 798744

It happens because you've created B as a descriptor since it defines one of the descriptor protocol methods. The only ways around it are to either not create it as a descriptor in the first place, or to return a separate object from the descriptor whose __setitem__() method behaves as desired.

Upvotes: 1

Related Questions