'Python super and setting parent class property

I'm having a really strange problem with Python super() and inheritance and properties. First, the code:

#!/usr/bin/env python3

import pyglet
import pygame

class Sprite(pyglet.sprite.Sprite):

    def __init__(self, *args, **kwargs):
        super().__init__(*args, **kwargs)
        self.rect = pygame.Rect(0, 0, self.width, self.height)
        self.rect.center = self.x, self.y

    @property
    def x(self):
        return super().x

    @x.setter
    def x(self, value):
        super(Sprite, self.__class__).x.fset(self, value)
        self.rect.centerx = value

    @property
    def y(self):
        return super().y

    @y.setter
    def y(self, value):
        super(Sprite, self.__class__).y.fset(self, value)
        self.rect.centery = value

This works fine. However, what I want (what seems Pythonic to me)

#super(Sprite, self.__class__).x.fset(self, value)
super().x = value

doesn't work even though

super().x

gets the value fine. x in this case is a property of the superclass with both fset and fget defined. So why doesn't it work?



Solution 1:[1]

super(type(self), type(self)).setter.fset(self, value) is a common workaround; however it doesn't work adequately with multiple inheritance, which can change the MRO (Method Resolution Order).

Try using my duper class: duper(super()).setter = value

class duper:
    """Super wrapper which allows property setting & deletion.
    Super can't be subclassed with empty __init__ arguments.
    Works with multiple inheritance.
    
    References:
      https://mail.python.org/pipermail/python-dev/2010-April/099672.html
      https://bugs.python.org/issue14965
      https://bugs.python.org/file37546/superprop.py
    
    Usage: duper(super())
    """

    def __init__(self, osuper):
        object.__setattr__(self, 'osuper', osuper)

    def _find(self, name):
        osuper = object.__getattribute__(self, 'osuper')
        if name != '__class__':
            mro = iter(osuper.__self_class__.__mro__)
            for cls in mro:
                if cls == osuper.__thisclass__:
                    break
            for cls in mro:
                if isinstance(cls, type):
                    try:
                        return object.__getattribute__(cls, name)
                    except AttributeError:
                        pass
        return None
    
    def __getattr__(self, name):
        return getattr(object.__getattribute__(self, 'osuper'), name)

    def __setattr__(self, name, value):
        osuper = object.__getattribute__(self, 'osuper')
        desc = object.__getattribute__(self, '_find')(name)
        if hasattr(desc, '__set__'):
            return desc.__set__(osuper.__self__, value)
        return setattr(osuper, name, value)

    def __delattr__(self, name):
        osuper = object.__getattribute__(self, 'osuper')
        desc = object.__getattribute__(self, '_find')(name)
        if hasattr(desc, '__delete__'):
            return desc.__delete__(osuper.__self__)
        return delattr(osuper, name)

(full source https://gist.github.com/willrazen/bef3fcb26a83dffb6692e5e10d3e67ac)

Sources

This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.

Source: Stack Overflow

Solution Source
Solution 1 SuperStormer