复制字典和在SQLAlchemy ORM对象上使用deepcopy时出现问题

时间:2010-06-03 23:13:49

标签: python sqlalchemy deep-copy simulated-annealing

我正在进行模拟退火算法,以优化给定的学生和项目分配。

这是来自维基百科的与语言无关的伪代码:

s ← s0; e ← E(s)                                // Initial state, energy.
sbest ← s; ebest ← e                            // Initial "best" solution
k ← 0                                           // Energy evaluation count.
while k < kmax and e > emax                     // While time left & not good enough:
  snew ← neighbour(s)                           // Pick some neighbour.
  enew ← E(snew)                                // Compute its energy.
  if enew < ebest then                          // Is this a new best?
    sbest ← snew; ebest ← enew                  // Save 'new neighbour' to 'best found'.
  if P(e, enew, temp(k/kmax)) > random() then   // Should we move to it?
    s ← snew; e ← enew                          // Yes, change state.
  k ← k + 1                                     // One more evaluation done
return sbest                                    // Return the best solution found.

以下是该技术的改编。我的主管说这个想法在理论上很好。

首先,我从整套随机分配中选取一些分配(即整个学生词典及其分配的项目,包括项目的等级),复制并传递给我的函数。我们称这个分配aOld(它是一本字典)。 aOld有一个与之相关的权重wOld。加权描述如下。

该功能执行以下操作:

  • 请将此分配aOld设为best_node
  • 从所有学生中,随机挑选一批学生并坚持列表
  • 剥离(DEALLOCATE)他们的项目++反映了项目的变化(allocated参数现在是False)和讲师(如果他们的一个或多个项目不再被分配,则可以腾出空位)
  • 随机列出
  • 尝试再次分配(REALLOCATE)该列表项目中的每个人
  • 计算权重(加上排名,排名1 = 1,排名2 = 2 ......且没有项目排名= 101)
  • 对于此新分配aNew,如果权重wNew小于我在开头选择的分配权重wOld,那么这就是best_node(如由上面的Simulated Annealing算法定义。将算法应用于aNew并继续。
  • 如果wOld < wNew,请再次将算法应用于aOld并继续。

分配/数据点表示为“节点”,使得node = (weight, allocation_dict, projects_dict, lecturers_dict)

现在,我只能执行一次此算法,但我需要尝试使用维基百科代码段中的数字N(由kmax表示),并确保我始终拥有,前一个{ {1}}和node

因此我不修改我原来的词典(我可能想要重置为),我已经完成了词典的浅层复制。从我在文档中看到的内容来看,它似乎只复制引用,因为我的字典包含对象,更改复制的字典最终会更改对象。所以我尝试使用best_node。这些词典引用已用SQLA映射的对象。


copy.deepcopy()

我已经为所面临的问题提供了一些解决方案,但是由于我使用Python进行了übergreen-ness,它们对我来说听起来相当神秘。

  1. Deepcopy与SQLA的效果不佳。我被告知ORM对象上的复制可能存在阻碍它按预期工作的问题。显然我会更好地“构建复制构造函数,即def copy(self):返回FooBar(....)。” 有人可以解释一下这意味着什么吗?

  2. 我查了一下,发现Questions:有问题因为SQLAlchemy在你的对象上放置了额外的信息,即deepcopy属性,我不希望在副本中但是对于要拥有的对象。我被告知:“有一些方法可以手动吹掉旧的_sa_instance_state并在对象上添加一个新的,但最简单的方法是使用_sa_instance_state创建一个新对象并设置属性很重要,而不是做一个完整的深层复制。“ 究竟是什么意思?我是否创建了一个类似于旧的映射类的新的未映射类?

  3. 另一种解决方案是我必须“对你的对象实现__init__()并确保设置一个新的_sa_instance_state,sqlalchemy.orm.attributes中有一些函数可以帮助你“。 再一次,这超出了我,所以有人可以解释这意味着什么?

  4. 一个更一般的问题:鉴于以上信息,我对如何维护__deepcopy__()的信息/状态(必须始终通过我的while循环保持)和{{1 ,如果我的实际对象(由字典引用,因此节点)由于重新分配/重新分配而发生变化?也就是说,不使用副本?

2 个答案:

答案 0 :(得分:2)

我有另一种可能的解决方案:使用交易。这可能仍然不是最佳解决方案,但实施它应该更快。

首先按照以下方式创建会话:

# transactional session
Session = sessionmaker(transactional=True)
sess = Session()

这样会是交易性的。交易的工作方式是sess.commit()会使您的更改成为永久更改,而sess.rollback()会将其还原。

在模拟退火的情况下,您希望在找到新的最佳解决方案时提交。在以后任何时候,您都可以调用rollback()将状态恢复到该位置。

答案 1 :(得分:0)

您不希望像这样复制sqlalchemy对象。您可以实现自己的方法,使副本足够容易,但这可能不是您想要的。您不希望数据库中的学生和项目副本吗?所以不要复制那些数据。

所以你有一本包含你的分配的字典。在此过程中,您永远不应该修改SQLAlchemy对象。可以修改的所有信息都应存储在这些词典中。如果您需要修改对象以将其考虑在内,请在最后复制数据。