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
uwh0am1
V2EX  ›  Python

Python inmutabledict 的实现

  •  1
     
  •   uwh0am1 · 2018-07-26 11:33:45 +08:00 · 1829 次点击
    这是一个创建于 2337 天前的主题,其中的信息可能已经有所发展或是发生改变。
    ## python inmutabledict 的实现

    关于在 python 中如何实现不可变字典的方法。早在 pep416 中,就建议 python 官方实现 inmutabledict,但是官方否认了。理由主要是

    * 根据 Raymond Hettinger 的说法,使用 frozendict 很愚蠢。 那些使用它的人倾向于仅将它用作提示,例如声明全局或类级别的“常量”:它们实际上不是永久不变的,因为任何人仍然可以指定名称。
    * There are existing idioms for avoiding mutable default values.


    所以,这个提议就被否决了。但是我们依旧可以自己实现一个 inmutabledict。inmutable 主要的特点是

    * dict 内的值只能在初始化的时候指定
    * 在运行期间,不能添加删除新增 dict 内部的值

    结合[starkoverflow 上面的回答]( https://stackoverflow.com/questions/2703599/what-would-a-frozen-dict-be)
    我们可以通过如下几种魔改的方式实现 python 的 inmutabledict

    ### 几种变通的方案
    #### 1. 最原始的方法,修改 setitem 魔术方法
    在 python 中,d["foo"]=bar,将 foo 和 bar 作为参数,调用的是 python 的模式方法`__setitem__`。函数原型为`def __setitem__(self, key, value):`。所以,我们可以继承 dict 类,实现自己的`__setitem__`。在修改值的时候,抛出 TypeError。不就是可以达到无法修改字典的值的目的了嘛。代码如下

    ```
    class myDict(dict):
    def __setitem__(self, key, value):
    raise TypeError("inmutabledict can not be modifyed value")

    d = myDict({1:2,3:4})
    d[1]=4

    ```
    运行则会提示

    ```
    raise TypeError("inmutabledict can not be modifyed value")
    TypeError: inmutabledict can not be modifyed value

    Process finished with exit code 1
    ```
    很好,完美的完成了任务。这种方法应用最为广泛,在 werkzeug 框架中的 ImmutableDict 等,就是通过修改魔术方法来实现的不可变字典类型。

    但是在 pep0416 中,还提到了几种其他方法,PyDictProxy_New 等。下面来试一下






    #### 2. [pythonapi.PyDictProxy_New]( http://code.activestate.com/recipes/576540/)

    在官方介绍 capi 的 PyDictProxy_New 中,使用代理模式,代理使用字典。并且拦截了字典的修改请求。介绍如下

    ```
    PyObject* PyDictProxy_New(PyObject *mapping)¶
    Return value: New reference.
    Return a types.MappingProxyType object for a mapping which enforces read-only behavior. This is normally used to create a view to prevent modification of the dictionary for non-dynamic class types.
    ```
    意思就是你传入个 dict,这个函数返回一个 dict (其实是 types.MappingProxyType ),然后这个返回的 dict 就不可以修改啦。是不是很简单,代码实现如下

    ```

    #!/usr/bin/env python
    # -*- coding: UTF-8 -*-


    from ctypes import pythonapi, py_object

    PyDictProxy_New = pythonapi.PyDictProxy_New
    PyDictProxy_New.argtypes = (py_object,)
    PyDictProxy_New.restype = py_object

    def make_dictproxy(obj):
    assert isinstance(obj, dict)
    return pythonapi.PyDictProxy_New(obj)
    a={'a': 'b', 'c': 'd'}
    d = make_dictproxy(a)
    ```

    这是如果修改的话,则会提示 TypeError: 'mappingproxy' object does not support item assignment。同样达到了要求。这种方法的弊端主要在于依赖特定的平台,只能适用于 cpython。而上面那种则适用于所有平台,cpython,pypy 等。




    #### 3 .class types.MappingProxyType(mapping)
    这种方法其实于 PyDictProxy_New 一样,只不过在 py3.3 中才实现。
    代码如下

    ```
    from types import MappingProxyType

    def make_dictproxy(obj):
    assert isinstance(obj, dict)
    return MappingProxyType(obj)
    ```
    4 条回复    2018-07-27 09:19:15 +08:00
    xpresslink
        1
    xpresslink  
       2018-07-26 11:45:03 +08:00
    建议考虑一下
    from UserDict import UserDict # py2
    from collections import UserDict # py3

    class MyDict(UserDict)
    KeatingSmith
        2
    KeatingSmith  
       2018-07-27 08:44:17 +08:00 via iPhone
    from types import MappingProxyType 可以将字典封装为只读

    或者

    继承 UserDict,实现 __setitem__ 方法
    uwh0am1
        3
    uwh0am1  
    OP
       2018-07-27 09:13:49 +08:00
    @KeatingSmith 😂我也是 MappingProxyType
    KeatingSmith
        4
    KeatingSmith  
       2018-07-27 09:19:15 +08:00
    @uwh0am1

    哈哈,巧了,昨晚看《流畅的 Python 》 刚好看到。
    关于   ·   帮助文档   ·   博客   ·   API   ·   FAQ   ·   实用小工具   ·   1013 人在线   最高记录 6679   ·     Select Language
    创意工作者们的社区
    World is powered by solitude
    VERSION: 3.9.8.5 · 23ms · UTC 21:22 · PVG 05:22 · LAX 13:22 · JFK 16:22
    Developed with CodeLauncher
    ♥ Do have faith in what you're doing.