Kivy官方乒乓球教程 - Vector(kivy.vector)的使用
我一直在跟着官方的Kivy PongApp教程(链接 - 网站底部有完整的程序代码),但是遇到了一个我不太明白的问题。
我定义了一个移动函数,用来在每一帧中通过一个速度向量来改变球的位置。代码如下:
def move(self):
self.pos = Vector(*self.velocity) + self.pos
但是,当我这样写代码的时候:
def move(self):
self.pos = self.pos + Vector(*self.velocity)
就出现了一个错误:
ValueError: PongBall.pos 的值长度是不可变的
为什么呢?这不应该是一样的吗?
2 个回答
self.pos
是一个 kivy.properties.ObservableReferenceList
。
当你试图设置这个属性时,它会检查新值的长度是否和旧值相同。
来自 kivy.properties.ReferenceProperty
的内容:
cdef check(self, EventDispatcher obj, value):
cdef PropertyStorage ps = obj.__storage[self._name]
if len(value) != len(ps.properties):
raise ValueError('%s.%s value length is immutable' % (
obj.__class__.__name__, self.name))
另外,kivy.properties.ObservableList
是 list
的一个子类。
不幸的是,kivy.vector.Vector
也是如此,任何有 Python 经验的人都知道,list.__add__
是用来连接它的参数的。
这意味着,向 self.pos
添加向量时,是通过 扩展 的方式,而不是逐个元素相加,这就导致 self.pos
报错,因为它的长度在变化。
反过来,Vector
重载了 __add__
方法,使其可以进行逐个元素相加。
由于 Python 更倾向于使用 __add__
而不是 __radd__
,所以整个过程就失败了。
我觉得这个问题主要是因为Vector类型重写了加法运算,让它可以进行向量加法。在第一个情况下,它调用了__add__
方法,这个方法会自动把self.pos(一个列表)当作另一个向量来处理。
而在第二个情况下,调用的是self.pos的__add__
方法,这个方法并不知道Vector类型的存在,所以它试图进行普通的列表加法,这样会导致列表的长度增加。这样做会出错,因为pos
必须是一个固定长度的列表。
所以总体来说(如果我没理解错的话),问题在于+
的行为会根据它所处理的对象类型而不同。通常这不是个大问题,但在这里却造成了很大的影响。