【问题标题】:How to override comparison for floats in a list in Python?如何覆盖 Python 列表中浮点数的比较?
【发布时间】:2015-10-04 19:38:03
【问题描述】:

我正在尝试检查两个法线向量是否相等。我的法线向量表示为一个三元素列表,每个空间坐标(X、Y 和 Z)一个元素。所有坐标均四舍五入到小数点后 4 位。我想检查两个表面是否具有相同的法线,所以我有如下内容:

if (surface1.normal in [surface2.normal, self.NegatedNormal(surface2.normal)]):
    # do stuff here

问题是我的法线看起来像这样:

surface1.normal: [0.9947, 0.0155, 0.1015]
surface2.normal: [0.9947, 0.0155, 0.1014]

请注意,z 坐标偏离了 0.0001。那么有没有一种方法可以覆盖等于运算符以接受彼此在 0.0001 范围内的答案,这与其他数据结构中的比较(如我的情况中的列表)兼容?我有一种感觉,我必须编写自己的 __eq__ 方法,但我不太确定该怎么做。

另外,如果这不是最好的做法,他们是比较给定公差内的两个浮动列表的更好方法吗?

【问题讨论】:

  • 您可以编写一个继承自常规浮点数的自定义浮点数对象,但为其运算符编写您自己的函数(例如__ge__() is >=
  • @SuperBiasedMan 这是一个有趣的方法。不幸的是,这需要重构大量代码,所以我犹豫是否尝试,但我不知道您可以从原始类型继承,所以这对未来有好处。
  • 是的,你基本上可以创建一个原语的子类,然后只覆盖你想要表现不同的函数。
  • @SuperBiasedMan 建议的几乎是最佳选择。只需创建您的自定义 normal 类并覆盖比较方法。示例here.

标签: python overriding comparison-operators


【解决方案1】:

您可以编写一个自定义函数来执行此操作。例如:

def comp_floats(x, y, delta):
    return abs(x - y) < delta

显然,您可以在函数本身中进行任何类型的纠错,但这只是一个示例。

【讨论】:

  • 我知道我可以做到这一点,但我正在寻找更通用的东西,可以与其他编程逻辑一起使用。例如原始帖子中的布尔值对于某个公差返回 true 或 surface1.normal == surface2.normal 返回 true。基本上我正在寻找一种重载比较运算符的好方法,但我不确定如何在 Python 中实现它,或者考虑到我想要的效果是否有可能。
  • 您不能在 Python 中覆盖关键字/符号。这就是你可以做到的方式。
  • 我想说比较 2 个浮点数的正确方法是:abs(x - y) &lt; epsilon
  • @imaluengo 谢谢,我永远记不住正确的算法。 :P
【解决方案2】:

您无法更改内置的 List 属性,但您可以通过扩展至您自己的列表子类来覆盖这些属性。

class CustomList(list):
    deviation = None

    def deviation_check(self, val1, val2):
       if self.deviation:
           return min(val1, val2) + self.deviation >= max(val1, val2)
       return val1 == val2

    def __eq__(self, other):
        is_equal = (self.deviation_check(self[0], other[0]) and
                   self.deviation_check(self[1], other[1]) and
                   self.deviation_check(self[2], other[2]))

        return is_equal

l = CustomList()
l.deviation = 0.0001 # Note: Added deviation as a property
l.extend([0.9947, 0.0155, 0.1015])

l1 = CustomList()
l1.extend([0.9947, 0.0155, 0.1014])

print l == l1

现在我们有两个 CustomList 对象 l 和 l1,当你尝试检查列表 l == l1 之间的相等性时,python 以这种方式检查它的相等性 l.__eq__(l1),这就是我们重写 __eq__ 魔术方法以按我们喜欢的方式工作的原因。

如果您不添加l.deviation,它会检查是否相等。你也必须清楚你要在哪一边添加deviation 值。

正如我所说,l == l1 在 python 中被转换为l.__eq__(l1),所以你必须将deviation 属性添加到== 左侧的列表对象中。

如果你运行上面的脚本..

# Output ---------------
True

这是因为我们已经定义了l.deviation == 0.0001,这对检查相等性产生了影响。

希望这能解决您的问题..

【讨论】:

  • def __eq__(self, other): if len(self) != len(other): return False return all([self.deviation_check(item, other[index]) for index, item in enumerate(self)]) 这将适用于任何长度的列表,而不仅仅是您上面的答案适用的 3 个长度的列表。
  • @gsb-eng 您应该覆盖列表构造以在其中添加偏差参数。像__init__(self, *args, deviation=1e-3) 和适当的super(CustomList, self).__init__(*args) 这样的东西应该可以让你创建CustomList(1,2,3,4,5, deviation=1e-6)(未经测试,所以它可能有错别字)。
猜你喜欢
  • 2017-08-20
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2018-08-15
  • 1970-01-01
  • 1970-01-01
  • 2016-02-11
相关资源
最近更新 更多