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

Python 中字典经过 redis 后添加修改都会出现问题

  •  1
     
  •   fxxkgw · 2016-03-20 15:09:53 +08:00 · 4202 次点击
    这是一个创建于 3173 天前的主题,其中的信息可能已经有所发展或是发生改变。
    我手头有个复杂的字典,需要保存在 redis 中,例如:
    appinfo = {
    'a': {'172.25.53.12': {'olversion': u'111', 'rb1': u'222', 'rb2': u'333'}},
    'c': {'172.25.53.14': {'olversion': u'111', 'rb1': u'222', 'rb2': u'333'}},
    'b': {'172.25.53.11': {'olversion': u'111', 'rb1': u'222', 'rb2': u'333'}},
    'd': {'172.25.53.13': {'olversion': u'111', 'rb1': u'222', 'rb2': u'333'}}}
    保存方法如下:
    class operate_redis:
    def __init__(self):
    self.r=redis.Redis(host='localhost', port=6379, db=0)

    def write2redis(self, key, **kwargs):
    self.r.set(key, pickle.dumps(kwargs))

    def read_from_redis(self, key):
    pickled_value = self.r.get(key)
    if pickled_value is None:
    return None
    return pickle.loads(pickled_value)

    def del_from_redis(self, key):
    return self.r.delete(key)

    def set_app_expire(self, key):
    self.r.expire(key, 36000)

    问题出现在我每次通过 read_from_redis 读取后,想在 key 为 a 的添加个参数
    appinfo['a']['172.25.53.12']['work'] = 'gofuckyourself'
    这个操作后, key 为'b', 'c', 'd'的同样会增加上面那个 key value
    同理 修改 appinfo['a']['172.25.53.12']这个里面的参数 b c d 也会一样被修改

    如果不经过 redis 存储 读取这个流程 单纯定义 appinfo 这个字典,上述修改只只对单个 key 生效。

    请问这个情况有大大们碰到过么? 改如果处理呢?
    15 条回复    2016-03-20 22:00:50 +08:00
    mongost3t
        1
    mongost3t  
       2016-03-20 15:46:44 +08:00
    为什么要用 pickle
    fxxkgw
        2
    fxxkgw  
    OP
       2016-03-20 16:05:19 +08:00
    @mongost3t
    如果用 hmset 方式的话,那 hgetall 取出来后,第二层 也就是 key 为 ip 的那些字典会被默认为 str 的,需要每个判断每个转化 所以采用了 pickle 取出后从里到外都是字典了
    TheCure
        3
    TheCure  
       2016-03-20 17:46:25 +08:00
    感觉是 pickle 的问题
    换 cpickle/pickle 或者版本试试?
    ethego
        4
    ethego  
       2016-03-20 17:55:17 +08:00
    dump 成 json 啊
    fxxkgw
        5
    fxxkgw  
    OP
       2016-03-20 18:08:41 +08:00
    @callofmx
    换了 cpickle 也一样
    我用了 pickle 写文件方式 再读取不会出现 redis 中的问题
    #!/usr/bin/env python

    import pickle


    appinfo = {
    'a': {'172.25.53.12': {'olversion': u'111', 'rb1': u'222', 'rb2': u'333'}},
    'c': {'172.25.53.14': {'olversion': u'111', 'rb1': u'222', 'rb2': u'333'}},
    'b': {'172.25.53.11': {'olversion': u'111', 'rb1': u'222', 'rb2': u'333'}},
    'd': {'172.25.53.13': {'olversion': u'111', 'rb1': u'222', 'rb2': u'333'}}}

    #op = operate_redis()
    #appinfo = op.read_from_redis('5006')

    p = pickle.dumps(appinfo)

    with open('test.txt', 'wb+') as f:
    f.write(p)
    f.seek(0)
    info = pickle.loads(f.read())

    print "==========="
    print "==========="

    info['a']['172.25.53.12']['rb1'] = 'gofuckyourself' #只修改 a 对应的 b c d 中 rb1 不变

    print info
    timonwong
        6
    timonwong  
       2016-03-20 18:17:08 +08:00
    > pickle stores such objects only once, and ensures that all other references point to the master copy. Shared objects remain shared, which can be very important for mutable objects.

    需要确认的是,你的 appinfo[x][ip] 是不是指向的同一个 object?
    fxxkgw
        7
    fxxkgw  
    OP
       2016-03-20 18:17:30 +08:00
    @ethego
    谢谢!
    把 pickle 换成 json 后 确实可以了。。。
    能解释下原因么?
    mongost3t
        8
    mongost3t  
       2016-03-20 18:21:58 +08:00
    @fxxkgw 因为 python dict 是 mutable object ,存的是引用不是值。。
    ethego
        9
    ethego  
       2016-03-20 18:44:45 +08:00
    @fxxkgw 对于 python 的 dict 来说,你引用了同一个对象,添加的时候自然全部都会变化了。
    fxxkgw
        10
    fxxkgw  
    OP
       2016-03-20 18:47:42 +08:00
    @timonwong
    @mongost3t
    这个我理解 dict list 都是可变的 赋值时候如果不是深拷贝会互相改变
    但是我还是没明白 在同一个字典内 改变 a 为什么 bcd 都会跟着变 那么说在 redis 内存储时候因为 a b c d 值相同 所以用的是同一个地址?
    我测试了下 一个字典,在本地 Python 下
    a={'x':1,
    'b':{'c':{'d':1,'e':2}},
    'f':{'g':{'d':1,'e':2}}
    }
    >>> id(a['b']['c'])
    9040448
    >>> id(a['f']['g'])
    9085376
    虽然值相同,但是存储的地址不同

    但是 a 经过 redis 的 pickle 方式存储后
    >>> id(a['b']['c'])
    31571776
    >>>
    >>> id(a['f']['g'])
    31571776

    地址是相同的,所以会造成那个问题。
    jamiesun
        11
    jamiesun  
       2016-03-20 18:48:48 +08:00
    copyonwrite
    fxxkgw
        12
    fxxkgw  
    OP
       2016-03-20 18:50:33 +08:00
    @ethego
    我还是觉得这个和 Python 的 dict 没关系 是 redis 模块处理的问题。
    mulog
        13
    mulog  
       2016-03-20 19:15:32 +08:00
    @fxxkgw redis 只知道你存进去的 pickle dump 出来的字符串,你取的时候把字符串还给你,这个锅人家不背。。
    Zzzzzzzzz
        14
    Zzzzzzzzz  
       2016-03-20 19:29:03 +08:00   ❤️ 1
    redis 膝盖中枪

    你传给 appinfo['a']['172.25.53.12']和 appinfo['a']['172.25.53.13']肯定是同一个 dict 变量, pickle 是 python 本身的序列化, 可以标识引用, 结果就这样, json 没办法标识引用, 只好复制, 变相间接解决了你的问题, 我给你复现几种情况吧.

    fxxkgw
        15
    fxxkgw  
    OP
       2016-03-20 22:00:50 +08:00
    @Zzzzzzzzz 确实是这样 非常感谢
    关于   ·   帮助文档   ·   博客   ·   API   ·   FAQ   ·   实用小工具   ·   3078 人在线   最高记录 6679   ·     Select Language
    创意工作者们的社区
    World is powered by solitude
    VERSION: 3.9.8.5 · 45ms · UTC 00:35 · PVG 08:35 · LAX 16:35 · JFK 19:35
    Developed with CodeLauncher
    ♥ Do have faith in what you're doing.