如何用Hypothesis生成属性互相关联的类A实例?
生成满足约束的A类Hypothesis策略
要生成A类型的对象并保证ages字典的键全部来自names列表,你可以通过Hypothesis的自定义策略实现,以下是两种常用方案:
方案一:使用@st.composite自定义策略(推荐)
这种方式逻辑清晰,便于扩展和维护:
from dataclasses import dataclass from hypothesis import given, strategies as st @dataclass class A: names: list[str] ages: dict[str, float] @st.composite def valid_a_strategy(draw): # 生成非空的names列表(若需支持空列表,可去掉min_size参数并处理后续逻辑) names = draw(st.lists(st.text(min_size=1), min_size=1)) # 从names中选取若干不重复的元素作为ages的键集合 selected_keys = draw(st.sets(st.sampled_from(names))) # 生成键来自selected_keys、值为合法浮点数的ages字典 ages = draw( st.dictionaries( keys=st.sampled_from(selected_keys), values=st.floats(allow_nan=False, allow_infinity=False) ) ) return A(names=names, ages=ages) # 验证约束的测试用例 @given(valid_a_strategy()) def test_a_ages_keys_in_names(obj: A): assert all(key in obj.names for key in obj.ages.keys())
关键逻辑说明:
@st.composite装饰器用于创建自定义策略,draw函数负责从子策略中生成具体值。st.sampled_from(names)确保键只能从已生成的names列表中选取。- 使用
st.sets避免生成重复键(符合字典键的唯一性要求)。 - 排除
nan和无穷大的浮点数,避免后续测试中出现异常值。
方案二:使用flatmap结合builds
如果偏好链式调用的写法,可以用flatmap传递生成的names列表:
valid_a_strategy = ( st.lists(st.text(min_size=1), min_size=1) .flatmap( lambda names: st.builds( A, names=st.just(names), # 固定names参数为已生成的列表 ages=st.dictionaries( keys=st.sampled_from(names), values=st.floats(allow_nan=False, allow_infinity=False) ) ) ) )
支持空names列表的扩展
如果需要允许names为空,需保证ages也为空字典,调整策略如下:
@st.composite def a_strategy_with_empty(draw): names = draw(st.lists(st.text())) if not names: ages = {} else: selected_keys = draw(st.sets(st.sampled_from(names))) ages = draw( st.dictionaries( keys=st.sampled_from(selected_keys), values=st.floats(allow_nan=False, allow_infinity=False) ) ) return A(names=names, ages=ages)
内容的提问来源于stack exchange,提问作者Zac Hatfield-Dodds
相关产品推荐
相关产品推荐

