2

人工生命シミュレーションを書きました。各クリーチャーは、私が定義したクラス「動物」のオブジェクトであり、いくつかのプロパティがあります。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])発生しません。理由は何ですか?

ありがとう!

4

2 に答える 2

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、それは読者にとっては演習になる可能性があります。

于 2013-03-02T23:56:50.467 に答える
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モジュールを使用しない場合)。ドキュメントはこれをうまくカバーしています。簡単に言えば、これは次のことが当てはまることを意味します。

>>> 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-03T00:29:48.573 に答える