Подтвердить что ты не робот

Почему добавление списка Python должно быть однородным?

Может ли кто-нибудь, знакомый с внутренними компонентами Python (CPython или другими реализациями), объяснить, почему добавление списка должно быть однородным:

In [1]: x = [1]

In [2]: x+"foo"
---------------------------------------------------------------------------
TypeError                                 Traceback (most recent call last)
C:\Users\Marcin\<ipython-input-2-94cd84126ddc> in <module>()
----> 1 x+"foo"

TypeError: can only concatenate list (not "str") to list

In [3]: x+="foo"

In [4]: x
Out[4]: [1, 'f', 'o', 'o']

Почему не следует, чтобы x+"foo" выше возвращал то же значение, что и конечное значение x в приведенном выше расшифровке?

Этот вопрос следует из вопроса NPE здесь: Является ли поведение списка Python + = iterable документированным где угодно?

Обновление: я знаю, что не требуется, чтобы гетерогенный += работал (но это так), и также не требуется, чтобы гетерогенная + была ошибкой. Этот вопрос связан с тем, почему этот последний выбор был сделан.

Слишком много говорить о том, что результаты добавления последовательности в список неопределенны. Если бы это было достаточным возражением, было бы целесообразно предотвратить гетерогенную +=. Update2: В частности, python всегда делегирует вызовы оператора в lefthand operand, поэтому не возникает проблема "что делать правильно": левый объект всегда управляет (если он не делегирует справа).

Update3: для тех, кто утверждает, что это дизайнерское решение, объясните (а), почему он не документирован; или (b) там, где оно документировано.

Update4: "что должно [1] + (2, ) вернуться?" Он должен вернуть значение результата, равное значению переменной x, первоначально удерживая [1] сразу после x+=(2, ). Этот результат хорошо определен.

4b9b3361

Ответ 1

Эти отчеты об ошибках предполагают, что эта ошибка дизайна была ошибкой.

Issue12318:

Да, это ожидаемое поведение и да, это противоречиво.

Это было так долго, и Гвидо сказал, что он не сделает этого снова (это в его списке сожалений). Однако мы не будем нарушать код, меняя его (list.__iadd__ работает как list.extend).

Issue575536:

Цель заключалась в том, что list.__iadd__ точно соответствует list.extend(). Нет необходимости в гиперсегментации list.__add__() тоже: это особенность, что люди, которые не хочу удивиться, похожие на Мартин примеры могут избежать их с помощью простого + для списков.

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

(Спасибо @Mouad за их поиск).

Ответ 2

Из Zen Python:

Перед лицом двусмысленности откажитесь от соблазна угадать.

Посмотрим, что здесь происходит:

x + y

Это дает нам значение, но какого типа? Когда мы добавляем вещи в реальную жизнь, мы ожидаем, что тип будет таким же, как и типы ввода, но что, если они являются несопоставимыми? Ну, в реальном мире мы отказываемся добавлять 1 и "a", это не имеет смысла.

Что делать, если у нас есть похожие типы? В реальном мире мы смотрим на контекст. Компьютер не может этого сделать, поэтому он должен угадать. Python выбирает левый операнд и позволяет ему решать. Ваша проблема возникает из-за отсутствия контекста.

Скажите программисту, что он хочет сделать ["a"] + "bc" - это может означать, что они хотят "abc" или ["a", "b", "c"]. В настоящее время решение состоит в том, чтобы либо вызвать "".join() в первом операнде, либо list() на втором, что позволяет программисту делать то, что они хотят, и ясен и ясен.

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

Итак, почему += отличается? Ну, это потому, что мы даем Python этот контекст. С помощью операции на месте мы говорим Python об изменении значения, поэтому мы знаем, что мы имеем дело с чем-то вроде того значения, которое мы модифицируем. В этом контексте Python должен сделать правильный вызов, поэтому нам не нужно угадывать.

Когда я говорю об угадывании, я говорю о том, что Python угадывает намерение программиста. Это то, что Python делает много - см. Раздел в 3.x. / выполняет разделение с плавающей точкой, исправляя ошибку его целочисленного деления в 2.x.

Это потому, что мы неявно запрашиваем float-деление, когда пытаемся разделить. Python учитывает это, и операции выполняются в соответствии с этим. Точно так же это касается угадывания намерений здесь. Когда мы добавляем с +, наше намерение неясно. Когда мы используем +=, это очень ясно.

Ответ 3

Я считаю, что дизайнеры Python сделали дополнение таким образом, чтобы оператор "+" оставался последовательно коммутативным в отношении типа результата: type(a + b) == type(b + a)

Все ожидают, что 1 + 2 имеет тот же результат, что и 2 + 1. Вы ожидали бы, что [1] + 'foo' будет таким же, как "foo" + [1]? Если да, каков должен быть результат?

У вас есть 3 варианта: вы выбираете левый операнд как тип результата, правый операнд в качестве типа результата или вызываете ошибку.

+ = не является коммутативным, поскольку содержит задание. В этом случае вы либо выбираете левый операнд в качестве результата, либо бросаете. Удивительно, что a += b не совпадает с a = a + b. a += b не переводится на английский язык на "Добавить a в b и присвоить результат". Он переводится как "Добавить a в b". Вот почему это не работает над непреложными, такими как строка или кортеж.

Спасибо за комментарии. Отредактировано сообщение.

Ответ 4

Я предполагаю, что Python строго типизирован, и здесь нет четкого указания на правильную вещь. Вы просите Python добавить строку непосредственно или передать строку в список (что вы указали, что вы хотели бы сделать)?

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