扩展使用 __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
我正在尝试创建我自己的 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