代码之家  ›  专栏  ›  技术社区  ›  Daniel Walker

创建返回唯一值的假设策略

  •  0
  • Daniel Walker  · 技术社区  · 3 年前

    我正在尝试创建 hypothesis 产生无重复整数的策略。这是我的代码:

    import hypothesis
    import hypothesis.strategies as strategies
    
    def unique(strat):
        previous = set()
    
        @strategies.composite
        def new_strategy(draw):
            while True:
                value = draw(strat)
                if value not in previous:
                    previous.add(value)
                    return value
    
        return new_strategy
    
    strategy = unique(strategies.integers(min_value=0, max_value=1000))
    
    @hypothesis.given(num=strategy)
    def test_unique(num):
        pass
    

    然而,当我跑步时 pytest ,我明白

        @check_function
        def check_strategy(arg, name="")
            if not isinstance(arg, SearchStrategy):
                hint = ""
                if isinstance(arg, (list, tuple)):
                    hint = ", such as st.sampled_from({}),".format(name or "...")
                if name:
                    name += "="
                raise InvalidArgument(
                    "Expected a SearchStrategy%s but got %s%r (type=%s)"
                    % (hint, name, arg, type(arg).__name__)
                )
    E           hypothesis.errors.InvalidArgument: Expected a SearchStrategy but got mapping['num']=<function accept.<locals>.new_strategy at 0x7f30622418b0> (type=function)
    
    0 回复  |  直到 3 年前
        1
  •  2
  •   Zephyr    2 年前
    @st.composite
    def unique(draw, strategy):
        seen = draw(st.shared(st.builds(set), key="key-for-unique-elems"))
        return draw(
            strategy
            .filter(lambda x: x not in seen)
            .map(lambda x: seen.add(x) or x)
        )
    

    这里有几个可爱的小技巧:

    1. 使用 st.shared() 创建一个新的 seen 每个不同示例的缓存。这解决了“如果你的价值观用完了怎么办”的问题,但是 而且 修复了关键的“测试无法回放失败”问题,这会使整个事情变得非常糟糕。
      • 有关高级技巧,请尝试使用 key= 的论点 shared 或者让它构造一本词典。
    2. .filter(...) 以排除已看到的项目。这比循环要好,因为这意味着假设可以更有效地避免和报告生成一个尚未看到的例子的徒劳尝试。
    3. 这个 .map(...) 要求将其添加到片场是对事实的公然滥用 set.add() 退货 None 在改变对象之后 or 如果第一项为false,则计算为第二项。