轻轻吐槽下python的deepcopy居然一点不deep

2013-05-16 22:59:39 +08:00
 banxi1988
在我看来一个对象的copy至少也得改小引用吧。
但python的不是。
sqlalchemy
我是这样做的:
wendy = session.query(User).filter_by(name='wendy').one()
In [3]: wendy
Out[3]: <User('wendy','Wendy Williams','foobar')>
In [7]: import copy

In [10]: clone_user = copy.deepcopy(wendy)
In [11]: clone_user
Out[11]: <User('wendy','Wendy Williams','foobar')>

In [12]: clone_user.name='clone_wendy'

In [13]: clone_user
Out[13]: <User('clone_wendy','Wendy Williams','foobar')>

In [14]: wendy
Out[14]: <User('wendy','Wendy Williams','foobar')>

In [15]: session.add(clone_user)
-------------------------------------
然后出错了。。。

/Users/yautou/work/python/youjia/ENV/lib/python2.7/site-packages/sqlalchemy/orm/identity.pyc in add(self, state)
117 "A conflicting state is already "
118 "present in the identity map for key %r"
--> 119 % (key, ))
120 else:
121 return

AssertionError: A conflicting state is already present in the identity map for key (<class '__main__.User'>, (1,))
4523 次点击
所在节点    Python
7 条回复
Esay
2013-05-17 09:06:50 +08:00
怀疑是不是wendy的主键也被复制给了clone_user,你插入的时候,主键重复了,所以会这样。
yetone
2013-05-17 10:35:34 +08:00
主键重复
lqs
2013-05-17 12:13:16 +08:00
sqlalchemy的session机制就是为了将多个同样的实例只在内存里留一份,然后被deepcopy了它就没法处理了
yueyoum
2013-05-17 19:43:47 +08:00
LZ是不是感到python很好学?

结果什么都不知道吧

__deepcopy__
banxi1988
2013-05-17 22:28:01 +08:00
@Esay
@yetone
你们这么一说我试了下,但还是会抱错:
In [15]: ed = session.query(User).filter_by(name='ed').one()
In [22]: import copy
In [23]: clone_ed = copy.deepcopy(ed)
In [24]: clone_ed
Out[24]: <User('ed','Ed Jones','edspassword')>

In [25]: clone_ed.id
Out[25]: 4

In [26]: ed.id
Out[26]: 4

In [27]: clone_ed.id = None

In [28]: clone_ed.name = 'clone ed'

In [29]: session.add(clone_ed)
---------------------------------------------------------------------------
AssertionError Traceback (most recent call last)
<ipython-input-29-c3ecb2f64abe> in <module>()
----> 1 session.add(clone_ed)

/Users/yautou/work/python/youjia/ENV/lib/python2.7/site-packages/sqlalchemy/orm/session.pyc in add(self, instance, _warn)
1396 raise exc.UnmappedInstanceError(instance)
1397
-> 1398 self._save_or_update_state(state)
1399
1400 def add_all(self, instances):

/Users/yautou/work/python/youjia/ENV/lib/python2.7/site-packages/sqlalchemy/orm/session.pyc in _save_or_update_state(self, state)
1408
1409 def _save_or_update_state(self, state):
-> 1410 self._save_or_update_impl(state)
1411
1412 mapper = _state_mapper(state)

/Users/yautou/work/python/youjia/ENV/lib/python2.7/site-packages/sqlalchemy/orm/session.pyc in _save_or_update_impl(self, state)
1664 self._save_impl(state)
1665 else:
-> 1666 self._update_impl(state)
1667
1668 def _delete_impl(self, state):

/Users/yautou/work/python/youjia/ENV/lib/python2.7/site-packages/sqlalchemy/orm/session.pyc in _update_impl(self, state, discard_existing)
1657 self.identity_map.replace(state)
1658 else:
-> 1659 self.identity_map.add(state)
1660 self._attach(state)
1661

/Users/yautou/work/python/youjia/ENV/lib/python2.7/site-packages/sqlalchemy/orm/identity.pyc in add(self, state)
117 "A conflicting state is already "
118 "present in the identity map for key %r"
--> 119 % (key, ))
120 else:
121 return

AssertionError: A conflicting state is already present in the identity map for key (<class '__main__.User'>, (4,))

In [30]: clone_ed
Out[30]: <User('clone ed','Ed Jones','edspassword')>

In [31]: clone_ed.id

In [32]:



@yueyoum 我google过,但是我觉得还让我去实现个__deepcopy__ 我不如直接自己写一个clone()
方法。


@lqs 判断同样实例的是?id我上面测了,不是的。
yuelang85
2013-05-17 22:58:27 +08:00
楼主看清报错信息,说得很明白了,主键重复。

请不要用None做试验。。。。
lqs
2013-05-17 23:51:44 +08:00
@banxi1988 在 session.identity_map 保存了所有的对象。

lone_ed.id = None 这句的意思是『下次保存时把 id 从 4 改成 None 』,而不是『建立一个新的对象』。sqlalchemy会记住『这个对象的 id 以前是 4』。

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

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

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

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

© 2021 V2EX