Python单元测试中的assertAlmostEqual用于浮点数的集合 [英] assertAlmostEqual in Python unit-test for collections of floats

查看:39
本文介绍了Python单元测试中的assertAlmostEqual用于浮点数的集合的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

assertAlmostEqual(x,y)方法://docs.python.org/library/unittest.html> Python的单元测试框架测试 x y 是否大致相等(假设它们是浮点型)

assertAlmostEqual()的问题在于它仅适用于浮点数.我正在寻找类似 assertAlmostEqual()的方法,该方法适用于浮点数列表,浮点数集,浮点数字典,浮点数元组,浮点数元组列表,浮点数列表集等

例如,让 x = 0.1234567890 y = 0.1234567891 . x y 几乎相等,因为它们在除最后一位以外的每个数字上都一致.因此 self.assertAlmostEqual(x,y) True ,因为 assertAlmostEqual()适用于浮点数.

我正在寻找一个更通用的 assertAlmostEquals(),它还会评估对 True 的以下调用:

  • self.assertAlmostEqual_generic([x,x,x],[y,y,y]).
  • self.assertAlmostEqual_generic({1:x,2:x,3:x},{1:y,2:y,3:y}).
  • self.assertAlmostEqual_generic([(x,x)],[(y,y)]).

是否有这种方法,还是我必须自己实现?

说明:

  • assertAlmostEquals()有一个名为 places 的可选参数,并且通过计算四舍五入到小数位数的 places .默认情况下, places = 7 ,因此 self.assertAlmostEqual(0.5,0.4)为False,而 self.assertAlmostEqual(0.12345678,0.12345679)为True.我的推测性 assertAlmostEqual_generic()应该具有相同的功能.

  • 如果两个列表具有相同顺序的几乎相等的数字,则认为它们几乎相等.形式上, for range(n)中的i:self.assertAlmostEqual(list1 [i],list2 [i]).

  • 类似地,如果两个集合可以转换为几乎相等的列表(通过为每个集合分配顺序),则认为它们几乎相等.

  • 类似地,如果每个字典的键集几乎等于另一个字典的键集,则两个字典被认为几乎相等,并且对于每个这样的几乎相等的键对,都有一个相应的几乎相等的值.

  • p>
  • 通常:我认为如果两个集合相等,则除两个相应的浮点数几乎相等之外,它们几乎相等.换句话说,我真的想比较对象,但在沿途比较浮点数时精度(自定义)较低.

解决方案

这是我实现通用 is_almost_equal(first,second)函数的方法:

首先,复制您需要比较的对象( first second ),但不要进行精确的复制:切掉您所用浮点数的无关紧要的十进制数字在物体内部遇到.

现在,您已经拥有了 first second 的副本,对于这些副本来说,十进制数字已经消失了,只需比较 first second使用 == 运算符.

让我们假设我们有一个 cut_insignificant_digits_recursively(obj,place)函数,该函数复制 obj 但只保留每个 places 最高有效十进制数字漂浮在原始的 obj 中.这是 is_almost_equals(第一,第二,地方)的有效实现:

 从insignificant_digit_cutter递归导入cut_insignificant_digits_def is_almost_equal(第一,第二,位置):如果第一和第二相等,则返回True.如果第一和第二个不相等但具有完全相同的值,则返回true结构和值,除了一堆几乎差不多的浮点数相等(如果我们仅考虑浮点数,则浮点数相等,则浮点数几乎相等(将每个数字的最高有效位)).'''如果第一个==第二个:返回Truecut_first = cut_insignificant_digits_recursively(第一,地方)cut_second =递减cut_insignificant_digits_digits(秒,位)返回cut_first == cut_second 

这是 cut_insignificant_digits_recursively(obj,place)的有效实现:

  def cut_insignificant_digits(数字,位):'''截断数字的最低有效十进制数字,仅保留[位数]个十进制数字'''如果type(number)!= float:返回数字number_as_str = str(数字)end_of_number = number_as_str.find('.')+位数+1如果end_of_number>len(number_as_str):返回数字返回float(number_as_str [:end_of_number])def cut_insignificant_digits_lazy(可迭代,位置):对于可迭代的obj:递归地产生cut_insignificant_digits_递归(obj,地方)def cut_insignificant_digits_recursively(obj,位):'''返回obj的副本,但每个浮点都失去其最低有效位小数位数仅剩余[位]小数位数'''t =类型(obj)如果t == float:返回cut_insignificant_digits(obj,位)如果t in(列表,元组,集合):返回t(cut_insignificant_digits_lazy(obj,位))如果t == dict:返回{cut_insignificant_digits_recursively(键,位置):cut_insignificant_digits_recursively(val,地方)用于键,val在obj.items()}中返回obj 

可在此处获得代码及其单元测试: https://github.com/snakile/approximate_comparator.我欢迎进行任何改进和错误修复.

The assertAlmostEqual(x, y) method in Python's unit testing framework tests whether x and y are approximately equal assuming they are floats.

The problem with assertAlmostEqual() is that it only works on floats. I'm looking for a method like assertAlmostEqual() which works on lists of floats, sets of floats, dictionaries of floats, tuples of floats, lists of tuples of floats, sets of lists of floats, etc.

For instance, let x = 0.1234567890, y = 0.1234567891. x and y are almost equal because they agree on each and every digit except for the last one. Therefore self.assertAlmostEqual(x, y) is True because assertAlmostEqual() works for floats.

I'm looking for a more generic assertAlmostEquals() which also evaluates the following calls to True:

  • self.assertAlmostEqual_generic([x, x, x], [y, y, y]).
  • self.assertAlmostEqual_generic({1: x, 2: x, 3: x}, {1: y, 2: y, 3: y}).
  • self.assertAlmostEqual_generic([(x,x)], [(y,y)]).

Is there such a method or do I have to implement it myself?

Clarifications:

  • assertAlmostEquals() has an optional parameter named places and the numbers are compared by computing the difference rounded to number of decimal places. By default places=7, hence self.assertAlmostEqual(0.5, 0.4) is False while self.assertAlmostEqual(0.12345678, 0.12345679) is True. My speculative assertAlmostEqual_generic() should have the same functionality.

  • Two lists are considered almost equal if they have almost equal numbers in exactly the same order. formally, for i in range(n): self.assertAlmostEqual(list1[i], list2[i]).

  • Similarly, two sets are considered almost equal if they can be converted to almost equal lists (by assigning an order to each set).

  • Similarly, two dictionaries are considered almost equal if the key set of each dictionary is almost equal to the key set of the other dictionary, and for each such almost equal key pair there's a corresponding almost equal value.

  • In general: I consider two collections almost equal if they're equal except for some corresponding floats which are just almost equal to each other. In other words, I would like to really compare objects but with a low (customized) precision when comparing floats along the way.

解决方案

Here's how I've implemented a generic is_almost_equal(first, second) function:

First, duplicate the objects you need to compare (first and second), but don't make an exact copy: cut the insignificant decimal digits of any float you encounter inside the object.

Now that you have copies of first and second for which the insignificant decimal digits are gone, just compare first and second using the == operator.

Let's assume we have a cut_insignificant_digits_recursively(obj, places) function which duplicates obj but leaves only the places most significant decimal digits of each float in the original obj. Here's a working implementation of is_almost_equals(first, second, places):

from insignificant_digit_cutter import cut_insignificant_digits_recursively

def is_almost_equal(first, second, places):
    '''returns True if first and second equal. 
    returns true if first and second aren't equal but have exactly the same
    structure and values except for a bunch of floats which are just almost
    equal (floats are almost equal if they're equal when we consider only the
    [places] most significant digits of each).'''
    if first == second: return True
    cut_first = cut_insignificant_digits_recursively(first, places)
    cut_second = cut_insignificant_digits_recursively(second, places)
    return cut_first == cut_second

And here's a working implementation of cut_insignificant_digits_recursively(obj, places):

def cut_insignificant_digits(number, places):
    '''cut the least significant decimal digits of a number, 
    leave only [places] decimal digits'''
    if  type(number) != float: return number
    number_as_str = str(number)
    end_of_number = number_as_str.find('.')+places+1
    if end_of_number > len(number_as_str): return number
    return float(number_as_str[:end_of_number])

def cut_insignificant_digits_lazy(iterable, places):
    for obj in iterable:
        yield cut_insignificant_digits_recursively(obj, places)

def cut_insignificant_digits_recursively(obj, places):
    '''return a copy of obj except that every float loses its least significant 
    decimal digits remaining only [places] decimal digits'''
    t = type(obj)
    if t == float: return cut_insignificant_digits(obj, places)
    if t in (list, tuple, set):
        return t(cut_insignificant_digits_lazy(obj, places))
    if t == dict:
        return {cut_insignificant_digits_recursively(key, places):
                cut_insignificant_digits_recursively(val, places)
                for key,val in obj.items()}
    return obj

The code and its unit tests are available here: https://github.com/snakile/approximate_comparator. I welcome any improvement and bug fix.

这篇关于Python单元测试中的assertAlmostEqual用于浮点数的集合的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆