1

Given the simple example below, a few strange observations are made:

  1. The Parameter.__set__ function is never called
  2. At the line self.color = Parameter() the inspector shows color as being of type Parameter
  3. At the conclusion of the program, f.color is of type str, not even aware of once being a Parameter

Why is the descriptor __set__ method not called when color is being assigned to? I'd expect it to be called for both the 'red' and 'blue' assignments.

class Parameter(object):
    def __init__(self, value=None):
        self.value = value

    def __set__(self, instance, value):
        print('in __set__')
        self.value = value

    def __get__(self, instance, owner):
        return self.value

    def __delete__(self, instance):
        pass


class Fruit(object):
    def __init__(self):
        self.color = Parameter()


class Apple(Fruit):
    def __init__(self):
        super().__init__()
        self.color = 'red'

f = Apple()
f.color = 'blue'
print(f.color)
rob
  • 116
  • 1
  • 6
  • 4
    Descriptors don't work in the instance dict. – user2357112 Apr 14 '20 at 23:33
  • 1
    The subclass has nothing to do with the problem - you'd see the same effect with an instance of `Fruit`. – user2357112 Apr 14 '20 at 23:39
  • Looks like the solution was to simply perform the initial assignment/creation as a class variable in the base class. Will post the answer shortly. Thanks. – rob Apr 14 '20 at 23:47

1 Answers1

1

Turns out the answer had nothing to do with inheritance but rather with how descriptors work using the instance dictionary. I found a great article (https://realpython.com/python-descriptors/) that goes over the details of descriptors and showed how to properly design the pattern I was shooting for.

note: this only works with Python 3.6+ due to using the __set_name__() method.

class Parameter(object):

    def __set_name__(self, owner, name):
        self.name = name

    def __set__(self, instance, value) -> None:
        print('in __set__')
        instance.__dict__[self.name] = value

    def __get__(self, instance, obj_type=None) -> object:
        return instance.__dict__.get(self.name) or 0


class Fruit(object):
    color = Parameter()


class Apple(Fruit):
    def __init__(self):
        super().__init__()
        self.color = 'red'


f = Apple()
f2 = Apple()

print(f.color)
f.color = 'blue'
print(f.color)
print(f2.color)

Output

in __set__
in __set__
red
in __set__
blue
red
rob
  • 116
  • 1
  • 6
  • 1
    Your descriptor is storing values on itself, so every fruit will share a single color. That's probably not what you want. – user2357112 Apr 14 '20 at 23:52
  • 1
    Updated with an implementation that is instance based. Thanks so much for the eyes! – rob Apr 15 '20 at 00:54