6

次のコード ブロック 1 の結果が出力 2 ではなく出力 1 になる理由が本当にわかりません。

コードブロック 1:

class FruitContainer:
       def __init__(self,arr=[]):
           self.array = arr
       def addTo(self,something):
           self.array.append(something)
       def __str__(self):
           ret = "["
           for item in self.array:
               ret = "%s%s," % (ret,item)
           return "%s]" % ret

arrayOfFruit = ['apple', 'banana', 'pear']
arrayOfFruitContainers = []

while len(arrayOfFruit) > 0:
   tempFruit = arrayOfFruit.pop(0)
   tempB = FruitContainer()
   tempB.addTo(tempFruit)
   arrayOfFruitContainers.append(tempB)

for container in arrayOfFruitContainers:
   print container 

**Output 1 (actual):**
[apple,banana,pear,]
[apple,banana,pear,]
[apple,banana,pear,]

**Output 2 (desired):**
[apple,]
[banana,]
[pear,]

このコードの目的は、配列を反復処理し、それぞれを親オブジェクトにラップすることです。これは、すべてのリンゴをリンゴの袋などに追加する実際のコードを縮小したものです。私の推測では、何らかの理由で、同じオブジェクトを使用しているか、フルーツ コンテナーが静的配列を使用しているかのように動作していると思われます。これを修正する方法がわかりません。

4

4 に答える 4

8

メソッドのデフォルト引数には、変更可能な値 ([] など) を使用しないでください。値は 1 回計算され、呼び出しごとに使用されます。空のリストをデフォルト値として使用すると、以前の関数呼び出しによって値が変更された場合でも、メソッドが引数なしで呼び出されるたびに同じリストが使用されます。

代わりにこれを行います:

def __init__(self,arr=None):
    self.array = arr or []
于 2009-10-31T17:14:37.377 に答える
2

コードには、クラスを初期化するためのデフォルトの引数があります。default 引数の値はコンパイル時に 1 回評価されるため、すべてのインスタンスが同じリストで初期化されます。次のように変更します。

def __init__(self, arr=None):
    if arr is None:
        self.array = []
    else:
        self.array = arr

ここでこれについてより完全に議論しました:Python でクラスを定義する方法

于 2009-10-31T17:19:28.443 に答える
1

Ned が言うように、問題は、リストをデフォルトの引数として使用していることです。ここに詳細があります。__init__解決策は、次のように機能を変更することです。

       def __init__(self,arr=None):
           if arr is not None:
               self.array = arr
           else:
               self.array = []
于 2009-10-31T17:18:26.753 に答える
0

None を渡すよりも良い解決策は — 一般的ではなく、この特定の例では — __init__ への arr パラメータを、内部ストレージに使用する配列ではなく、FruitContainer を事前に初期化するための列挙可能なアイテムのセットとして扱うことです。

class FruitContainer:
  def __init__(self, arr=()):
    self.array = list(arr)
  ...

これにより、コンテナを初期化するために他の列挙可能な型を渡すことができます。これは、より高度な Python ユーザーが実行できることを期待するものです。

myFruit = ('apple', 'pear') # Pass a tuple
myFruitContainer = FruitContainer(myFruit)
myOtherFruit = file('fruitFile', 'r') # Pass a file
myOtherFruitContainer = FruitContainer(myOtherFruit)

また、別の潜在的なエイリアシング バグを解消します。

myFruit = ['apple', 'pear']
myFruitContainer1 = FruitContainer(myFruit)
myFruitContainer2 = FruitContainer(myFruit)
myFruitContainer1.addTo('banana')
'banana' in str(myFruitContainer2)

このページの他のすべての実装では、コンテナーの内部ストレージに誤ってエイリアスを設定したため、True が返されます。

注:このアプローチは常に正しい答えとは限りません。それ以外の場合は、"if not None" の方が適しています。自問してみてください: オブジェクトのセットを渡しているのでしょうか、それとも変更可能なコンテナーを渡しているのでしょうか? オブジェクトを渡すクラス/関数が、指定したストレージを変更する場合、それは (a) 驚くべきことでしょうか、それとも (b) 望ましいことでしょうか? この場合、私はそれが (a) であると主張します。したがって、list(...) 呼び出しが最適なソリューションです。(b) の場合、「None でない場合」が正しいアプローチです。

于 2009-11-01T11:20:50.563 に答える