【问题标题】:How can I create my own datatype in python so that I could overwrite arithmetic operators?如何在 python 中创建自己的数据类型以便覆盖算术运算符?
【发布时间】:2012-10-24 07:32:00
【问题描述】:

我目前正在使用 Python/Numpy 处理地理/GPS 数据(喜欢它!),并且我面临着计算由坐标对 pn = [lon, lat] 定义的地理点之间的距离的重复任务。

我有一个这样使用的函数:dist = geodistance(p1, p2),它类似于线性代数中的欧几里得距离(向量减法/差),但出现在测地线(球形)空间而不是矩形欧几里得空间中。

以编程方式,欧几里得距离由下式给出

dist = ((p2[0] - p1[0])**2 + (p2[1] - p1[1])**2)**0.5

在数学上,这相当于“惯用的”(因为没有更好的词)句子

dist = p1 - p1   # the "norm" of the vector difference, subtraction.

目前,我的距离是这样的:

p1 = [-51.598354,-29.953363]
p2 = [-51.598701,-29.953045]
dist = geodistance(p1, p2)
print dist

>> 44.3904032407

我想这样做:

print p2 - p1  # these points now are from some fancy datatype

>> 44.3904032407

以及最终目标:

track = numpy.array([[-51.203018 -29.996149]
                     [-51.203018 -29.99625 ]
                     [-51.20266  -29.996229]
                     [-51.20229  -29.996309]
                     [-51.201519 -29.99416 ]], dtype=fancy)  # (**) or something like

print numpy.diff(track)

>> ndarray([[   0.        ]
            [   7.03531252]
            [  39.82663316]
            [  41.50958596]
            [ 172.49825765]])

类似的事情是:如果你取两个datetime 对象并减去它们,该操作返回一个timedelta 对象。我想减去两个坐标并得到测地线距离。

我想知道一个类是否可以工作,但是 dtype(例如 float32 的“子类型”)对从列表创建数组有很大帮助(** 这是我从 xml 文件中读取内容的方式)。

非常感谢!

【问题讨论】:

  • 查看运算符重载。
  • 您可以将 ndarray 子类化以重载减号运算符,但在您做一些可能令人困惑的事情之前,我会三思而后行。当然也可以添加一个新方法,比如.dist()。 numpy 文档有一些简单的示例。只需添加一个方法,您实际上不需要做太多事情,否则可能会有点棘手。
  • 使用 numpy.array(..., dtype=fancy) 时会遇到的一个问题是你会失去 numpy 的速度,因为它会考虑 Python 对象的数组而不是数字。也许你想要一个 Coord 类型,它有时包含一对数字,有时包含一对数组..

标签: python numpy gps latitude-longitude user-defined-types


【解决方案1】:

您可以通过创建一个类并编写__add____sub__ 方法来定义自己的类型。

例如:

class P(object):
    def __init__(self, lon, lat):
        self.lon = lon
        self.lat = lat

    def __sub__(self, other):
        dist = ((other.lon - self.lon)**2 + (other.lat - self.lat)**2)**0.5
        return dist

鉴于您当前正在使用列表索引语法获取点的坐标,您还可以实现这些:

class P(object):
    def __init__(self, lon, lat):
        self.lon = lon
        self.lat = lat

    def __sub__(self, other):
        dist = ((other[0] - self[0])**2 + (other[1] - self[1])**2)**0.5
        return dist

    def __getitem__(self, key):
        if key == 0:
            return self.lon
        elif key == 1:
            return self.lat
        else:
            raise IndexError

    def __setitem__(self, key, value):
        if key == 0:
            self.lon = value
        elif key == 1:
            self.lat = value
        else:
            raise IndexError

(我意识到上述方法可能不是最优雅的方式)。

这样,您的新课程就可以替代您当前使用的列表。

Python documentation 包含有关创建用户定义类型所需编写的双下划线方法的更多信息。 (您要查找的信息大约从页面的一半开始)

【讨论】:

  • 据我目前的理解,我无法使用 dtype 关键字(如 numpy.array(coordlist, dtype=fancy))直接从列表创建数组,而是必须创建一个实例列表,例如 Coord 类,然后做numpy.array(CoordList, dtype=object),不是吗?如果可能的话,我想要一个实际的数字类型,而不是object 的子类,但如果这不可能,我认为这将是可行的方法。目前我对numeric.Real抽象基类很好奇...
【解决方案2】:

Python 语言参考,§3.4.8,"Emulating numeric types"

具体来说,__sub__()

【讨论】:

  • 我已经读过它,也读过抽象基类(例如number.Real),但找不到一个可行的例子。我应该寻找什么?
  • 你不会寻找任何东西。 a - ba.__sub__(b)(除非不是,但那是另一回事)。
  • 我的意思是寻找如何创建我的新类型,例如使用 numeric.Real 抽象基类。我可以使用numpy.array(coordlist, dtype=coordtype) 创建我的数组吗,或者我必须将对象子类化并像numpy.array(coordlist, dtype=object) 那样做,然后是coordlist 一个 Coord(object) 实例的列表?
  • coordtype 不是dtype 的有效值。此外,由于您的类型实际上不是数字(它是数字,是的,但它不满足数字的其余要求)它不应该从另一个数字类型派生.
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2019-03-13
  • 2010-12-29
  • 2014-06-23
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多