Ошибка инициализации объекта Python. Или я неправильно понимаю, как работают объекты? - PullRequest
1 голос
/ 08 октября 2009
  1 import sys
  2 
  3 class dummy(object):
  4     def __init__(self, val):
  5         self.val = val
  6 
  7 class myobj(object):
  8     def __init__(self, resources):
  9         self._resources = resources
 10 
 11 class ext(myobj):
 12     def __init__(self, resources=[]):
 13         #myobj.__init__(self, resources)
 14         self._resources = resources
 15 
 16 one = ext()
 17 one._resources.append(1)
 18 two = ext()
 19 
 20 print one._resources
 21 print two._resources
 22 
 23 sys.exit(0)

Будет напечатана ссылка на объект, присвоенный one._resources для объектов one и two. Я бы подумал, что two будет пустым массивом, поскольку он явно устанавливает его как таковой, если он не определен при создании объекта. Раскомментируя myobj.__init__(self, resources) делает то же самое. Использование super(ext, self).__init__(resources) также делает то же самое.

Единственный способ заставить его работать - это использовать следующее:

two = ext(dummy(2))

Мне не нужно вручную устанавливать значение по умолчанию при создании объекта, чтобы эта работа работала. Или, может быть, я делаю. Есть мысли?

Я пробовал это использовать Python 2.5 и 2.6.

Ответы [ 5 ]

7 голосов
/ 08 октября 2009

Вы должны изменить

def __init__(self, resources=[]):
    self._resources = resources

до

def __init__(self, resources=None):
    if resources is None:
        resources = []
    self._resources = resources

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

есть еще немного информации.
6 голосов
/ 08 октября 2009

Ваша проблема в том, что значение по умолчанию оценивается во время определения функции. Это означает, что один и тот же объект списка совместно используется экземплярами. См. Ответ на этот вопрос для дальнейшего обсуждения.

2 голосов
/ 08 октября 2009

Пожалуйста, прочитайте этот ответ для обсуждения того, как настроить класс из __init__(). Вы столкнулись с хорошо известной особенностью Python: вы пытаетесь настроить изменяемый файл, и ваш изменяемый объект оценивается один раз при компиляции __init__(). Стандартный обходной путь:

class ext(myobj):
    def __init__(self, resources=None):
        if resources is None:
            resources = []
        #myobj.__init__(self, resources)
        self._resources = resources
1 голос
/ 08 октября 2009

С http://docs.python.org/3.1/tutorial/controlflow.html:

Значение по умолчанию оценивается только один раз. Это имеет значение, когда по умолчанию это изменчивый объект, такой как список, словарь или экземпляры большинства классы.

0 голосов
/ 08 октября 2009

Это известный Python gotcha .

Вы должны избегать использования изменяемого объекта при вызове функции / метода.

Объекты, предоставляющие значения по умолчанию , не создаются в то время, когда функция / метод вызывается . Они создаются в то время, когда выполняется оператор, определяющий функцию . (См. Обсуждение в Аргументы по умолчанию в Python: две простые ошибки : "Выражения в аргументах по умолчанию вычисляются при определении функции, а не при ее вызове." )

Это поведение не бородавка в языке Python. Это действительно особенность, а не ошибка. Есть моменты, когда вы действительно хотите использовать изменяемые аргументы по умолчанию. Например, они могут сохранить список результатов предыдущих вызовов, что может быть очень удобно.

Но для большинства программистов - особенно начинающих Pythonistas - такое поведение является ошибкой. Поэтому в большинстве случаев мы принимаем следующие правила.

  • Никогда не используйте изменяемый объект - то есть: список, словарь или экземпляр класса - в качестве значения аргумента по умолчанию.
  • Игнорируйте правило 1, только если вы действительно, действительно ДЕЙСТВИТЕЛЬНО знаете, что делаете.

Итак ... мы планируем всегда следовать правилу № 1. Теперь вопрос в том, как это сделать ... как закодировать functionF, чтобы получить поведение, которое мы хотим.

К счастью, решение является простым. Изменяемые объекты, используемые в качестве значений по умолчанию, заменяются на None, а затем аргументы проверяются на None.


Так как же это сделать правильно? Одним из решений является избегать использования изменяемых значений по умолчанию для аргументов . Но это вряд ли удовлетворительно, так как время от времени новый список является полезным по умолчанию. Есть несколько сложных решений, таких как определение декоратора для функций, которые копируют все аргументы. Это перебор, и проблему можно легко решить следующим образом:

class ext(myobj):
    def __init__(self, resources=None):
        if not resources:
            resources = []
        self._resources = resources
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...