Присваивание в программировании: что изменяет операция и как она работает

Присваивание в программировании: что изменяет операция и как она работает Полезное

Операция присваивания изменяет значение переменной, записанной слева от знака: программа вычисляет выражение справа и записывает результат в эту переменную, а прежнее значение переменной теряется. Выражение справа при этом не меняется: в x = y + 1 меняется только x, а y остается прежним.

Ниже — как присваивание выполняется по шагам, чем = отличается от == и :=, как работает составное присваивание (+=) и почему в Python и JavaScript «присвоить» не всегда значит «скопировать». Примеры проверены на Python 3.14, Java 25 (javac 25.0.4), Node.js 24, Free Pascal 3.2.2 и Apple clang 21 (сентябрь 2026).

Коротко о терминах

Четыре слова, которые в этой теме легко смешать:

  • переменная — имя, через которое программа обращается к данным;
  • значение — то, что сейчас хранится под этим именем (число, строка, ссылка на объект);
  • левая часть (lvalue) — то, куда записываем: переменная, элемент массива, поле объекта;
  • правая часть (rvalue) — выражение, которое вычисляется в значение.

Присваивание переменной меняет связь «имя -> значение», а объект, на который имя указывало раньше, оно не меняет — это важно для списков и объектов, об этом ниже. Исключение — присваивание элементу или полю (a[0] = 9, obj.name = "x"): здесь меняется сам контейнер, и изменение видно через все имена, которые на него ссылаются.

Как выполняется присваивание: шаг за шагом

Минимальный пример на Python:

x = 5
y = x
x = x + 1
print(x, y)

Результат:

6 5

Проследим состояние переменных после каждой строки:

Строка Что вычисляется справа x y
x = 5 5 5 нет
y = x значение x, то есть 5 5 5
x = x + 1 5 + 1 = 6 6 5

Запись x = x + 1 с точки зрения математики абсурдна, а для программы нормальна: сначала вычисляется правая часть со старым значением x, затем результат записывается в x. Переменная y получила копию значения 5 в момент присваивания и дальше от x не зависит.

Общий порядок для простого присваивания:

  1. Вычислить выражение справа.
  2. Определить, куда писать (переменная, элемент a[i], поле obj.name).
  3. Записать значение; старое значение этой переменной больше недоступно через нее.

Это упрощенная модель. В каком порядке вычисляются части левой и правой стороны, если в них есть вызовы функций или индексы, зависит от языка. В Python правая часть вычисляется раньше целей слева. В C порядок вычисления операндов во многих случаях не определен стандартом, поэтому выражения вида a[i] = i++ там писать нельзя.

Знак присваивания в разных языках: =, == и :=

Главная путаница новичка — знак =. В математике это равенство, а в большинстве языков — команда «записать».

Язык Присваивание Сравнение на равенство Заметка
Pascal, школьный алгоритмический язык := = знаки разные, путаницы нет
C, C++, Java, C# = == присваивание — выражение со значением
Python = (инструкция), := (выражение, с 3.8) == := называют «моржом»
JavaScript = === (строгое), == (с приведением типов) const запрещает повторное присваивание
Go =, := (объявление с присваиванием) == := в Go и Python значит разное

Тот же сценарий на Pascal, где знаки не путаются:

program Assign;
var
  x: integer;
begin
  x := 5;             { присваивание }
  x := x + 1;
  if x = 6 then       { сравнение }
    writeln('x = ', x);
end.

Вывод (Free Pascal 3.2.2):

x = 6

Ошибка: = вместо == в условии

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

Неверный код на C:

#include <stdio.h>

int main(void) {
    int x = 0;
    if (x = 5) {            /* ошибка: присваивание вместо сравнения */
        printf("x = %d\n", x);
    }
    return 0;
}

Фактический результат: программа компилируется и печатает x = 5. Условие x = 5 записывает 5 в x, значение выражения равно 5, а любое ненулевое число в C — истина. Clang предупреждает уже без дополнительных флагов (текст Apple clang 21), GCC — только с -Wall или -Wparentheses, и формулировка у него другая:

warning: using the result of an assignment as a condition without parentheses [-Wparentheses]

Исправление: if (x == 5). В GCC без -Wall предупреждения не будет вовсе, поэтому флаг стоит включать всегда. Если присваивание в условии нужно намеренно, его берут в дополнительные скобки: if ((x = f())) — так компилятор понимает, что это не опечатка.

В Java та же строка не компилируется, потому что условие обязано быть boolean:

Cmp.java:4: error: incompatible types: int cannot be converted to boolean
        if (x = 5) {
              ^

Python 3.14 тоже не пропускает if x = 5: и сам подсказывает исправление:

SyntaxError: invalid syntax. Maybe you meant '==' or ':=' instead of '='?

Моржовый оператор := в Python

В Python = — это инструкция, ее нельзя использовать внутри условия. Если нужно присвоить и сразу проверить значение, есть выражение присваивания :=:

data = [3, 8, 15, 2]
if (n := len(data)) > 3:
    print(f"элементов: {n}")

Вывод: элементов: 4. Здесь n получает длину списка, и это же значение участвует в сравнении.

Присваивание как выражение и цепочки

В C, C++, Java и JavaScript присваивание — выражение: оно возвращает записанное значение. Поэтому работает цепочка a = b = c = 7: сначала c = 7, результат 7 идет в b, затем в a. В Pascal присваивание — только инструкция, у него нет значения.

В Python цепочка x = y = [] выглядит так же, но устроена иначе: это отдельный синтаксис, который связывает оба имени с одним и тем же объектом:

x = y = []
x.append(1)
print(y)

Вывод: [1]. Список один, имен два. Если нужны два независимых списка — пишите x = [] и y = [] отдельно.

Присвоить — не всегда значит скопировать

Для чисел пример y = x выше выглядит как копирование. Для изменяемых объектов (списки, словари, объекты классов) в Python присваивание копирует ссылку, а не данные:

a = [1, 2]
b = a          # второе имя для того же списка
b.append(3)    # изменение объекта, а не переменной
print(a, b, a is b)

b = [9]        # присваивание: b теперь связана с новым списком
print(a, b, a is b)

Вывод:

[1, 2, 3] [1, 2, 3] True
[1, 2, 3] [9] False

Здесь видны два разных действия. b.append(3) меняет сам список, и изменение видно через оба имени. b = [9] — это присваивание: меняется только то, на что указывает b, а список под именем a остается прежним. Если нужна независимая копия, используют b = a.copy() или b = list(a) (для вложенных структур — copy.deepcopy).

В JavaScript то же разделение закреплено в const: оно запрещает повторное присваивание переменной, но не изменение объекта.

const nums = [1, 2];
nums.push(3);          // изменение массива разрешено
console.log(nums);     // [ 1, 2, 3 ]
nums = [];             // повторное присваивание запрещено

Последняя строка в Node.js 24 падает с ошибкой TypeError: Assignment to constant variable.

Составное присваивание: +=, -= и другие

Составной оператор объединяет арифметическую или побитовую операцию с записью результата: x += 3 читается как «увеличить x на 3».

Оператор Пример Смысл для чисел
+= x += 3 x = x + 3
-= x -= 3 x = x — 3
*= x *= 2 x = x * 2
/= x /= 2 x = x / 2 (в Python результат — float)
//= x //= 2 целочисленное деление, только Python
%= x %= 3 остаток от деления
**= x **= 2 возведение в степень (Python, JavaScript)
&=, |=, ^=, <<=, >>= x <<= 1 побитовые операции и сдвиги

Формула «x += 3 это x = x + 3» — упрощение. Есть два важных отличия.

Первое: левая часть вычисляется один раз. В a[f()] += 1 функция f() вызывается один раз, а в a[f()] = a[f()] + 1 — дважды.

Второе: для изменяемых объектов Python += может менять объект на месте, а x = x + ... создает новый:

a = [1, 2]
alias = a
a += [3]        # для списка: изменение на месте
print(alias, a is alias)

a = [1, 2]
alias = a
a = a + [3]     # новый список, alias смотрит на старый
print(alias, a is alias)

s = "ab"
t = s
s += "c"        # строка неизменяема: создается новая
print(t, s)

Вывод:

[1, 2, 3] True
[1, 2] False
ab abc

В Java у составного присваивания есть еще одна особенность — неявное приведение типа к типу левой части:

public class Main {
    public static void main(String[] args) {
        byte b = 10;
        b += 5;            // неявное приведение к byte
        System.out.println(b);
    }
}

Программа печатает 15. А строка b = b + 5; не компилируется: b + 5 имеет тип int, и javac 25 сообщает error: incompatible types: possible lossy conversion from int to byte. У этого удобства есть цена: при += переполнение byte пройдет молча.

Обмен значений и множественное присваивание

Чтобы поменять значения двух переменных, в C и Java нужна временная переменная: tmp = a; a = b; b = tmp;. В Python и JavaScript есть множественное присваивание:

a, b = 1, 2
a, b = b, a
print(a, b)   # 2 1

В JavaScript то же делается деструктуризацией: [a, b] = [b, a];. В Python правая часть b, a сначала полностью вычисляется в кортеж, и только потом значения раскладываются по именам слева — поэтому старое значение a не теряется.

Встречаются и трюки обмена без временной переменной через сложение (a = a + b; b = a - b; a = a - b) или XOR. Они работают только для чисел, сложение может переполниться в языках с фиксированной разрядностью, а XOR-вариант обнуляет значение, если обе стороны — одна и та же ячейка памяти. В рабочем коде лучше временная переменная или множественное присваивание.

Типичные ошибки с присваиванием

Симптом Причина Что сделать
Условие в C всегда истинно, переменная «сама» меняется = вместо == в if писать ==, компилировать с -Wall
NameError в Python / ошибка компиляции «might not have been initialized» в Java переменной не присвоили значение до чтения присвоить начальное значение до первого использования
Изменили копию списка, а поменялся оригинал b = a копирует ссылку a.copy(), list(a) или copy.deepcopy
Два «разных» списка ведут себя как один x = y = [] создать списки отдельными присваиваниями
TypeError: Assignment to constant variable. повторное присваивание const использовать let или менять содержимое объекта

Выводы

  • Операция присваивания изменяет значение переменной слева от знака; правая часть сначала вычисляется, затем результат записывается, старое значение теряется.
  • = в C, Java, Python и JavaScript — присваивание, сравнение — == (в JavaScript обычно ===); в Pascal присваивание — :=, сравнение — =.
  • В C присваивание — выражение со значением, поэтому if (x = 5) компилируется и работает неправильно; Java и Python такой код не пропускают.
  • Составное присваивание (+= и др.) вычисляет левую часть один раз; в Python для списков оно меняет объект на месте, в Java — неявно приводит тип.
  • Для списков и объектов присваивание копирует ссылку, а не данные: изменение объекта и повторное присваивание — разные действия.

Где применяется / связь с практикой

Освойте тему на практике

Присваивание встречается почти в каждой строке программы, и ошибки с ним — одни из самых частых у начинающих: перепутанные = и ==, неожиданно общий список, «потерянное» значение при обмене. Разница между изменением объекта и повторным присваиванием в Python дальше всплывает в функциях, аргументах по умолчанию и работе с коллекциями. Если хочется разобрать переменные, типы и изменяемость данных на задачах с проверкой, посмотрите курс «Python-разработчик. Базовый уровень». Разобрать отдельные темы с преподавателями можно на открытых уроках Otus.

FAQ

Можно ли присвоить значение выражению, например x + 1 = 5?
Нет. Слева должно стоять место для записи: переменная, элемент массива или поле объекта. Python на такую строку выдает SyntaxError, C и Java — ошибку компиляции.

Чем присваивание отличается от инициализации?
Инициализация задает значение при объявлении переменной (int x = 5;), присваивание меняет значение уже существующей переменной. В C++ это разные операции: для объектов классов при инициализации вызывается конструктор, а при присваивании — оператор присваивания.

Почему в школьной информатике пишут :=, а в Python =?
Школьный алгоритмический язык и Pascal используют :=, чтобы не путать присваивание с математическим равенством. C и выросшие из него языки выбрали короткий = для присваивания и == для сравнения; Python взял ту же пару.

OTUS Журнал