扩展使用 __getattr__ (pygame.Rect) 的 class

extending a class that uses __getattr__ (pygame.Rect)

我正在尝试创建我自己的 pygame.Rect 矩形版本,但增加了一个功能,即当一个正方形超出某些世界边界时,它会出现在另一边。

这意味着我必须在我的扩展中重写 pygame.Rect 的很多功能,我成功​​了。这里没问题。

当我尝试更改 __getattr__ 和 __setattr__ 时,问题就开始了。 pygame.Rect 大量使用这些函数,因此例如询问 'top' 或 'bottom' 分别指的是 'y' 和 'y'+'width'。我已经更改了这些功能以适应我的功能,但我还需要做一件事: 在 __init__ 函数中,我需要创建 worldwidth 和 worldheight 变量。但我不能,因为 __setattr__ 函数不允许它。

这是我现在拥有的:

class Rectinworld(pygame.Rect):
    '''hides the fact that the world is round, when it comes to collisions and such'''

    __slots__ = ['_r']

    def __init__(self,(worldwidth,worldheight),*args):
        object.__setattr__(self, "worldwidth",worldwidth)
        if len(args)==0:
            super(Rectinworld, self).__init__((0,0,0,0))
        else:
            super(Rectinworld, self).__init__(*args)

def __getattr__(self, name):
    if name == 'top':
        return self._r.y
    #etc etc etc
    elif name == 'height':
        return self._r.h
    else:
        raise AttributeError(name)

def __setattr__(self, name, value):
    if name == 'top' or name == 'y':
        self._r.y = value % worldheight
    #etc etc etc
    elif name == 'height':
        if int(value) < 0:
            self._ensure_proxy()
        self._r.h = int(value)
    else:
        raise AttributeError(name)

为了清楚起见,我在 #etc etc etc 注释中遗漏了一些代码。 pygame.Rect 的代码类似:__setattr__ 和 __getattr__ 不引用 worldwidth 或 worldheight,但其他方面相同,pygame.Rect.__init__ 函数是非常长,但我认为以下片段涵盖了最重要的内容:

def __init__(self, *args):
#etc etc etc
if len(args) == 4:
            if args[2] < 0 or args[3] < 0:
                object.__setattr__(self, '_r', _RectProxy((int(args[0]),
                                                          int(args[1]),
                                                          int(args[2]),
int(args[3]))))

完整代码见https://github.com/brython-dev/brython-pygame/blob/master/pygame/rect.py

我现在得到的错误是:

line 10, in __init__
    object.__setattr__(self, "worldwidth",0)
AttributeError: 'Rectinworld' object has no attribute 'worldwidth'

一个明显的修复似乎是将 worldwidth 和 worldheight 添加到 __slots__ 然后从那里开始。这给出了更奇怪的错误。尝试设置任何变量时,它会给出以下消息:

line 65, in __getattr__
    raise AttributeError(name)
AttributeError: _r

简而言之,我的问题归结为:为什么我不能创建新变量,我需要做什么才能创建新变量?

我不确定它是否正确,但它对我有用(在 Python 2 和 3 上)
它会根据需要使用 slots

import pygame

class Rectinworld(object):

    '''hides the fact that the world is round, when it comes to collisions and such'''

    __slots__ = ['_r', '_worldwidth', '_worldheight']

    def __init__(self, worldwidth, worldheight, *args):
        if not args:
            args = (0,0,0,0)

        super(Rectinworld, self).__setattr__("_r", pygame.Rect(args))
        super(Rectinworld, self).__setattr__("_worldwidth", worldwidth)
        super(Rectinworld, self).__setattr__("_worldheight", worldheight)

    def __getattr__(self, name):
        if name == 'top':
            return self._r.y
        #etc etc etc
        elif name == 'height':
            return self._r.h
        else:
            raise AttributeError(name)

    def __setattr__(self, name, value):
        if name == 'top' or name == 'y':
            self._r.y = value % self._worldwidth
        #etc etc etc
        elif name == 'height':
            if int(value) < 0:
                self._ensure_proxy()
            self._r.h = int(value)
        else:
            raise AttributeError(name)

# --- test ---

r = Rectinworld(100, 100)
r.top = 120
print(r.top) # gives 20