Если мы возьмем, b = [1,2,3]
и если мы попытаемся сделать:b+=(4,)
Он возвращается b = [1,2,3,4]
, но если мы попытаемся b = b + (4,)
это сделать, это не сработает.
b = [1,2,3]
b+=(4,) # Prints out b = [1,2,3,4]
b = b + (4,) # Gives an error saying you can't add tuples and lists
Я ожидал b+=(4,)
потерпеть неудачу, так как вы не можете добавить список и кортеж, но это сработало. Поэтому я пытался b = b + (4,)
ожидать того же результата, но это не сработало.
python
python-3.x
list
tuples
Супун Дасанта Куруппу
источник
источник
Ответы:
Проблема с вопросами «почему» заключается в том, что обычно они могут означать несколько разных вещей. Я постараюсь ответить на каждый вопрос, который, я думаю, вы могли иметь в виду.
"Почему это может работать по-другому?" на что отвечает, например, это . По сути,
+=
пытается использовать различные методы объекта:__iadd__
(который проверяется только с левой стороны), vs__add__
и__radd__
(«обратное добавление», проверяется с правой стороны, если с левой стороны нет__add__
) для+
.«Что именно делает каждая версия?» Короче говоря,
list.__iadd__
метод делает то же самое, что иlist.extend
(но из-за языкового дизайна, есть еще задание назад).Это также означает, например, что
+
Конечно, создает новый объект, но явно требует другой список вместо того, чтобы пытаться извлечь элементы из другой последовательности.«Почему это полезно для + =? Это более эффективно;
extend
метод не должен создавать новый объект. Конечно, иногда это дает некоторые неожиданные эффекты (как выше), и в целом Python на самом деле не касается эффективности. Но эти решения были приняты давно.«В чем причина запрета добавления списков и кортежей с +?» Смотрите здесь (спасибо, @ splash58); одна идея состоит в том, что (tuple + list) должен производить тот же тип, что и (list + tuple), и не ясно, какой тип должен быть результатом.
+=
не имеет этой проблемы, потому что,a += b
очевидно, не должен менять типa
.источник
|
, так что это как бы разрушает мой пример. Если позже я подумаю о более ясном примере, я его поменяю.|
для множеств является коммутирующим оператором, но+
для списков - нет. По этой причине я не думаю, что аргумент о неоднозначности типов особенно силен. Поскольку оператор не коммутирует, зачем требовать то же самое для типов? Можно просто согласиться с тем, что результат имеет тип lhs. С другой стороны, ограничиваяlist + iterator
, разработчик должен быть более откровенным в своих намерениях. Если вы хотите создать новый список , который содержит материал , изa
продолженную материала изb
уже есть способ сделать это:new = a.copy(); new += b
. Это еще одна линия, но кристально чистая.a += b
ведет себя иначе, чемa = a + b
не эффективность. Это на самом деле, что Гвидо посчитал выбранное поведение менее запутанным. Представьте себе функцию, получающую список вa
качестве аргумента, а затем выполняющуюa += [1, 2, 3]
. Этот синтаксис, безусловно, выглядит так, как будто он изменяет список на месте, а не создает новый список, поэтому было принято решение, что он должен вести себя в соответствии с тем, что большинство людей думает об ожидаемом результате. Тем не менее, механизм также должен был работать для неизменяемых типов, таких какint
s, что привело к текущему дизайну.a += b
сокращениеa = a + b
, как это сделала Руби, но я могу понять, как мы туда попали.Они не эквивалентны:
является сокращением для:
в то время как
+
объединяет списки, поэтому:вы пытаетесь объединить кортеж в список
источник
Когда вы делаете это:
преобразуется в это:
Под капотом он вызывает
b.extend((4,))
,extend
принимает итератор и вот почему это тоже работает:но когда вы делаете это:
преобразуется в это:
принять только список объектов.
источник
Из официальных документов для изменяемых типов последовательностей оба:
определяются как:
Что отличается от того, что определяется как:
Это также означает , что будет работать любой тип последовательности
t
, включая кортеж, как в вашем примере.Но это также работает для диапазонов и генераторов! Например, вы также можете сделать:
источник
Подобные «расширенные» операторы присваивания
+=
были введены в Python 2.0, который был выпущен в октябре 2000 года. Дизайн и обоснование описаны в PEP 203 . Одной из заявленных целей этих операторов была поддержка операций на месте. Письмопредполагается обновить список
a
на месте . Это имеет значение, если есть другие ссылки на списокa
, например, когдаa
был получен в качестве аргумента функции.Однако операция не всегда может происходить на месте, так как многие типы Python, включая целые числа и строки, являются неизменяемыми , поэтому, например,
i += 1
для целого числаi
не может работать на месте.Таким образом, операторы расширенного присваивания должны были работать, когда это возможно, и создавать новый объект в противном случае. Для облегчения этих целей проектирования выражение
x += y
было задано следующим образом:x.__iadd__
определено,x.__iadd__(y)
оценивается.x.__add__
реализуетсяx.__add__(y)
, оценивается.y.__radd__
реализуетсяy.__radd__(x)
, оценивается.Первый результат, полученный этим процессом, будет возвращен
x
(если только этот результат не являетсяNotImplemented
одиночным, в этом случае поиск продолжается со следующего шага).Этот процесс позволяет реализовывать типы, которые поддерживают модификацию на месте
__iadd__()
. Типы, которые не поддерживают модификацию на месте, не должны добавлять какие-либо новые магические методы, так как Python автоматически вернется к существенномуx = x + y
.Итак, давайте наконец подойдем к вашему актуальному вопросу - почему вы можете добавить кортеж в список с помощью оператора расширенного присваивания. По памяти история этого была примерно такой:
list.__iadd__()
метод был реализован для простого вызова уже существующегоlist.extend()
метода в Python 2.0. Когда в Python 2.1 были представлены итераторы,list.extend()
метод был обновлен, чтобы принимать произвольные итераторы. Конечным результатом этих изменений было то, чтоmy_list += my_tuple
работало начиная с Python 2.1. Однакоlist.__add__()
метод никогда не должен был поддерживать произвольные итераторы в качестве правого аргумента - это считалось неуместным для строго типизированного языка.Лично я считаю, что реализация расширенных операторов оказалась слишком сложной в Python. У него много неожиданных побочных эффектов, например, этот код:
Возникает вторая строка
TypeError: 'tuple' object does not support item assignment
, но операция все равно успешно выполняется -t
будет([42, 44], [43])
после выполнения строки, которая вызывает ошибку.источник
Большинство людей ожидают, что X + = Y будет эквивалентно X = X + Y. Действительно, в Справочнике по карманам Python (4-е издание) Марка Латца говорится на странице 57 «Следующие два формата примерно эквивалентны: X = X + Y, X + = Y ". Однако люди, которые указали Python, не сделали их эквивалентными. Возможно, это была ошибка, которая приведет к часам отладки разочарованными программистами до тех пор, пока Python остается в использовании, но теперь Python такой же, как и он. Если X является изменяемым типом последовательности, X + = Y эквивалентно X.extend (Y), а не X = X + Y.
источник
Как объясняется здесь , если метод
array
не реализует__iadd__
,b+=(4,)
он будет просто кратким,b = b + (4,)
но, очевидно, это не так, такarray
же как и__iadd__
метод реализации . Видимо реализация__iadd__
метода примерно такая:Однако мы знаем, что приведенный выше код не является реальной реализацией
__iadd__
метода, но мы можем предположить и принять, что есть что-то вродеextend
метода, который принимаетtupple
входные данные.источник