Переменные по умолчанию класса Python являются объектами класса?

Возможный дубликат:
«Наименьшее удивление» в Python: аргумент Mutable Default

Сегодня я писал код, и наткнулся на ошибку в моем коде. Я заметил, что значения по умолчанию для одного из моих вновь созданных объектов переносятся с другого объекта! Например:

class One(object): def __init__(self, my_list=[]): self.my_list = my_list one1 = One() print(one1.my_list) [] # empty list, what you'd expect. one1.my_list.append('hi') print(one1.my_list) ['hi'] # list with the new value in it, what you'd expect. one2 = One() print(one2.my_list) ['hi'] # Hey! It saved the variable from the other One! 

Поэтому я знаю, что это можно решить, сделав это:

 class One(object): def __init__(self, my_list=None): self.my_list = my_list if my_list is not None else [] 

То, что я хотел бы знать, это … Почему? Почему классы Python структурированы так, что значения по умолчанию сохраняются в экземплярах класса?

Заранее спасибо!

6 Solutions collect form web for “Переменные по умолчанию класса Python являются объектами класса?”

Несколько других отметили, что это экземпляр проблемы «изменчивого значения по умолчанию» в Python. Основная причина заключается в том, что аргументы по умолчанию должны существовать «вне» функции для ее передачи в нее.

Но реальный корень этого как проблемы не имеет ничего общего с аргументами по умолчанию . В любое время было бы плохо, если измененное значение по умолчанию было изменено, вам действительно нужно спросить себя: было бы плохо, если бы явное значение было изменено? Если кто-то не знаком с кишками вашего класса, следующее поведение также будет очень удивительным (и, следовательно, приведет к ошибкам):

 >>> class One(object): ... def __init__(self, my_list=[]): ... self.my_list = my_list ... >>> alist = ['hello'] >>> one1 = One(alist) >>> alist.append('world') >>> one2 = One(alist) >>> >>> print(one1.my_list) # Huh? This isn't what I initialised one1 with! ['hello', 'world'] >>> print(one2.my_list) # At least this one's okay... ['hello', 'world'] >>> del alist[0] >>> print one2.my_list # What the hell? I just modified a local variable and a class instance somewhere else got changed? ['world'] 

9 раз из 10, если вы обнаружите, что достигли «шаблона» использования « None в качестве значения по умолчанию и использовали, if value is None: value = default , вы не должны быть. Вы должны просто не изменять свои аргументы! Аргументы не должны рассматриваться как принадлежащие вызываемому коду, если только оно не задокументировано как право собственности на них.

В этом случае (особенно потому, что вы инициализируете экземпляр класса, поэтому изменяемая переменная будет жить долгое время и будет использоваться другими методами и потенциально другим кодом, который извлекает ее из экземпляра). Я бы сделал следующее:

 class One(object): def __init__(self, my_list=[]) self.my_list = list(my_list) 

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

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

Другая проблема заключается в том, что если my_list может быть очень большим, копия может быть дорогой. Там вам нужно сделать компромисс. В этом случае, может быть, лучше всего использовать список переданных в конце, и использовать if my_list is None: my_list = [] шаблон, чтобы все экземпляры по умолчанию, связанные с одним списком. Но если вы это сделаете, вам нужно прояснить, будь то в документации или имени класса, что вызывающие лица отказываются от владения списками, которые они используют для инициализации экземпляра. Или, если вы действительно хотите создавать список исключительно для того, чтобы обертывать экземпляр One , возможно, вам стоит выяснить, как инкапсулировать создание списка внутри инициализации One , а не сначала его создавать; в конце концов, это действительно часть экземпляра, а не инициализирующее значение. Иногда это недостаточно гибко.

И иногда вы действительно честно хотите, чтобы наложение псевдонимов продолжалось, и передайте код, изменяя значения, к которым у них есть доступ. Тем не менее, я думаю, очень тяжело, прежде чем я посвящу себя такому дизайну. И это удивит других (и вы, когда вернетесь к коду через X месяцев), так что документация – ваш друг!

На мой взгляд, обучение новых программистов на Python в отношении «изменяемого аргумента по умолчанию» gotcha на самом деле (слегка) вредно. Мы должны спросить их: «Почему вы изменяете свои аргументы?» (а затем указывая, как аргументы по умолчанию работают в Python). Сам факт функции, обладающей разумным аргументом по умолчанию, часто является хорошим показателем того, что он не предназначен как нечто, которое получает право собственности на ранее существовавшее значение, поэтому, вероятно, не следует изменять аргумент, получал ли он значение по умолчанию.

Это известное поведение способа работы значений по умолчанию Python, что часто бывает неожиданным для неосторожного. Пустой объект массива [] создается во время определения функции, а не во время ее вызова .

Чтобы исправить это, попробуйте:

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

В принципе, функции функции python сохраняют кортеж аргументов по умолчанию, что отлично подходит для неизменяемых вещей, таких как целые числа, но списки и другие изменяемые объекты часто изменяются на месте, что приводит к поведению, которое вы наблюдаете.

Это стандартное поведение аргументов по умолчанию в любом месте Python, а не только в классах.
Дополнительные сведения см. В разделе Mutable defaults для аргументов function / method .

Функции Python – это объекты. По умолчанию аргументы функции являются атрибутами этой функции. Поэтому, если значение аргумента по умолчанию изменено и оно изменяется внутри вашей функции, изменения отражаются в последующих вызовах этой функции.

Не ответ, но стоит отметить, что это также верно для переменных класса, определенных вне любых функций класса.

Пример:

 >>> class one: ... myList = [] ... >>> >>> one1 = one() >>> one1.myList [] >>> one2 = one() >>> one2.myList.append("Hello Thar!") >>> >>> one1.myList ['Hello Thar!'] >>> 

Обратите внимание, что значение myList не только сохраняется, но и каждый экземпляр myList указывает на тот же список .

Я сам столкнулся с этой ошибкой / функцией и провел что-то вроде 3 часов, пытаясь понять, что происходит. Довольно сложно отлаживать, когда вы получаете достоверные данные, но это не из локальных вычислений, а из предыдущих.

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

Решение, по крайней мере для меня, состояло в том, чтобы просто создать всю переменную класса внутри __init__ .

  • Как заменить символы в строке на следующую?
  • Загрузите и проанализируйте CSV-файл с помощью Google App Engine
  • ImportError: нет модуля с именем apiclient.discovery
  • Что такое питонический метод выполнения нескольких орлов?
  • Пользовательская аутентификация (модель пользователя) для облачных конечных точек - Python
  • время python + эквивалент timedelta
  • Разделение строки на основе поискового запроса в Python?
  • API Google Mirror, бросающий исключение BadStatusLine (Python)
  • Python - лучший язык программирования в мире.