gaogaotiantian / objprint

A library that can print Python objects in human readable format
Apache License 2.0
510 stars 43 forks source link

Feature: Partially setting the honor_existing option #102

Open ChenMoFeiJin opened 8 months ago

ChenMoFeiJin commented 8 months ago

我不确定这是不是个普遍的场景,还是算作特殊的需求。如下面的代码所示,我想用 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)

image


我当前是通过加上自定义的 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)

image

gaogaotiantian commented 8 months ago

这里是无限递归么?看起来每个path都有一个不同的parent啊。有若干种方式解决这个问题,formatter是个挺clean的方案,把parent给exclude掉也可以。你设想的使用方式是什么呢?在op这个函数下的honor_exist必然是对所有object生效的,如果你想config它就需要一个基本同样复杂的filter。还有一个方案是直接在Struct上add_objprint,应该会覆盖它的str

ChenMoFeiJin commented 8 months ago

使用 formatter 确实已经很简便了,但是这是一个全局的配置,如果其它地方有另外的设置的话可能会打架,而 honor_exist 是一次性的,没有什么副作用;exclude 的问题是如果刚好有另一个也叫 parent 的字段的话就没法操作了;add_objprint 的话,因为只是使用 objprint 来作为 debug 的工具,并不能将 objprint 嵌入项目代码中,而且对于众多的 dataclass 这样也不是很方便。

我的想法是,对于大部分情况来说,在同一次打印中,对于相同的类型,使用的打印策略应该是相同的,所以我想到的方案 1 和 2,如果要极致的自定义的话可能可以使用方案 3:

  1. 将 honor_existing 的类型修改为 Union[bool, Set[type]],当为 bool 是按原来的策略处理,为 Set[type] 时判断是否在集合中,如果在则按 True 处理,不在则按 False 处理;
  2. 将 honorexisting 的类型修改为 Union[bool, Callable[[type], bool]],当为 bool 时则自动转换为 lambda : honor_existing, 在判断时则将对应类型传入 honor_existing;
  3. 将 honor_existing 的类型修改为 Union[bool, Dict[str, Union[bool, Recursion...]],该设置与对象同步递归,如果不存在则默认为 True。
gaogaotiantian commented 8 months ago

这几个方案都不理想,Union[bool, Set]这种东西就不太应该存在,尤其考虑到config其实是**kwargs,没有提示的。我觉得在argument里应该尽可能避免这种情况。一个可能的方案是增加一个formatter参数,作为一个一次性的formatter,这样和register_formatter可以保持一致。

happytree718 commented 7 months ago

如果是一次性的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() 应该已经够用了?