我写了一个人工生命模拟。每个生物都是我定义的“动物”类的一个对象,具有一些属性。我在Animal类之外定义了一个“重现”的函数:
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])
但未发生时。是什么原因?
谢谢!
答案 0 :(得分:2)
编辑:与初始断言相反,以下内容未显示我在描述中遗漏的深度复制行为。不过,我会留下它以防万一有用。
有点猜测,但以下展示了您遇到的行为:
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
的其他属性类似的问题,但这可能是读者的练习。
答案 1 :(得分:2)
基于@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很好地掩盖了这一点。 Tersely,这意味着以下情况属实:
>>> 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)