V2EX = way to explore
V2EX 是一个关于分享和探索的地方
现在注册
已注册用户请  登录
推荐学习书目
Learn Python the Hard Way
Python Sites
PyPI - Python Package Index
http://diveintopython.org/toc/index.html
Pocoo
值得关注的项目
PyPy
Celery
Jinja2
Read the Docs
gevent
pyenv
virtualenv
Stackless Python
Beautiful Soup
结巴中文分词
Green Unicorn
Sentry
Shovel
Pyflakes
pytest
Python 编程
pep8 Checker
Styles
PEP 8
Google Python Style Guide
Code Style from The Hitchhiker's Guide
deplives
V2EX  ›  Python

python3 中 json 的一个 bug?

  •  
  •   deplives · 2020-11-18 11:06:56 +08:00 · 2819 次点击
    这是一个创建于 1458 天前的主题,其中的信息可能已经有所发展或是发生改变。

    目前发现这样一个问题:

    自定义一个 dict 子类,并且实现自定义 __bool__ 方法后 json.dumps() 会产生和预期不符的结果

    话不多说,上代码

    import json
    
    class InnerDict(dict):
        def __init__(self, **kwargs):
            super().__init__(**kwargs)
    
        def __bool__(self):
        # 原始这里的逻辑是只要字典为空才返回 False,非空都返回 True
        # 但是如果改掉比如这里所有都返回 False
            return False
            
    dct = InnerDict()
    dct["hello] = "world"
    print(dct) # 到这里都没有问题
    json.dumps(dct, ensure_ascii=False, indent=4) # 但是这里,dumps 的结果就是 {}
    

    原因分析:

    jsonencoder.py 文件中 _make_iterencode 方法内有个闭包的方法 _iterencode_dict

    在这方法中第一件事去判断 dct 是否为空,用了

    if not dct:
        yield '{}'
        return
    

    截图在这里 https://www.v2ex.com/i/5Xjw6YFt.jpeg

    但是问题就在这里,这里会调用 __bool__ 判断,但是 __bool__ 的逻辑已经被改了,所以在上面的代码里这里一直为 {}

    而且追踪了 _iterencode_dict 方法,发现三处调用处均判断了 dct 对象为 dict

    这里的 if not dct 仅仅做了判断 dct 是否为空 dict

    目前就是这么个情况,不知道这算不算一个 bug

    16 条回复    2020-11-18 17:11:02 +08:00
    ruanimal
        1
    ruanimal  
       2020-11-18 11:12:46 +08:00
    这不算 bug 吧。。

    而且你这个继承违反了里氏代换原则
    acmore
        2
    acmore  
       2020-11-18 11:13:55 +08:00
    如果 `_iterencode_dict` 只管按自己的逻辑去判断是否为空,而不管你怎么实现 `__bool__`,这才是一个 Bug,因为实际上对 `__bool__` 的重载是无效的。

    你没有正确实现 `__bool__` 的功能,自然也得不到期待的结果。
    huangzhiyia
        3
    huangzhiyia  
       2020-11-18 11:15:32 +08:00 via Android
    不算,我认为你破坏了设计上的传递性。
    Vinty
        4
    Vinty  
       2020-11-18 11:15:53 +08:00
    对象为空,所以 dump 也是空的,这很符合逻辑啊
    ofooo
        5
    ofooo  
       2020-11-18 11:27:42 +08:00
    不要弄 dict 的子类,确实有 bug,python 猫公众号前几天还讲过。

    不要弄 python 官方类的子类的魔法函数,他继承顺序有一定的不一致性
    ofooo
        6
    ofooo  
       2020-11-18 11:29:27 +08:00
    不过感觉你这个是自作自受吧。。。。人家这个逻辑没问题啊
    xuboying
        7
    xuboying  
       2020-11-18 12:54:44 +08:00
    看了半天没理解 op 觉得哪里是 bug ?是对那个 if 判断的条件有异议么? op 期望是怎样的结果。
    cz5424
        8
    cz5424  
       2020-11-18 13:01:01 +08:00 via iPhone
    这个问题类似于,我修改了支付宝客服端,加了个一百万,但是我买不了东西
    Wincer
        9
    Wincer  
       2020-11-18 13:01:12 +08:00 via Android
    楼主意思是 if not dct 应该改成 if dct is not {}?
    SjwNo1
        10
    SjwNo1  
       2020-11-18 14:38:22 +08:00
    那就再改 __bool__ 知道你满意为止哈哈
    mringg
        11
    mringg  
       2020-11-18 14:40:42 +08:00
    我想说人生已经够难了,不要难为自己了,就用些基本的用法不好么
    no1xsyzy
        12
    no1xsyzy  
       2020-11-18 14:40:55 +08:00
    dict 的 __bool__ 含义就是这个 dict 是否为空
    _iterencode_dict 说:既然你说它是空,那它就是空咯,不反驳,'{}' 来
    timothyqiu
        13
    timothyqiu  
       2020-11-18 14:41:39 +08:00
    说白了就是 garbage in, garbage out
    cnrting
        14
    cnrting  
       2020-11-18 15:35:23 +08:00 via iPhone
    都用 python 了何苦再为难自己
    lithbitren
        15
    lithbitren  
       2020-11-18 17:01:28 +08:00
    只有在算法题里写字典树的时候会继承字典,其他情况下原则上都不会继承这些基础数据结构
    krixaar
        16
    krixaar  
       2020-11-18 17:11:02 +08:00
    这是不是可以类比成后端偷偷改了接口返回值,前端崩了说是前端有 bug ?
    关于   ·   帮助文档   ·   博客   ·   API   ·   FAQ   ·   实用小工具   ·   1153 人在线   最高记录 6679   ·     Select Language
    创意工作者们的社区
    World is powered by solitude
    VERSION: 3.9.8.5 · 24ms · UTC 18:48 · PVG 02:48 · LAX 10:48 · JFK 13:48
    Developed with CodeLauncher
    ♥ Do have faith in what you're doing.