Python Scoping / Статическое недоразумение - PullRequest
6 голосов
/ 31 октября 2009

Я действительно застрял на том, почему следующий блок кода 1 приводит к выводу 1 вместо вывода 2?

Кодовый блок 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 ]

8 голосов
/ 31 октября 2009

Никогда не следует использовать изменяемое значение (например, []) для аргумента метода по умолчанию. Значение вычисляется один раз, а затем используется для каждого вызова. Когда вы используете пустой список в качестве значения по умолчанию, этот же список используется каждый раз, когда метод вызывается без аргумента, даже если значение было изменено предыдущими вызовами функций.

Сделайте это вместо:

def __init__(self,arr=None):
    self.array = arr or []
2 голосов
/ 31 октября 2009

Ваш код имеет аргумент по умолчанию для инициализации класса. Значение аргумента по умолчанию оценивается один раз во время компиляции, поэтому каждый экземпляр инициализируется одним и тем же списком. Измените это так:

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

Я обсуждал это более подробно здесь: Как определить класс в Python

1 голос
/ 31 октября 2009

Как говорит Нед, проблема в том, что вы используете список в качестве аргумента по умолчанию. Подробнее здесь . Решение состоит в том, чтобы изменить __init__ функцию, как показано ниже:

       def __init__(self,arr=None):
           if arr is not None:
               self.array = arr
           else:
               self.array = []
0 голосов
/ 01 ноября 2009

Лучшее решение, чем передача None - в данном конкретном случае, а не в целом - это обработать параметр arr для __init__ как перечисляемый набор элементов для предварительной инициализации 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, потому что вы случайно указали псевдоним внутреннего хранилища ваших контейнеров.

Примечание: Этот подход не всегда правильный ответ: «если не None» лучше в других случаях. Просто спросите себя: я передаю набор объектов или изменяемый контейнер? Если класс / функция, в которую я передаю свои объекты, изменяет хранилище, которое я ему предоставил, это будет (а) неожиданно или (б) желательно? В этом случае я бы сказал, что это (а); Таким образом, вызов списка (...) является лучшим решением. Если (б), «если не None» будет правильным подходом.

...