То, почему “изменяемый параметр по умолчанию фиксирует” синтаксис, так ужасно, спрашивает новичок Python

Теперь после моей серии "вопросов о новичке Python" и на основе другого вопроса.

Прерогатива

Перейдите к http://python.net/~goodger/projects/pycon/2007/idiomatic/handout.html#other-languages-have-variables и прокрутите вниз к "Значениям параметров По умолчанию". Там можно найти следующее:

def bad_append(new_item, a_list=[]):
    a_list.append(new_item)
    return a_list

def good_append(new_item, a_list=None):
    if a_list is None:
        a_list = []
    a_list.append(new_item)
    return a_list

Существует даже "Важное предупреждение" на python.org с этим точно тем же примером, tho не действительно высказывание, что это "лучше".

Один способ поместить его

Так, вопрос здесь: почему ужасен "хороший" синтаксис по известной проблеме как этот на языке программирования, который продвигает "изящный синтаксис" и "простой в использовании"?

править:

Другой способ поместить его

Я не спрашиваю, почему или как это происходит (спасибо Mark для ссылки).

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

Я думаю, что лучший путь был бы, вероятно, способность выполнить в чем-то def самостоятельно, в котором аргумент имени был бы присоединен к "локальному", или "новый" в def, изменяемый объект. Что-то как:

def better_append(new_item, a_list=immutable([])):
    a_list.append(new_item)
    return a_list

Я уверен, что кто-то может идти с лучшим синтаксисом, но я также предполагаю, что должно быть очень хорошее объяснение к тому, почему это не было сделано.

28
задан Community 23 May 2017 в 10:27
поделиться

8 ответов

Аргументы по умолчанию оцениваются во время выполнения оператора def , что, вероятно, является наиболее разумным подходом: часто это именно то, что требуется. Если бы это было не так, это могло бы вызвать запутанные результаты, если бы среда немного изменилась.

Дифференциация с помощью магического локального метода или чего-то подобного далеко от идеала. Python пытается сделать вещи довольно простыми, и нет очевидной, четкой замены для текущего шаблона, который не прибегал бы к путанице с довольно последовательной семантикой, которую в настоящее время имеет Python.

6
ответ дан 28 November 2019 в 03:48
поделиться

Я отредактировал этот ответ, чтобы включить в него мысли из множества комментариев, опубликованных в вопросе.

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

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

def better_append(new_item, a_list=[]): 
    new_list = list(a_list)
    new_list.append(new_item) 
    return new_list 

Для чего-то немного другого, вы можете создать генератор, который может принимать список или генератор в качестве входных данных:

def generator_append(new_item, a_list=[]):
    for x in a_list:
        yield x
    yield new_item

Я думаю, вы ошибаетесь, что Python по-разному обрабатывает изменяемые и неизменяемые аргументы по умолчанию; это просто неправда. Скорее, неизменность аргумента заставляет вас тонко изменить свой код, чтобы автоматически поступать правильно. Возьмите ваш пример и примените его к строке, а не к списку:

def string_append(new_item, a_string=''):
    a_string = a_string + new_item
    return a_string

Этот код не изменяет переданную строку - он не может, потому что строки неизменяемы. Он создает новую строку и присваивает ей a_string.Аргумент по умолчанию можно использовать снова и снова, потому что он не меняется, вы сделали его копию в начале.

3
ответ дан 28 November 2019 в 03:48
поделиться

Я думаю, вы путаете элегантный синтаксис с синтаксическим сахаром. Синтаксис python четко передает оба подхода, просто так получается, что правильный подход кажется менее элегантным (с точки зрения синтаксиса), чем неправильный подход. Но поскольку неправильный подход, ну ... неправильный, его элегантность не имеет значения. Что касается того, почему что-то вроде того, что вы демонстрируете в better_append, не реализовано, я предполагаю, что Должен быть один - и желательно только один - очевидный способ сделать это. превосходит незначительную элегантность.

0
ответ дан 28 November 2019 в 03:48
поделиться

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

Итак, у вас есть два разных метода передачи параметров, обеспечивающих разное поведение. И это хорошо, я бы не стал это менять.

1
ответ дан 28 November 2019 в 03:48
поделиться

Что, если бы вы говорили не о списках, а о AwesomeSets, классе, который вы только что определили? Хотели бы вы определить ".local" в каждом классе ?

class Foo(object):
    def get(self):
        return Foo()
    local = property(get)

, возможно, сработает, но очень быстро устареет. Довольно скоро шаблон «if a is None: a = CorrectObject ()» становится второй натурой, и вы не сочтете его уродливым - вы обнаружите, что он проливает свет.

Проблема не в синтаксисе, а в семантике - значения параметров по умолчанию оцениваются во время определения функции, а не во время выполнения функции.

2
ответ дан 28 November 2019 в 03:48
поделиться

Это называется «изменяемая ловушка по умолчанию». См .: http://www.ferg.org/projects/python_gotchas.html#contents_item_6

В основном, a_list инициализируется при первой интерпретации программы, а не каждый раз, когда вы вызываете функцию. (как и следовало ожидать от других языков). Таким образом, вы не получаете новый список каждый раз, когда вызываете функцию, но вы повторно используете один и тот же.

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

Это:

>>> my_list = []
>>> my_list.append(1)

Яснее и легче для чтения, чем:

>>> my_list = my_append(1)

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

11
ответ дан 28 November 2019 в 03:48
поделиться

Это лучше, чем good_append (), IMO:

def ok_append(new_item, a_list=None):
    return a_list.append(new_item) if a_list else [ new_item ]

Вы также можете быть особенно осторожными и проверить, что a_list был списком ...

0
ответ дан 28 November 2019 в 03:48
поделиться

Чрезвычайно конкретный вариант использования функции, которая позволяет вам опционально передавать список для изменения, но генерирует новый список, если вы специально не передали его, определенно не стоит особого синтаксиса. Серьезно, если вы выполняете несколько вызовов этой функции, зачем вам нужно использовать особый случай для первого вызова в серии (передавая только один аргумент), чтобы отличать его от всех других (для чего потребуются два аргумента чтобы иметь возможность продолжать пополнять существующий список) ?! Например, рассмотрим что-то вроде (конечно, при условии, что betterappend сделал что-то полезное, потому что в текущем примере было бы безумием называть его вместо прямого .append ! -):

def thecaller(n):
  if fee(0):
    newlist = betterappend(foo())
  else:
    newlist = betterappend(fie())
  for x in range(1, n):
    if fee(x):
      betterappend(foo(), newlist)
    else:
      betterappend(fie(), newlist)

это просто безумие, и, очевидно, вместо этого

def thecaller(n):
  newlist = []
  for x in range(n):
    if fee(x):
      betterappend(foo(), newlist)
    else:
      betterappend(fie(), newlist)

всегда следует использовать два аргумента, избегать повторений и строить гораздо более простую логику.

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

4
ответ дан 28 November 2019 в 03:48
поделиться
Другие вопросы по тегам:

Похожие вопросы: