Skip to content

Instantly share code, notes, and snippets.

@Susensio
Last active April 19, 2024 00:42
Show Gist options
  • Star 17 You must be signed in to star a gist
  • Fork 3 You must be signed in to fork a gist
  • Save Susensio/979259559e2bebcd0273f1a95d7c1e79 to your computer and use it in GitHub Desktop.
Save Susensio/979259559e2bebcd0273f1a95d7c1e79 to your computer and use it in GitHub Desktop.
Inherit property setter in python 3.7

Python @property inheritance the right way

Given a Parent class with value property, Child can inherit and overload the property while accessing Parent property getter and setter.

Although we could just reimplement the Child.value property logic completely without using Parent.value whatsover, this would violate the DRY principle and, more important, it wouldn't allow for proper multiple inheritance (as show in the example property_inheritance.py bellow).

Two options:

  • Child redefines value property completely, both getter and setter.

  • Child uses Parent.value property, and only overloads setter. Parent class must be referenced explicitly.

In either pattern, using Parent.value.getter is as simple as

return super().value

However, accessing Parent.property.setter is more verbose:

super(Child, type(self)).value.fset(self, new_value)

It would be nice to just super().value = new_value but this won't work due to super current implementation (as Python 3.7). There is a Python Issue regarding this rather unintuitive behaviour.

class Number:
"""
>>> n = Number(-4.1)
>>> print(n.value)
-4.1
"""
def __init__(self, value):
"""Uses property setter."""
self.value = value
@property
def value(self):
return self._value
@value.setter
def value(self, new_value):
self._value = new_value
class Integer(Number):
"""
>>> i = Integer(-4.1)
>>> print(i.value)
-4
"""
@property
def value(self):
return super().value
@value.setter
def value(self, new_value):
_value = int(new_value)
super(Integer, type(self)).value.fset(self, _value)
class Positive(Number):
"""
>>> p = Positive(-4.1)
>>> print(p.value)
4.1
"""
@Number.value.setter
def value(self, new_value):
_value = abs(new_value)
super(Positive, type(self)).value.fset(self, _value)
class Natural(Integer, Positive):
""" With multiple inheritance, it works as expected.
>>> n = Natural(-4.1)
>>> print(n.value)
4
"""
if __name__ == '__main__':
from doctest import testmod
testmod()
@gitPrinz
Copy link

PyCharm will tell me "Cannot find reference 'fset' in 'None | str'", but it works!
Thank you very much 👍

@TogzhanK
Copy link

TogzhanK commented Jan 9, 2021

Thank you so much! Usually, during tutorials only super() implemented on int and str are shown which doesn't explain syntax for other methods. This really helped me!

@willrazen
Copy link

super(type(self), type(self)).setter.fset(self, value) doesn't follow mro adequately (because of the second type(self)).
Check my solution: https://gist.github.com/willrazen/bef3fcb26a83dffb6692e5e10d3e67ac
Usage: duper(super()).setter = value

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment