地图功能似乎没有执行?

时间:2015-04-02 21:39:18

标签: python-3.x

我试图为使用简单验证功能的函数编写测试。我的函数需要验证所有传入的参数。验证函数在出现问题时抛出AttributeError,否则返回True。但是,当我映射validate时,除非我执行其他操作,否则它不会起作用,例如使用all()

def my_function(a, b, c=None):
    map(validate, (a, b, c))  # This doesn't fail validation (incorrect behavior)
    all(map(validate, (a, b, c))  # This DOES fail validation (correct behavior)
    # Some other stuff

我的单元测试是这样做的:

def test_my_function(self):
    bad_id = 0
    self.assertRaises(AttributeError, add_favorite, 10, bad_id)

这是我的验证功能:

def validate(identifier):
    if identifier is None:
        return True
    elif not isinstance(identifier, int):
        raise AttributeError("Identifier={0} not of type integer.".format(identifier))
    elif not (identifier > 0):
        raise AttributeError("Identifier={0} not an integer greater than zero.".format(identifier))
    else:
        return True

我很困惑,为什么map似乎无法执行,除非我将其包装在其他内容中:我已经确认它不会通过调试运行测试来执行并看到它永远不会进入validate()。 (我认为做出额外的工作没有意义......)根据documentation,似乎map 应该迭代这些参数并执行该函数。有人可以解释为什么它没有?

编辑:正确documentation表明map返回一个延迟评估的迭代器。

1 个答案:

答案 0 :(得分:4)

在python 3.x中,map创建一个可迭代的。 Iterables被懒惰地评估 - 也就是说,实际上只评估迭代的元素(例如循环遍历它们)。在您的示例中,由于您不会迭代任何值,因此不会评估任何值。但是,在any的情况下,any会迭代整个结果map,因此整个事情都会得到评估。

它有点复杂,因为还有其他方法来评估迭代的部分而不迭代它们(例如使用next()来评估并获得下一个项目),但是对于您的目的,这是足够接近。

所以这些得到评估:

all(map(validate, (a, b, c))
list(map(validate, (a, b, c)))
[_ for x in map(validate, (a, b, c))]
a = map(validate, (a, b, c);all(a)
b = map(validate, (a, b, c));list(b)

请注意,在最后两种情况下,在调用all(a)list(a)之前不会评估iterable。这可能是代码后面,另一个函数和/或另一个模块中的许多行。

此外,迭代只能一次评估一个步骤。考虑这个例子:

for x in map(validate, (a, b, c)):
    print(x)

它将在第一个项目上运行validate,放入x,然后运行循环体。完成循环的第一次运行后,它才会评估第二项,然后再次遍历循环体,然后评估下一项,依此类推。如果中断循环,则不会评估剩余的项目。

这些不会被评估:

map(validate, (a, b, c)
a = map(validate, (a, b, c)
(_ for x in map(validate, (a, b, c)))