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

2016-03-20 15:09:53 +08:00
 fxxkgw
我手头有个复杂的字典,需要保存在 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 生效。

请问这个情况有大大们碰到过么? 改如果处理呢?
4193 次点击
所在节点    Python
15 条回复
mongost3t
2016-03-20 15:46:44 +08:00
为什么要用 pickle
fxxkgw
2016-03-20 16:05:19 +08:00
@mongost3t
如果用 hmset 方式的话,那 hgetall 取出来后,第二层 也就是 key 为 ip 的那些字典会被默认为 str 的,需要每个判断每个转化 所以采用了 pickle 取出后从里到外都是字典了
TheCure
2016-03-20 17:46:25 +08:00
感觉是 pickle 的问题
换 cpickle/pickle 或者版本试试?
ethego
2016-03-20 17:55:17 +08:00
dump 成 json 啊
fxxkgw
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
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
2016-03-20 18:17:30 +08:00
@ethego
谢谢!
把 pickle 换成 json 后 确实可以了。。。
能解释下原因么?
mongost3t
2016-03-20 18:21:58 +08:00
@fxxkgw 因为 python dict 是 mutable object ,存的是引用不是值。。
ethego
2016-03-20 18:44:45 +08:00
@fxxkgw 对于 python 的 dict 来说,你引用了同一个对象,添加的时候自然全部都会变化了。
fxxkgw
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
2016-03-20 18:48:48 +08:00
copyonwrite
fxxkgw
2016-03-20 18:50:33 +08:00
@ethego
我还是觉得这个和 Python 的 dict 没关系 是 redis 模块处理的问题。
mulog
2016-03-20 19:15:32 +08:00
@fxxkgw redis 只知道你存进去的 pickle dump 出来的字符串,你取的时候把字符串还给你,这个锅人家不背。。
Zzzzzzzzz
2016-03-20 19:29:03 +08:00
redis 膝盖中枪

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

fxxkgw
2016-03-20 22:00:50 +08:00
@Zzzzzzzzz 确实是这样 非常感谢

这是一个专为移动设备优化的页面(即为了让你能够在 Google 搜索结果里秒开这个页面),如果你希望参与 V2EX 社区的讨论,你可以继续到 V2EX 上打开本讨论主题的完整版本。

https://www.v2ex.com/t/264929

V2EX 是创意工作者们的社区,是一个分享自己正在做的有趣事物、交流想法,可以遇见新朋友甚至新机会的地方。

V2EX is a community of developers, designers and creative people.

© 2021 V2EX