objprint
objprint copied to clipboard
Feature: Partially setting the honor_existing option
我不确定这是不是个普遍的场景,还是算作特殊的需求。如下面的代码所示,我想用 objprint 来打印 Struct 类型的变量。但是因为 dataclass 已经有了 __str__ 的实现,我只能把 honor_existing 参数设为 False。可是这样 pathlib.Path 中 parent 属性会让 objprint 在输出时陷入无限递归,直到 depth。然而实际上我不需要对 pathlib.Path 的 honor_existing 设为 False。
from dataclasses import dataclass
from pathlib import Path
from objprint import objprint
@dataclass
class Struct:
path: Path = Path("~")
if __name__ == "__main__":
objprint(Struct(), honor_existing=False, depth=5)
我当前是通过加上自定义的 Formatter 实现上面的需求
from dataclasses import dataclass
from pathlib import Path
from objprint import objprint
@dataclass
class Struct:
path: Path = Path("~")
if __name__ == "__main__":
objprint.register_formatter(Path, str)
objprint(Struct(), honor_existing=False, depth=5)
objprint.unregister_formatter(Path)
这里是无限递归么?看起来每个path都有一个不同的parent啊。有若干种方式解决这个问题,formatter是个挺clean的方案,把parent给exclude掉也可以。你设想的使用方式是什么呢?在op这个函数下的honor_exist必然是对所有object生效的,如果你想config它就需要一个基本同样复杂的filter。还有一个方案是直接在Struct上add_objprint,应该会覆盖它的str
使用 formatter 确实已经很简便了,但是这是一个全局的配置,如果其它地方有另外的设置的话可能会打架,而 honor_exist 是一次性的,没有什么副作用;exclude 的问题是如果刚好有另一个也叫 parent 的字段的话就没法操作了;add_objprint 的话,因为只是使用 objprint 来作为 debug 的工具,并不能将 objprint 嵌入项目代码中,而且对于众多的 dataclass 这样也不是很方便。
我的想法是,对于大部分情况来说,在同一次打印中,对于相同的类型,使用的打印策略应该是相同的,所以我想到的方案 1 和 2,如果要极致的自定义的话可能可以使用方案 3:
- 将 honor_existing 的类型修改为 Union[bool, Set[type]],当为 bool 是按原来的策略处理,为 Set[type] 时判断是否在集合中,如果在则按 True 处理,不在则按 False 处理;
- 将 honor_existing 的类型修改为 Union[bool, Callable[[type], bool]],当为 bool 时则自动转换为 lambda _: honor_existing, 在判断时则将对应类型传入 honor_existing;
- 将 honor_existing 的类型修改为 Union[bool, Dict[str, Union[bool, Recursion...]],该设置与对象同步递归,如果不存在则默认为 True。
这几个方案都不理想,Union[bool, Set]
这种东西就不太应该存在,尤其考虑到config其实是**kwargs,没有提示的。我觉得在argument里应该尽可能避免这种情况。一个可能的方案是增加一个formatter
参数,作为一个一次性的formatter,这样和register_formatter可以保持一致。
如果是一次性的formatter的话,可不可以在objprint() 里加一个argument,类似于这种:
objprint(Struct(), honor_existing=False, depth=5, one_time_formatter=str)
这样会不会更简洁,实现起来也更简单。
因为如果只是一次性的话,在额外加一个register_formatter(Path, str, one_time=true)
这类的call使用起来感觉会更复杂,而如果要在同一个scope里反复使用的话,register_formatter()
和 unregister_formatter()
应该已经够用了?