2016-12-22 1 views
-3

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

Так что я только что это случилось со мной сегодня, и теперь я знаком с этой ловушкой, в которую входят многие новые разработчики Python: изменяемые аргументы по умолчанию. Для краткости, вот пример того, что я везу о:

def MyClass(object): 
     ... 
    def my_func(self,my_str="default",my_dict={},**kwargs): 
     my_dict.update(kwargs) 
     self.my_dict = my_dict 

---------------------------------------- 
<< a = MyClass() 
<< a.my_func(foo='bar') 
<< a.my_dict 
>> {'foo':'bar'} 

<< b = MyClass() 
<< b.my_func(bar='baz') 
<< b.my_dict 
>> {'foo': 'bar', 'bar': 'baz'} 

Я понимаю, что это намеренное и понять, что это хорошее решение было бы изменить my_func к этому вместо:

def my_func(self,my_str="default",my_dict=None,**kwargs) 
    if my_dict is None: my_dict = {} 
       ... 

Однако Я не удовлетворен этим. Это просто портит меня неправильно. По умолчанию я не хочу, чтобы он был None. По умолчанию я хочу пустой dict. Так я думал, что сделать это:

def my_func(self,my_str="default", my_dict={}, **kwargs) 
    my_dict.update(kwargs) 
    self.my_dict = my_dict 
    my_dict = {} 

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

Я действительно повесил трубку, потому что считаю полезным использовать аргумент по умолчанию для передачи типа читателю, что повышает читаемость кода IMO. Это хорошо, потому что читателю не нужно было погружаться в функцию, чтобы определить, что my_dict должен быть dict (inb4, предлагающий «просто использовать комментарии» - идеалистический, но не очень реалистичный для моих целей).

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

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

+0

«Никто никогда не будет использовать это» - неправильно. Я использовал его. Он похож на атрибуты класса и атрибуты экземпляра. – TigerhawkT3

+0

Гипербола. Я уверен, что большинство разработчиков Python, особенно те, которые приходят с языка, где это не функция, не подумали бы использовать его. –

+0

'my_dict' на самом деле изменен, но вы не меняете аргумент' my_dict', вы переназначаете его. – Natecat

ответ

4

Если вы правильно поняли, вы думаете, что, назначив my_dict = {}, вы «сбросите» значение, чтобы в следующий раз, когда вы вызываете функцию, он снова будет пуст.

Это не так, как работают аргументы по умолчанию. Скорее, он работает примерно так:

  1. Когда вы определяете свою функцию, создается «секретный» объект, который содержит значения по умолчанию для аргументов. (Вы можете видеть этот секретный объект как func.__defaults__ на простой функции, хотя есть некоторые дополнительные осложнения, если вы смотрите на объект метода.)
  2. Каждый раз, когда вы вызываете функцию, если вы не передали значение для данный аргумент, Python присваивает значение этого аргумента ранее сохраненному секретному объекту.

Другими словами, Python действительно не сохраняет значение my_dict через вызовы.Он сохраняет один значение по умолчанию и извлекает его при каждом вызове (если вы не передадите новый).

Когда вы делаете my_dict = {}, вы просто назначаете новое значение локальной переменной my_dict, которая не затрагивает секретный объект. С другой стороны, когда вы делаете что-то вроде my_dict.update(), вы мутируете секретный объект. (Если вас удивляет то, что назначение переменной и вызов метода, например update, имеют разные эффекты, тогда вы должны искать здесь много других вопросов о назначении, привязке переменных, изменяемых vs неизменяемых объектах и ​​т. П. В Python.)

+0

Возможно, вы захотите упомянуть 'dictobj.clear()' в вашем ответе – Natecat

+0

@Natecat: Это может быть опасно. Представляете ли вы вызов 'my_dict.clear()' в конце для сброса значения?Это было бы плохо, если бы значение, отличное от значения по умолчанию, фактически было передано, поскольку оно будет стирать все, что было в dict, который вы передали в качестве вашего аргумента. – BrenBarn

+0

@BrenBarn - по сути, победил эту функцию для тех, кто ожидал, что она там будет? Это то, что я хочу сделать на самом деле. –

Смежные вопросы