使用Python元组作为向量

6 投票
7 回答
17353 浏览
提问于 2025-04-15 21:14

我需要在Python中表示不可变的向量(这里的“向量”是指线性代数中的向量,不是编程中的向量)。元组看起来是个不错的选择。

问题是,当我需要实现像加法和数乘这样的操作时。如果ab是向量,而c是一个数字,我能想到的最好的方法是这样:

tuple(map(lambda x,y: x + y, a, b)) # add vectors 'a' and 'b'
tuple(map(lambda x: x * c, a))      # multiply vector 'a' by scalar 'c'

但这样看起来不太优雅;应该有更清晰、更简单的方法来完成这个操作——更不用说还要避免调用tuple,因为map返回的是一个列表。

有没有更好的选择呢?

7 个回答

9

虽然使用像NumPy这样的库似乎是解决问题的好办法,但我认为还有一种简单的解决方案,它不需要额外的库,并且可以保持不变,使用可迭代对象。

可以使用itertoolsoperators这两个模块:

imap(add, a, b) # returns iterable to sum of a and b vectors

这个实现很简单。它不使用lambda表达式,也不进行任何列表或元组的转换,因为它是基于迭代器的。

from itertools import imap
from operator import add
vec1 = (1, 2, 3)
vec2 = (10, 20, 30)
result = imap(add, vec1, vec2)
print(tuple(result))

输出:

(11, 22, 33)
11

在Python和它的第三方扩展中,不可变类型其实挺少见的。提问者说得对,“线性代数的用处很多,我不太可能自己去做一个”,但我知道的所有线性代数相关的类型都是可变的!所以,如果提问者坚持要不可变的类型,那就只能自己动手去做了。

不过,自己做也没那么复杂,比如说如果你特别需要二维向量:

import math
class ImmutableVector(object):
    __slots__ = ('_d',)
    def __init__(self, x, y):
        object.__setattr__(self, _d, (x, y))
    def __setattr__(self, n, v):
        raise ValueError("Can't alter instance of %s" % type(self))
    @property
    def x(self): 
        return self._d[0]
    @property
    def y(self):
        return self._d[1]
    def __eq__(self, other):
        return self._d == other._d
    def __ne__(self, other):
        return self._d != other._d
    def __hash__(self):
        return hash(self._d)
    def __add__(self, other):
        return type(self)(self.x+other.x, self.y+other.y)
    def __mul__(self, scalar):
        return type(self)(self.x*scalar, self.y*scalar)
    def __repr__(self):
        return '%s(%s, %s)' % (type(self).__name__, self.x, self.y)
    def __abs__(self):
        return math.hypot(self.x, self.y)

我“顺便加了一些”额外的功能,比如 .x.y 这两个只读属性,还有好看的字符串表示法,可以在集合中使用或者作为字典的键(不然为什么要不可变呢?),内存占用也低,abs(v) 可以计算出 v 的向量长度——我相信你可以根据自己的应用领域想出其他“如果能有就好了”的方法和操作符,它们也会很简单。如果你需要其他维度的向量,做起来也不会太难,只是可读性稍差,因为 .x.y 的写法就不适用了;-)(不过我建议用生成表达式,而不是 map)。

11

NumPy 可以对它的数组进行各种代数运算。

撰写回答