0

python にはかなり新しく、python クラスには非常に新しいです。質問は少し複雑です。あなたの忍耐に最も感謝します:

私はクラス「スター」を持っています。簡単です。属性 x、v、および質量。別のクラスである Galaxy には、星のオブジェクトの単なるリストである "stars" 属性があります。

class Galaxy:

    numstars=0.
    stars=[]

    def __init__(self,numin,xes,vees,masses):
        self.numstars=numin
        for n in range(numin):
            self.stars.append(Star(xes[n],vees[n],masses[n]))

Galaxy には、time_stepper という属性関数もあります。time_stepper は単に「星」のすべての要素を更新してから、「星」を返すと言えば十分です。

def time_stepper(self,dt):
    self.velstep(dt)
    self.xstep(dt)
    self.velstep(dt)
    return(self.stars)

今、私はこれを動かして、「星」のさまざまな更新を「履歴」と呼ばれるリストに保存しようとしています:

gal=Galaxy(#stuff#)
history=[]
for n in range(100):
    history.append(gal.time_stepper(.1))

最後に、私の質問: このループの各反復で、「星」の新しい要素が「歴史」に追加されますが、...そしてここにあります...歴史の以前の要素はすべて上書きされ、歴史の最新要素と同じ価値! それで、何が起こっているのですか?以前は理解できなかった Python リストに関する問題に出くわしましたが、ようやく解決したと思いました。どうやらそうではありません。ご協力いただきありがとうございます。

補遺: 皆さん、ご協力ありがとうございます。多くの役立つ返信があり、特にすぐに返されるとは思っていませんでした。私の問題は、これら 2 つのコードが本質的に同じであると想定していたことです。初め:

>>> a=[]
>>> b=[1,2,3]
>>> a.append(b)
>>> b=[4,5,6]
>>> a.append(b)
>>> a
[[1, 2, 3], [4, 5, 6]]

2番:

>>> a=[]
>>> b=[1,2,3]
>>> a.append(b)
>>> b[:]=(4,5,6)
>>> b
[4, 5, 6]
>>> a.append(b)
>>> a
[[4, 5, 6], [4, 5, 6]]

そしておっと!そうではありません。したがって、コード 1 では、a[0] が古い b を指し続けている間、b は完全に新しいメモリ位置に「再ポイント」されていると思います。2 番目では、b のメモリが「編集」され、a[0] はまだその場所を指しています。追加後、a[1] もその場所を指しています。私は今それを持っていますか?

私はpythonに非常に慣れていないので、まだ「pythonic」の哲学を理解しています。しかし、私にとって、ポインターの再割り当ては簡単に行われますが、より複雑な方法で行われる「ディープコピー」は、私が通常やりたい方法とは逆のようなものです。誰でも私を啓発できますか?再度、感謝します。

4

3 に答える 3

5

銀河が1つ以上ある場合、それらは同じ星を共有することに注意する価値があると思います。これは、あなたが本当にそうではないときにあなたがあなたの星を上書きしているとあなたに信じさせるかもしれません...

私はあなたがおそらく次のようなものでより良いだろうと推測して__init__います:

def __init__(self,numin,xes,vees,masses):
    self.stars = []
    self.numstars = numin
    for n in range(numin):
        self.stars.append(Star(xes[n],vees[n],masses[n]))

ここでは、クラス属性 starsインスタンス属性にシフトしました。これで、各インスタンスは、1つのリストを宇宙の他のすべての銀河とstars共有するのではなく、独自のリストを持つようになります。stars

他の人が指摘しているように、あなたのhistoryリストも同様の問題を抱えています(同じリストへの複数の参照があります)。ただし、修正は実際にはとで何をするかに依存しself.velstepますself.xstep。所定の場所でオブジェクトを変更するとStar、単純な(浅い)リストのコピーでは何の役にも立ちません(例gal.time_stepper(0.1)[:])。(新しいリストを作成しますが、常に更新されている同じ星を保持します)。その場合、copy.deepcopy履歴リストに追加するときに必要になります。

history=[]
for n in range(100):
    history.append(copy.deepcopy(gal.time_stepper(.1)))
于 2012-11-20T21:48:43.073 に答える
4

これは、starsリストが変更可能であるためです。これは変更可能な変数です。を呼び出すとstars.append、新しいリストは作成されません。既存のリストを編集するだけです。また、を呼び出すとhistory.append(x)、Pythonは新しいクリーンなコピーを作成しません。xこれは、「実際の」配列xを配置することを前提としています。history

各反復でリストの状態をコピーする場合は、いくつかのオプションがあります(Pythonでリストのクローンを作成する方法を参照してください)。

copyモジュールがそのトリックを実行します。これは、「浅い」コピーと「深い」コピーの両方を提供します。大まかに言えば、深いコピーは、オブジェクト内で見つかった他の変数(たとえば、他のリストを含むリスト)もコピーしようとしますが、浅いコピーは1つ下のレイヤーに移動します。

これが浅いコピーcopyです:

import copy

history=[]
for n in range(100):
    history.append(copy.copy(gal.time_stepper(.1)))

そして深いコピー:

import copy

history=[]
for n in range(100):
    history.append(copy.deepcopy(gal.time_stepper(.1)))

配列のスライスは、中間ステップとして常に浅いコピーであるため、機能します。

history = []
for n in range(100):
    history.append(gal.time_stepper(.1)[:])

既存のリストを呼び出すこともできますlist。このtypecast風の操作は、常に新しいリストオブジェクトを返します(これも浅く)。

history = []
for n in range(100):
    history.append(list(gal.time_stepper(.1)))
于 2012-11-20T21:47:14.847 に答える
1

すべての値を変更したり上書きしたりするのではなく、履歴内のすべての要素が同じ配列オブジェクトを指します。配列のコピーを使用して参照するのではなく、値で割り当てる必要があります。

于 2012-11-20T21:47:43.867 に答える