27

我在 Python super() 以及继承和属性方面遇到了一个非常奇怪的问题。首先,代码:

#!/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

这工作正常。但是,我想要什么(对我来说似乎是 Pythonic)

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

即使不起作用

super().x

得到很好的价值。在这种情况下,x 是定义了 fset 和 fget 的超类的属性。那么为什么它不起作用呢?

4

2 回答 2

22

我试图找到正确的语言来支持为什么这种行为是这样的,以免给你一个“因为它只是”的答案......但似乎这个问题已经被问过不止一次了,并且它归结为 的行为super()。您可以在此处查看有关此确切行为的 2010 年讨论:http: //mail.python.org/pipermail/python-dev/2010-April/099672.html

最终,它实际上只是归结为 super() 调用,只允许您直接访问 getter,而不是 setter。设置器必须通过fset()或访问__set__()。可能最简单的解释是“super() 功能不支持它”。它将在“set”操作中解析“get”操作的属性功能,而不是左手赋值中的setter(因此是fset()方法调用)。从这个讨论线程的日期可以看出,自从引入super().

也许其他人有更具体的技术原因,但坦率地说,我不确定这是否重要。如果它不支持,那几乎是一个足够好的理由。

于 2012-05-30T05:38:33.053 回答
0

super(type(self), type(self)).setter.fset(self, value)是一种常见的解决方法;但是它不能与多重继承充分合作,这可能会改变 MRO(方法解析顺序)。

尝试使用我的 duper 类: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)

(完整来源https://gist.github.com/willrazen/bef3fcb26a83dffb6692e5e10d3e67ac

于 2021-05-21T02:06:07.937 回答