rob
rob

Reputation: 116

Using Python Descriptors in Inherited Classes

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)

Upvotes: 1

Views: 244

Answers (1)

rob
rob

Reputation: 116

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

Upvotes: 1

Related Questions