使用Python元组作为向量

时间:2010-04-04 23:39:44

标签: python tuples

我需要在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 个答案:

答案 0 :(得分:10)

不可变类型在Python及其第三方扩展中非常罕见; OP正确地声称“线性代数有足够的用途,我似乎不太可能自己动手” - 但我知道线性代数的所有现有类型都是可变!因此,由于OP坚持不变性, 没有任何东西可用,但是滚动你自己的路线。

并非所有涉及的内容都很多,例如:如果你特别需要2-d向量:

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 R / O属性,漂亮的字符串表示,集合中的可用性或作为键的键(为什么还有人想要不变性) ? - ),内存占用率低,abs(v)给出v的矢量长度 - 我相信你可以想到其他“不会很酷的方法”和操作员,取决于您的应用领域,他们将同样容易。如果你需要其他的维度,那就不会那么难了,虽然因为.x.y符号不再适用,因此可读性稍差;-)(但我会使用genexps,而不是map)。

答案 1 :(得分:9)

NumPy支持其数组的各种代数运算。

答案 2 :(得分:8)

通过继承元组,你可以很容易地创建一个漂亮的Vector类。这里有足够的代码来提供向量的添加,以及通过标量乘以向量。它为您提供任意长度向量,并且可以处理复数,整数或浮点数。

class Vector(tuple):
    def __add__(self, a):
        # TODO: check lengths are compatable.
        return Vector(x + y for x, y in zip(self, a))
    def __mul__(self, c):
        return Vector(x * c for x in self)
    def __rmul__(self, c):
        return Vector(c * x for x in self)

a = Vector((1, 2, 3))
b = Vector((2, 3, 4))

print a + b
print 3 * a
print a * 3

答案 3 :(得分:5)

尽管使用像NumPy这样的库似乎是OP的解决方案,但我认为在一个简单的解决方案中仍然有一些价值,它不需要额外的库,并且你可以使用iterables保持不可变。

使用itertoolsoperators模块:

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

这种实现很简单。它不使用lambda也不使用任何list-tuple转换,因为它是基于迭代器的。

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)

答案 4 :(得分:3)

为什么不创建自己的类,使用2个笛卡尔点成员变量? (对不起,如果语法有点偏,我的python生锈了)

class point:
    def __init__(self,x,y):
        self.x=x
        self.y=y
        #etc

    def add(self,p):
        return point(self.x + p.x, self.y + p.y)

class vector:
    def __init__(self,a,b):
        self.pointA=a
        self.pointB=b
        #etc

    def add(self,v):
        return vector(self.pointA + v.pointA, self.pointB + v.pointB)

答案 5 :(得分:0)

由于几乎所有的序列操作函数都返回了列表,这就是你将要做的事情。

答案 6 :(得分:0)

对于偶尔使用,可以通过使用标准运算符包来实现不重复lambda的Python 3解决方案:

from operator import add, mul

a = (1, 2, 3)
b = (4, 5, 6)

print(tuple(map(add, a , b)))
print(tuple(map(mul, a , b)))

打印:

(5, 7, 9)
(4, 10, 18)

对于使用numpy向量的严格线性代数计算是规范解决方案:

import numpy as np

a = np.array([1, 2, 3])
b = np.array([4, 5, 6])

print(a+b)
print(a*b)

打印:

[5 7 9]
[ 4 10 18]