我写了一个人工生命模拟。每个生物都是我定义的"Animal“类的一个对象,具有一些属性。我在Animal类的外部定义了一个函数"reproduce“:
def reproduce(parent):
child = Animal()
child.brain.w= parent.brain.w[:]
child.brain.ix= parent.brain.ix[:]
child.x,child.y = random.randint(0,width),random.randint(0,height)
child.age = 0
child.fitness= 9 + parent.fitness/10 #parent.fitness/2
mutation = random.choice([0,1,1,1,1,1,1,1,1,2,3,4,5])
for b in range(mutation):
child.brain.mutate()
animals.append(child)可以看出,每个动物都有一个大脑,它是来自不同类的对象:我为每个动物定义了animals[i].brain = Brain()。复制功能中的“变异”部分确保了孩子的大脑与父母的大脑不一样。
然而,问题是,当我将这个函数应用于列表中的一些动物时,孩子确实获得了稍微新的大脑,但父母的大脑变得与孩子的新大脑完全相同。当我使用reproduce(copy.deepcopy(animals[i]))而不是reproduce(animals[i])时,这种情况不会发生。原因何在?
谢谢!
发布于 2013-03-03 08:29:49
基于@Armin的评论的另一次刺杀。这确实展示了相关的深度复制行为:
import random
width = 5
height = 5
class Brain(object):
def __init__(self):
self.w = [[1]]
self.ix = [[1]]
def mutate(self):
self.w[0].append(1)
class Animal(object):
def __init__(self):
self.brain = Brain()
self.x = random.randint(0, width)
self.y = random.randint(0, height)
self.age = 0
self.fitness = 10
def reproduce(parent):
child = Animal()
child.brain.w= parent.brain.w[:]
child.brain.ix= parent.brain.ix[:]
child.x,child.y = random.randint(0,width),random.randint(0,height)
child.age = 0
child.fitness= 9 + parent.fitness/10 #parent.fitness/2
mutation = random.choice([0,1,1,1,1,1,1,1,1,2,3,4,5])
for b in range(mutation):
child.brain.mutate()
animals.append(child)
animals = []
parent = Animal()
animals.append(parent)
print parent.brain.w
#reproduce(parent)
import copy
reproduce(copy.deepcopy(parent))
for each in animals:
print each.brain.w这里的修复方法是不将状态值存储在您在对象之间复制的可变类型中;在本例中是一个列表,但它可以是任何可变对象。
编辑:您在原始代码中所做的是将parent.brain.w的内容复制到child.brain.w中。Python的属性是赋值给原始对象,而不是对象或内容的副本(除非使用copy模块)。The docs很好地介绍了这一点。简而言之,这意味着以下情况是正确的:
>>> a = [1, 2, 3, 4, 5]
>>> b = a
>>> b.append(6)
>>> b
[1, 2, 3, 4, 5, 6]
>>> a
[1, 2, 3, 4, 5, 6]
>>> a is b
True也就是说,a和b是同一个列表。这并不是您正在做的事情;您正在将一个列表复制到一个对象中,但这是等效的:
>>> a = [[1, 2, 3]]
>>> b = []
>>> b = a[:] # What you are doing
>>> b is a
False
>>> b[0] is a[0]
True
>>> b[0].append(4)
>>> b[0]
[1, 2, 3, 4]
>>> a[0]
[1, 2, 3, 4]如果您的类型不是可变的,那么当您修改它时,将创建一个新对象。例如,考虑在某种程度上等价的元组列表(它们是不可变的):
>>> a = [(1, 2, 3)]
>>> b = []
>>> b = a[:]
>>> b is a
False
>>> b[0] is a[0] # Initially the objects are the same
True
>>> b[0] += (4,) # Now a new object is created and overwrites b[0]
>>> b[0] is a[0]
False
>>> b[0]
(1, 2, 3, 4)
>>> a[0]
(1, 2, 3)发布于 2013-03-03 07:56:50
编辑:与最初的断言相反,下面的内容没有展示我在描述中遗漏的深度复制行为。不过,我还是会把它保留下来,以防有什么有用的东西。
有一点猜测,但下面展示了您遇到的行为:
import random
width = 5
height = 5
class Brain(object):
def __init__(self):
self.w = [1]
self.ix = [1]
self.state = 1
def mutate(self):
self.state += 1
class Animal(object):
brain = Brain()
x = random.randint(0, width)
y = random.randint(0,height)
age = 0
fitness = 10
def reproduce(parent):
child = Animal()
child.brain.w= parent.brain.w[:]
child.brain.ix= parent.brain.ix[:]
child.x,child.y = random.randint(0,width),random.randint(0,height)
child.age = 0
child.fitness= 9 + parent.fitness/10 #parent.fitness/2
mutation = random.choice([0,1,1,1,1,1,1,1,1,2,3,4,5])
for b in range(mutation):
child.brain.mutate()
animals.append(child)
animals = []
parent = Animal()
animals.append(parent)
print parent.brain.state
reproduce(parent)
for each in animals:
print each.brain.state当你运行它的时候,你会得到:
1
2
2如果是这样,问题是因为animal.brain是类的属性,而不是实例的属性。这样做的结果是所有实例都共享该属性。
这很容易纠正:
class Animal(object):
x = random.randint(0, width)
y = random.randint(0,height)
age = 0
fitness = 10
def __init__(self):
self.brain = Brain()有了这个修复,您将为父级输出正确的值,并且子级将发生变化。例如:
1
1
3(根据mutate()被调用的次数)。
对于Brain的其他属性,您可能也会遇到类似的问题,但这对读者来说可能是一个练习。
https://stackoverflow.com/questions/15180125
复制相似问题