在python中对对象应用函数

时间:2013-03-02 22:23:55

标签: python class artificial-life

我写了一个人工生命模拟。每个生物都是我定义的“动物”类的一个对象,具有一些属性。我在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])但未发生时。是什么原因?

谢谢!

2 个答案:

答案 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

也就是说,ab都是相同的列表。那不是你在做什么的;您将列表复制到对象中,但这是等效的:

>>> 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)