1

我正在尝试用 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[:] = []
4

2 回答 2

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()
于 2012-11-03T01:39:31.720 回答
1

当您创建一个类时,您实际上是在创建一个“类对象”。这些对象就像 Python 中的任何其他对象一样;Python 中的一切都是一个对象,这些对象的作用是由它们的方法定义的,而不是它们的类!这就是鸭子打字的魔力。在 Python 中,您甚至可以动态创建新类。

无论如何,您正在将一个列表对象添加到唯一的“个人”类对象的“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”对象中,它覆盖了类属性。

希望这可以帮助!

于 2012-11-03T05:06:04.193 回答