具有浅连接的Python类实例的问题

时间:2012-11-03 00:40:57

标签: python python-2.7 deep-copy shallow-copy

我正在尝试用Python编写遗传算法框架,并且遇到了浅/深复制的问题。我的背景主要是C / C ++,我很难理解这些连接是如何持续存在的。

我所看到的是子类中属性列表的长度爆炸。我的代码如下......我会指出问题。

这是单个基因的类。本质上,它应该有一个名称,值和布尔标志。 Gene的实例填充了Individual类中的列表。

# gene class
class Gene():
  # constructor
  def __init__(self, name, is_float):
    self.name_     = name
    self.is_float_ = is_float
    self.value_    = self.randomize_gene()


  # create a random gene
  def randomize_gene(self):
    return random.random()

这是我的Individual课程。每一代,都会创建这些代码(我将在类声明后显示创建代码)并应用典型的遗传算法操作。值得注意的是print len(self.Genes_)调用,每次实例化此类时都会增长。

# individual class
class Individual():
  # genome definition
  Genes_      = []    # genes list
  evaluated_  = False # prevent re-evaluation
  fitness_    = 0.0   # fitness value (from evaluation)
  trace_      = ""    # path to trace file
  generation_ = 0     # generation to which this individual belonged
  indiv_      = 0     # identify this individual by number

  # constructor
  def __init__(self, gen, indv):
    # assign indices
    self.generation_ = gen
    self.indiv_      = indv
    self.fitness_    = random.random()

    # populate genome
    for lp in cfg.params_:
      g = Gene(lp[0], lp[1])
      self.Genes_.append(g)

    print len(self.Genes_)

> python ga.py
> 24
> 48
> 72
> 96
> 120
> 144
......

正如您所看到的,每个人应该有24个基因,但是这个群体爆发得非常快。 我创建了一个像这样的新个人的初始人口:

# create a randomized initial population
def createPopulation(self, gen):
  loc_population = []
  for i in range(0, cfg.population_size_):
    indv = Individual(gen, i)
    loc_population.append(indv)
  return loc_population

以及后来我的主循环(为整个转储道歉,但觉得有必要 - 如果需要我的二次调用(突变/交叉)请告诉我))

for i in range(0, cfg.generations_):
      # evaluate current population
      self.evaluate(i)

      # sort population on fitness
      loc_pop = sorted(self.population_, key=operator.attrgetter('fitness_'), reverse=True)

      # create next population & preserve elite individual
      next_population = []
      elitist = copy.deepcopy(loc_pop[0])
      elitist.generation_ = i
      next_population.append(elitist)

      # perform selection
      selection_pool = []
      selection_pool = self.selection(elitist)

      # perform crossover on selection
      new_children = []
      new_children = self.crossover(selection_pool, i)

      # perform mutation on selection
      muties = []
      muties = self.mutation(selection_pool, i)

      # add members to next population
      next_population = next_population + new_children + muties

      # fill out the rest with random
      for j in xrange(len(next_population)-1, cfg.population_size_ - 1):
        next_population.append(Individual(i, j))

      # copy next population over old population
      self.population_ = copy.deepcopy(next_population)

      # clear old lists
      selection_pool[:]  = []
      new_children[:]    = []
      muties[:]          = []
      next_population[:] = []

2 个答案:

答案 0 :(得分:1)

我不是完全确定我理解你的问题,但我怀疑你的问题是你的Individual()类中的Genes_变量是在类名称空间中声明的。该命名空间可供该类的所有成员使用。换句话说,Individual()的每个实例将共享相同的变量Genes _。

考虑以下两个片段:

class Individual():
  # genome definition
  genes = []
  def __init__(self):
      for i in xrange(10):
              self.genes.append(i)

ind_1 = Individual()
print ind_1.genes
ind_2 = Individual()
print ind_1.genes
print ind_2.genes

class Individual():
  # genome definition
  def __init__(self):
      self.genes = []
      for i in xrange(10):
              self.genes.append(i)

ind_1 = Individual()
print ind_1.genes
ind_2 = Individual()
print ind_1.genes
print ind_2.genes

第一个片段输出

>>> [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
>>> [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
>>> [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9]

而第二个片段输出

>>> [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
>>> [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
>>> [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]

在第一个场景中,当第二个Individual()被实例化时,基因列表变量已经存在,并且来自第二个体的基因被添加到现有列表中。

而不是像这样创建Individual()类,

# individual class
class Individual():
  # genome definition
  Genes_      = []    # genes list

  # constructor
  def __init__(self, gen, indv):
    # assign indices
    self.generation_ = gen
    self.indiv_      = indv
    self.fitness_    = random.random()

你应该考虑在 init 中声明Genes_变量,以便每个Individual()实例获得自己的基因集

# individual class
class Individual():

  # constructor
  def __init__(self, gen, indv):
    # genome definition
    self.Genes_      = []    # genes list
    # assign indices
    self.generation_ = gen
    self.indiv_      = indv
    self.fitness_    = random.random()

答案 1 :(得分:1)

当你创建一个类时,你实际上只创建了一个“类对象”。这些对象就像Python中的任何其他对象一样; Python中的所有东西都是一个对象,这些对象的作用是由它们的方法定义的,而不是它们的类!这就是鸭子打字的神奇之处。在Python中,您甚至可以动态地动态创建新类。

无论如何,您只将一个列表对象添加到一个且仅“Individuals”类对象的“Genes_”属性中。结果是“Individual”类对象的每个实例对象都访问相同的“Genes_”列表对象。

考虑一下

    # In 2.2 <= Python < 3.0 you should ALWAYS inherit from 'object'.
    class Foobar(object):
        doodah = []

    a = Foobar()
    b = Foobar()
    assert id(a.doodah) == id(b.doodah) # True

在这种情况下,正如你所看到的,“a.doodah”和“b.doodah”是同一个对象!

    class Foobar(object):
        def __init__(self):
            self.doodah = []

    a = Foobar()
    b = Foobar()
    assert id(a.doodah) != id(b.doodah) # True

在这种情况下,它们是不同的对象。

可以吃蛋糕也可以吃。考虑一下这个

    class Foobar(object):
        doodah = []

    a = Foobar()
    b = Foobar()
    a.doodah = 'hlaghalgh'
    assert id(a.doodah) != id(b.doodah) # True

在这种情况下,“doodah”属性被添加到“a”对象,该属性将覆盖类属性。

希望这有帮助!