2013-03-02 80 views
2

我寫了一個人工生命模擬。每個生物都是我定義的「動物」類的一個對象,具有一些屬性。我定義了一個函數「重現」 Animal類以外:對python中的對象應用函數

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])那不會發生。是什麼原因?

謝謝!

+4

我想你將需要發佈'動物'的代碼。 – 2013-03-02 22:53:23

+4

只是一個猜測,但也許'brain.w'或'brain.ix'是列表的列表?然後'w [​​:]'給你一個新的列表,其中仍然包含相同的舊子列表。 – 2013-03-02 22:55:56

+4

使用[:]應該製作父腦的副本。你能發佈更多的代碼嗎?就我個人而言,我會重現()一個動物類的方法。 – Alex 2013-03-02 22:57:23

回答

2

基於@ Armin的評論的另一刺。這確實展覽相關deepcopy的行爲:

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 

也就是說,兩個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) 
+0

謝謝你的回答。但爲什麼我不能將狀態存儲爲列表列表?爲什麼複製這樣的列表時出現問題? – user1767774 2013-03-03 04:50:42

+0

我將擴大答案了一下... – 2013-03-03 10:29:16

+0

我還建議您選中正確的答案的兩個。 – 2013-03-03 10:50:12

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其他屬性的類似問題,但這可能是讀者的練習。