Я понятия не имею, как они на самом деле называются, но я вижу их все время. Реализация Python выглядит примерно так:
x += 5
в качестве сокращенной записи для x = x + 5
.
Но почему это считается хорошей практикой? Я сталкивался с этим почти в каждой книге или учебнике по программированию, которые читал для Python, C, R и так далее, и так далее. Я понимаю, что это удобно, сохраняя три нажатия клавиш, включая пробелы. Но они, кажется, всегда сбивают меня с толку, когда я читаю код, и, по крайней мере, на мой взгляд, делают его менее читабельным, а не более.
Я пропускаю какую-то ясную и очевидную причину, по которой они используются повсеместно?
programming-practices
Fomite
источник
источник
x += 5
чемx = x + 5
? Или это действительно просто синтаксический сахар, как вы предлагаете?Ответы:
Это не стенография.
+=
Символ появился на языке C в 1970 - х годах, а также - с идеей C «умного ассемблера» соответствует четко другому режиму команд машины и адресации:Такие вещи, как "
i=i+1
","i+=1
"и"++i
", хотя и на абстрактном уровне производят одинаковый эффект, на низком уровне соответствуют другому способу работы процессора.В частности, эти три выражения, предполагая, что
i
переменная находится в адресе памяти, хранящемся в регистре процессора (назовем егоD
- воспринимайте его как «указатель на int»), а ALU процессора принимает параметр и возвращает результат в виде «Аккумулятор» (назовем его A - думайте как int).С этими ограничениями (очень распространенными во всех микропроцессорах того периода) перевод, скорее всего, будет
Первый способ сделать это неоптимален, но он более общий при работе с переменными вместо константы (
ADD A, B
илиADD A, (D+x)
) или при переводе более сложных выражений (все они сводятся к операции push с низким приоритетом в стеке, вызывают высокий приоритет, pop и повторять, пока все аргументы не будут устранены).Второй тип более типичен для «конечного автомата»: мы больше не «вычисляем выражение», а «работаем со значением»: мы все еще используем ALU, но избегаем перемещения значений, поскольку в результате можно заменить параметр. Этот вид инструкций не может использоваться, когда требуется более сложное выражение:
i = 3*i + i-2
его нельзя использовать на месте, посколькуi
требуется больше раз.Третий, даже более простой, даже не рассматривает идею «сложения», но использует более «примитивную» (в вычислительном смысле) схему для счетчика. Инструкция закорочена, загружается быстрее и выполняется немедленно, поскольку комбинаторная сеть, необходимая для модификации регистра, чтобы сделать его счетчиком, меньше и, следовательно, быстрее, чем у полного сумматора.
С современными компиляторами (см. C, к настоящему времени), позволяющими оптимизировать компилятор, соответствие может быть изменено в зависимости от удобства, но в семантике все еще есть концептуальное различие.
x += 5
означаетНо
x = x + 5
значит:Конечно, оптимизация может
&x
вместо аккумулятора применяется аккумулятортаким образом, оптимизированный код должен совпадать с
x += 5
одним.Но это можно сделать только в том случае, если «поиск х» не имеет побочных эффектов, в противном случае
а также
семантически различаются, поскольку
x()
побочные эффекты (признаниеx()
- это функция, выполняющая странные вещи и возвращающаяint*
), будут создаваться дважды или один раз.Следовательно, эквивалентность между
x = x + y
иx += y
обусловлена частным случаем, когда+=
и=
применяются к прямому l-значению.Чтобы перейти на Python, он унаследовал синтаксис от C, но так как нет перевода / оптимизации ДО выполнения на интерпретируемых языках, вещи не обязательно так тесно связаны (так как есть еще один шаг разбора). Однако интерпретатор может ссылаться на разные подпрограммы выполнения для трех типов выражений, используя преимущества различного машинного кода в зависимости от того, как сформировано выражение и от контекста оценки.
Для тех, кто любит более подробно ...
Каждый ЦП имеет АЛУ (арифметико-логический блок), который по своей сути является комбинаторной сетью, чьи входы и выходы «подключены» к регистрам и / или памяти в зависимости от кода операции инструкции.
Двоичные операции обычно реализуются как «модификатор регистра накопителя с вводом, взятым« где-то », где где-то может быть - внутри самого потока команд (типично для константы манифеста: ADD A 5) - внутри другого реестра (типично для вычисления выражения с временные значения: например, ADD AB) - внутри памяти, по адресу, заданному регистром (типично для выборки данных, например: ADD A (H)) - H, в этом случае работает как указатель разыменования.
С этим псевдокодом,
x += 5
являетсяв то время как
x = x+5
этоТо есть x + 5 дает временное значение, которое назначается позже.
x += 5
работает непосредственно на х.Фактическая реализация зависит от реального набора команд процессора: если нет
ADD (.) c
кода операции, первый код становится вторым: нет пути.Если есть такой код операции, и оптимизация включена, второе выражение, после устранения обратных движений и корректировки регистра кода операции, становится первым.
источник
В зависимости от того, как вы думаете об этом, на самом деле это легче понять, потому что это более просто. Взять, к примеру:
x = x + 5
вызывает умственную обработку «возьмите х, добавьте к нему пять, а затем присвойте это новое значение обратно х»x += 5
можно рассматривать как «увеличение х на 5»Таким образом, это не просто стенография, это фактически описывает функциональность более прямо. Когда вы читаете куски кода, это гораздо легче понять.
источник
x = x + 5
все еще беспокоил меня. Когда я попал в математику в более позднем возрасте, это беспокоило меня еще больше. Использованиеx += 5
значительно более наглядно и имеет гораздо больше смысла в качестве выражения.reallyreallyreallylongvariablename = reallyreallyreallylongvariablename + 1
... о, нет !!! опечаткаПо крайней мере, в Python ,
x += y
иx = x + y
может делать совершенно разные вещи.Например, если мы делаем
затем
a += [3]
приведет к, вa == b == [3]
то время какa = a + [3]
приведет кa == [3]
иb == []
. То есть+=
изменяет объект на месте (ну, это может быть сделано, вы можете определить__iadd__
метод, который будет делать практически все, что вам нравится), в то же время=
создает новый объект и привязывает к нему переменную.Это очень важно при выполнении числовой работы с NumPy , так как вы часто сталкиваетесь с несколькими ссылками на разные части массива, и важно убедиться, что вы случайно не изменили часть массива, на которую есть другие ссылки, или без необходимости копировать массивы (которые могут быть очень дорогими).
источник
__iadd__
: есть языки , где вы можете создать неизменяемую ссылку на изменяемую структуру данные, в которойoperator +=
определяются, например , лестница:val sb = StringBuffer(); lb += "mutable structure"
противvar s = ""; s += "mutable variable"
. дальнейшее изменение содержимого структуры данных, в то время как последняя указывает переменную на новую.Это называется идиома . Идиомы программирования полезны, потому что они представляют собой последовательный способ написания конкретной конструкции программирования.
Всякий раз, когда кто-то пишет,
x += y
вы знаете, чтоx
это увеличение,y
а не какая-то более сложная операция (как лучший метод, как правило, я бы не смешивал более сложные операции и эти сокращения синтаксиса). Это имеет смысл при увеличении на 1.источник
++x
иx+=1
эквивалентны в C и Java (возможно, также в C #), но не обязательно в C ++ из-за сложной семантики операторов. Ключевым моментом является то, что они оба оцениваютx
один раз, увеличивают переменную на единицу и имеют результат, который является содержимым переменной после оценки.++x + 3
не то же самое, чтоx += 1 + 3
. Сделайте скобкиx += 1
и они идентичны. Как заявления сами по себе, они идентичны.++x + 3
,x += 1 + 3
либо(x += 1) + 3
имеет неопределенное поведение (при условии, что полученное значение «подходит»).Чтобы прояснить точку зрения Пабби, рассмотрим
someObj.foo.bar.func(x, y, z).baz += 5
Без
+=
оператора есть два пути:someObj.foo.bar.func(x, y, z).baz = someObj.foo.bar.func(x, y, z).baz + 5
, Это не только ужасно избыточно и долго, но и медленнее. Поэтому нужно было быtmp := someObj.foo.bar.func(x, y, z); tmp.baz = tmp.bar + 5
. Это нормально, но это много шума для простой вещи. Это на самом деле очень близко к тому, что происходит во время выполнения, но писать это утомительно, и простое использование+=
переключит работу на компилятор / интерпретатор.Преимущество
+=
и других подобных операторов неоспоримо, а привыкнуть к ним - только вопрос времени.источник
Это правда, что он короче и проще, и правда, что он, вероятно, был вдохновлен базовым языком ассемблера, но причина, по которой он лучше всего подходит, заключается в том, что он предотвращает целый класс ошибок и облегчает просмотр кода и уверенность что оно делает.
С участием
Поскольку задействовано только одно имя переменной, вы уверены, что делает оператор.
С участием
RidiculouslyComplexName = RidiculosulyComplexName + 1;
Всегда есть сомнения, что обе стороны абсолютно одинаковы. Вы видели ошибку? Еще хуже, когда присутствуют подписчики и квалификаторы.
источник
Хотя обозначение + = идиоматично и короче, это не причины, по которым его легче читать. Наиболее важной частью чтения кода является сопоставление синтаксиса со значением, и поэтому, чем ближе синтаксис соответствует мыслительным процессам программиста, тем более читабельным он будет (это также причина, по которой шаблонный код плох: он не является частью мысли процесс, но все же необходимо сделать код функции). В этом случае мысль «увеличить переменную х на 5», а не «пусть х будет значением х плюс 5».
Есть и другие случаи, когда более короткая запись плоха для читабельности, например, когда вы используете троичный оператор, где
if
оператор был бы более уместным.источник
Чтобы понять, почему эти операторы используют языки «стиля С», вот выдержка из K & R 1st Edition (1978), 34 года назад:
Я думаю, из этого отрывка ясно, что Брайан Керниган и Деннис Ритчи (K & R) полагали, что составные операторы присваивания помогли с читабельностью кода.
С тех пор, как K & R написал это, прошло много времени, и с тех пор многие «лучшие практики» о том, как люди должны писать код, изменились или эволюционировали. Но этот вопрос programmers.stackexchange - это первый случай, когда я вспоминаю, как кто-то высказывал жалобу на удобочитаемость составных назначений, поэтому мне интересно, считают ли многие программисты их проблемой? Опять же, когда я набираю это, у вопроса 95 голосов, поэтому, возможно, люди находят их раздражающими при чтении кода.
источник
Помимо читабельности, они на самом деле делают разные вещи:
+=
не нужно оценивать его левый операнд дважды.Например,
expr = expr + 5
будет оцениватьсяexpr
дважды (при условии, чтоexpr
это нечисто).источник
expr = expr + 5
иexpr += 5
expr
есть побочные эффекты.expr
есть побочные эффекты, тогдаexpr = expr + 5
нужно вызывать эти побочные эффекты дважды.volatile
являются побочными эффектамиx=x+5
иx+=5
имеют те же побочные эффекты, когдаx
естьvolatile
Помимо очевидных достоинств, которые другие люди описали очень хорошо, когда у вас очень длинные имена, он более компактен.
или же
источник
Это сжато.
Это намного короче, чтобы напечатать. В нем задействовано меньше операторов. Он имеет меньшую площадь поверхности и меньше возможностей для путаницы.
Он использует более конкретный оператор.
Это надуманный пример, и я не уверен, что фактические компиляторы реализуют это. x + = y фактически использует один аргумент и один оператор и модифицирует x на месте. x = x + y может иметь промежуточное представление x = z, где z - x + y. Последний использует два оператора, сложение и присваивание, и временную переменную. Один оператор делает супер ясным, что сторона значения не может быть ничем иным, кроме y, и не нуждается в интерпретации. И теоретически может быть какой-нибудь причудливый процессор с оператором плюс-равно, который работает быстрее, чем оператор плюс и оператор назначения последовательно.
источник
ADD
инструкции на многих процессорах есть варианты , которые работают непосредственно на регистрах или памяти с использованием других регистров, памяти или констант в качестве второго слагаемого. Не все комбинации доступны (например, добавить память в память), но их достаточно, чтобы быть полезными. В любом случае, любой компилятор с приличным оптимизатором будет знать, что нужно генерировать тот же код,x = x + y
что и дляx += y
.Это хорошая идиома. Быстрее это или нет, зависит от языка. В C это быстрее, потому что это переводит в инструкцию, чтобы увеличить переменную справа. Современные языки, включая Python, Ruby, C, C ++ и Java, поддерживают синтаксис op =. Он компактен, и вы к нему быстро привыкаете. Поскольку вы увидите это в коде других людей (OPC), вы также можете привыкнуть к нему и использовать его. Вот что происходит на нескольких других языках.
В Python типизация
x += 5
все еще вызывает создание целочисленного объекта 1 (хотя его можно извлечь из пула) и потерю целочисленного объекта, содержащего 5.В Java это вызывает молчаливое приведение. Попробуйте набрать
источник
x+=5
так же мало смысла, какx=x+5
; например, в Haskell последнее не приводитx
к увеличению на 5 - вместо этого он создает бесконечный цикл рекурсии. Кто бы хотел для этого стенографию?+=
не столько зависит от языка, сколько зависит от процессора . Например, X86 - это двухадресная архитектура, она поддерживается только+=
изначально. Оператор likea = b + c;
должен быть скомпилирован так,a = b; a += c;
потому что просто нет инструкции, которая могла бы поместить результат сложения где-либо еще, кроме как на место одного из слагаемых. Архитектура Power, напротив, представляет собой трехадресную архитектуру, для которой нет специальных команд+=
. На этой архитектуре операторыa += b;
иa = a + b;
всегда компилируются в один и тот же код.Такие операторы, как
+=
очень полезны, когда вы используете переменную в качестве аккумулятора , то есть промежуточного итога:Гораздо легче читать, чем:
В первом случае, концептуально, вы изменяете значение в
x
. Во втором случае вы вычисляете новое значение и присваиваете егоx
каждому разу. И хотя вы, вероятно, никогда бы не написали такой простой код, идея остается прежней ... основное внимание уделяется тому, что вы делаете с существующим значением, а не создаете какое-то новое значение.источник
Учти это
D0 ты действительно хочешь написать
источник
Другие ответы предназначены для более распространенных случаев, но есть и другая причина: в некоторых языках программирования он может быть перегружен; например, Скала .
Маленький урок Scala:
Если класс определяет только
+
оператор,x+=y
это действительно ярлыкx=x+y
.Однако если класс перегружается
+=
, это не так:Кроме того, операторы
+
и+=
являются двумя отдельными операторами (и не только они: + a, ++ a, a ++, a + ba + = b также являются различными операторами); на языках, где доступна перегрузка операторов, это может создать интересные ситуации. Как описано выше - если вы будете перегружать+
оператора, чтобы выполнить добавление, имейте в виду, что+=
его также придется перегружать.источник
+=
а затем определяютa+b
как «сделать копиюa
, надетьb
ее+=
, вернуть копиюa
».Скажите это один раз и только один раз:
x = x + 1
я говорю «х» дважды.Но никогда не пишите «a = b + = 1», иначе нам придется убить 10 котят, 27 мышей, собаку и хомяка.
Вы никогда не должны изменять значение переменной, так как это упрощает проверку правильности кода - см. Функциональное программирование. Однако, если вы делаете, то лучше не говорить вещи только один раз.
источник