2009-10-31 11 views
6

Aşağıdaki kod bloğu 1'in neden çıkış 2 yerine çıkış 1 ile sonuçlandığına gerçekten takıldım mı?Python Scoping/Static Misunderstanding

Kod bloğu 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,] 

Bu kodun amacı bir dizi yineleme ve bir üst nesne her sarılmasıdır. Bu, tüm elmaları bir elmalı çuvala ve buna benzer şekilde ekleyen gerçek kodumun azalmasıdır. Benim tahminimce, bir nedenden dolayı, aynı nesne kullanıyor ya da meyve konteyneri statik bir dizi kullanıyormuş gibi davranıyor. Bunu nasıl düzelteceğimi bilmiyorum.

+1

Sorunuza bir cevap değil, aynı zamanda dikkat çekicidir: "while (arrayOfFruit)> 0:", "arrayOfFruit:" iken.İkincisi, en azından Python Stil Kılavuzuna göre tercih edilir. –

cevap

2

Kodunuz sınıfını başlatmak için varsayılan bir argüman vardır: çözüm olarak aşağıda __init__ fonksiyonunu değiştirmektir. Varsayılan argümanın değeri derleme zamanında bir kez değerlendirilir, böylece her örnek aynı listeyle başlatılır. şöyle değiştirin:

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

Bu daha ayrıntılı bir şekilde ele: How to define a class in Python

8

Bir yönteme varsayılan argüman için değiştirilemez bir değeri ([] gibi) kullanmamalısınız. Değer bir kez hesaplanır ve daha sonra her bir çağrı için kullanılır. Boş bir listeyi varsayılan değer olarak kullandığınızda, aynı işlev, fonksiyonun argüman olmadan her çağrılmasında kullanılır, değer önceki işlev çağrıları tarafından değiştirilmiş olsa bile.

yerine bunu yapın: Ned söylediği gibi

def __init__(self,arr=None): 
    self.array = arr or [] 
+0

Mükemmel !!! Bu harika ve basit. –

+4

Değerin false değerini değerlendirip değerlendirmediğini görmek için 'Yok' için sınamanızı gerçekten görmek istemiyorum. Bir '' Yok' testini kullanmak daha iyidir. Arayan, başlatıcı için boş bir listeyi yasal olarak geçirebilir ve kodunuz boş listeyi iptal eder ve yeni bir boş liste yapar. Bu, yalnızca birileri sınıfın birkaç örneğini aynı şeyi paylaşmaya çalışıyorsa ortaya çıkar. Başlangıçta boş liste, sanırım, ama mümkün. Her halükarda 'Yok''u test etmek için' Yok' kullanmak iyi bir alışkanlıktır. – steveha

+0

Haklısınız, '' Yok' daha güvenlidir. –

1

, sorun varsayılan bir argüman olarak bir listesi kullanırken olduğunu. Daha fazla ayrıntı here var.

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

Yok geçen daha iyi bir çözüm - bu özel durumda, yerine genel olarak daha - için varış parametresini tedavi etmektir __init__ öğelerin bir enumerable seti dahili depolama için kullanılacak olan FruitContainer yerine bir dizi önceden başlatmak üzere: Bu kapsayıcınızı başlatılamadı diğer enumerable türlerinde geçmesine izin verecek

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

, hangi daha gelişmiş Python kullanıcıları yapabilmek bekliyoruz:

myFruit = ('apple', 'pear') # Pass a tuple 
myFruitContainer = FruitContainer(myFruit) 
myOtherFruit = file('fruitFile', 'r') # Pass a file 
myOtherFruitContainer = FruitContainer(myOtherFruit) 
Ayrıca diğer potansiyel örtüşme hata etkisiz hale olacaktır

: bu gerçek dönecektir bu sayfadaki tüm diğer uygulamaları ile

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

, çünkü Yanlışlıkla kaplarınızın iç deposunu taklit ettiniz.

Not: Bu yaklaşım doğru cevap değil daima şudur: "değil Yok eğer" diğer durumlarda daha iyidir. Sadece kendinize sorun: bir dizi nesneyi veya değişken bir kapsayıcıyı mı geçiyorum? Nesnelerimi/nesneyi verdiğim depolamayı değiştirdiğimde değiştirirseniz (a) şaşırtıcı mı (b) arzu edilir mi? Bu durumda, bunun (a); Böylece liste (...) çağrısı en iyi çözümdür. Eğer (b) “değilse Yok” ise doğru yaklaşım olacaktır.

+1

Merhaba, sanırım "birileri". Bu örneği bu şekilde kodlamaya itirazım yok. Bu örnekte, FruitContainer sınıfındaki dahili depolamaya eklenecek meyveleri geçiyorsunuz. Kullanıcı, konteyneri bir miktar meyve ile başlatarak liste nesnesinde çok fazla geçiş yapmıyor. Şimdi, genel durumda, kullanıcının sağladığı şeyleri sessizce zorlamanın iyi bir fikir olduğunu düşünmüyorum.Sanırım birbirimizi konuşuyor olmamızın sebebi genel vakaya odaklandığım ve bu çok özel durumu düşündüğünüzü. * Genel olarak, türleri tiplendirerek ördek yazmayı kırmayın. – steveha

+1

Mükemmel. Bunu yansıtmak için son paragrafı yeniden yazdım. Noktanızı bana daha iyi hale getirmek için zaman ayırdığınız için teşekkürler! –

+0

Sadece üzgünüm, daha önce nereden geldiğini anlayamadım. Tam pedant moddaydım, sanırım. :-) – steveha