Kivy官方乒乓球教程 - Vector(kivy.vector)的使用

6 投票
2 回答
1465 浏览
提问于 2025-04-18 17:35

我一直在跟着官方的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 个回答

7

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.ObservableListlist 的一个子类。

不幸的是,kivy.vector.Vector 也是如此,任何有 Python 经验的人都知道,list.__add__ 是用来连接它的参数的。

这意味着,向 self.pos 添加向量时,是通过 扩展 的方式,而不是逐个元素相加,这就导致 self.pos 报错,因为它的长度在变化。

反过来,Vector 重载了 __add__ 方法,使其可以进行逐个元素相加。

由于 Python 更倾向于使用 __add__ 而不是 __radd__,所以整个过程就失败了。

5

我觉得这个问题主要是因为Vector类型重写了加法运算,让它可以进行向量加法。在第一个情况下,它调用了__add__方法,这个方法会自动把self.pos(一个列表)当作另一个向量来处理。

而在第二个情况下,调用的是self.pos的__add__方法,这个方法并不知道Vector类型的存在,所以它试图进行普通的列表加法,这样会导致列表的长度增加。这样做会出错,因为pos必须是一个固定长度的列表。

所以总体来说(如果我没理解错的话),问题在于+的行为会根据它所处理的对象类型而不同。通常这不是个大问题,但在这里却造成了很大的影响。

撰写回答