传入tuple时event()因WeakKeyDictionary抛出TypeError问题咨询
问题根因
这个报错来自Hypothesis测试框架event()方法的内部实现:它用WeakKeyDictionary缓存传入的事件键做统计去重,而Python由C实现的内置类型(包括tuple、int、str等)默认不支持创建弱引用,直接传入tuple就会触发你看到的TypeError。
通用处理方案
实际使用中大家一般根据测试场景选下面几种方案,都能正常拿到list/tuple值的统计结果:
轻量包装类方案(通用性最强)
写一个极简单的自定义类把要统计的tuple包一层,自定义类默认支持弱引用,只要实现__hash__和__eq__保证统计去重逻辑和直接传tuple完全一致即可,不需要改原有业务逻辑:class _EventKey: __slots__ = ("_obj", "__weakref__") def __init__(self, obj): self._obj = obj def __hash__(self): return hash(self._obj) def __eq__(self, other): return isinstance(other, _EventKey) and self._obj == other._obj def __repr__(self): return repr(self._obj) # 调用时直接包一层就行 @given(lists(builds(Foo, integers(), text()), max_size=3)) def test_foo(l): t = tuple(l) event(_EventKey(t))这个方案对所有可哈希但不支持弱引用的内置类型都生效,统计结果和直接传原对象没有区别。
自定义容器方案(适合长期维护的测试用例)
如果你本来就是要统计一组Foo对象的分布,直接定义一个frozen的自定义容器类代替tuple即可,自定义容器天然支持弱引用,还可以按需加统计维度的辅助方法:@dataclass(frozen=True) class FooList: items: tuple[Foo, ...] def __hash__(self): return hash(self.items) @given(lists(builds(Foo, integers(), text()), max_size=3)) def test_foo(l): event(FooList(tuple(l)))特征标签方案(日常调试最常用)
event()本质是做分类统计,大多数时候你不需要传入完整对象,只要把你关心的统计维度拼成字符串标签传入即可,既规避弱引用问题,统计输出的可读性还更高:@given(lists(builds(Foo, integers(), text()), max_size=3)) def test_foo(l): # 比如你只关心列表长度、所有Foo的x值分布,直接拼标签即可 event(f"list_len={len(l)}, x_values={tuple(item.x for item in l)}")
不要尝试给tuple等内置类型打猴子补丁加弱引用支持,Python不允许给C实现的内置类型动态绑定属性,会直接抛出异常。
内容的提问来源于stack exchange,提问作者Yoshi
相关产品推荐
相关产品推荐

