使用Python元组作为向量
我需要在Python中表示不可变的向量(这里的“向量”是指线性代数中的向量,不是编程中的向量)。元组看起来是个不错的选择。
问题是,当我需要实现像加法和数乘这样的操作时。如果a
和b
是向量,而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这样的库似乎是解决问题的好办法,但我认为还有一种简单的解决方案,它不需要额外的库,并且可以保持不变,使用可迭代对象。
可以使用itertools
和operators
这两个模块:
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 可以对它的数组进行各种代数运算。