Search code examples
pythongetsetdescriptor

Python descriptors __get__ and __set__


I am sure this will be marked as a duplicate, but I truly don't understand what I'm looking at. I've checked The python documentation on Descriptors, but I have been "programming" in Python for two weeks now and I don't really know what I'm looking for!

This is what I got:

>>> class Blub(object):
...     def __init__(self, value):
...             print('Blub is ' + value)
...             self.value = value
...     def __get__(self):
...             print('Blub gets ' + self.value)
...             return self.value
...     def __set__(self, value):
...             print('Blub becomes ' + value)
...             self.value = value
...
>>> class Quish(object):
...     def __init__(self, value):
...             self.blub = Blub(value)
...     def __get__(self):
...             return self.blub
...     def __set__(self, value):
...             self.blub = Blub(value)
... 

The following is what I want to happen and don't know how to do:

>>> a = Quish('One')
Blub is One
>>> a.blub
Blub gets One
'One'
a.blub = 'Two'
Blub becomes Two

What do I do in Blub or Quish in order for this to happen. I have really simple classes here, but I have a much more intricate version which works perfectly but only if I type:

>>> a.blub.__get__()

I thought that the point of these descriptors was to make it unnecessary to actually write get() and set(). How do I make it behave like I would like, or can Python not do that?


Solution

  • Normally you'd not use descriptors directly, but use property, which is an implementation of a descriptor in an easy-to-use way. Here's how you'd use it:

    class Quish(object):
        def __init__(self, value):
            self.blub = value
    
        @property
        def blub(self):
            print('Blub gets ' + self._blub)
            return self._blub
    
        @blub.setter
        def blub(self, value):
            print('Blub becomes ' + value)
            self._blub = value
    

    If you really want to write your own descriptor, your problem is that it needs to be set directly on the type, not as another instance attribute, and you'll need to deal with having one descriptor multiple instances:

    class Blub(object):
        def __get__(self, instance, owner):
            print('Blub gets ' + instance._blub)
            return instance._blub
    
        def __set__(self, instance, value):
            print('Blub becomes ' + value)
            instance._blub = value
    
    
    class Quish(object):
        blub = Blub()
    
        def __init__(self, value):
            self.blub = value