Оператор присваивания в Python: Пишите надежные присваивания

Оглавление

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

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

В этом руководстве вы узнаете:

  • Используйте оператор присваивания в Python для написания инструкций присваивания
  • Воспользуйтесь преимуществами расширенных назначений в Python
  • Изучите варианты присвоения, такие как выражения присвоения и управляемые атрибуты
  • Узнайте о незаконных и опасных назначениях в Python

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

Бесплатный исходный код: Нажмите здесь, чтобы скачать бесплатный исходный код оператора присваивания, который вы будете использовать для написания инструкций присваивания, позволяющих вам создавать, инициализировать и обновлять переменные в вашем коде.

Операторы присваивания и оператор присваивания

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

Синтаксис оператора присваивания

Чтобы создать новую переменную или обновить значение существующей переменной в Python, вы будете использовать оператор присваивания. Этот оператор состоит из следующих трех компонентов:

  1. Левый операнд, который должен быть переменной
  2. Оператор присваивания (=)
  3. Правый операнд, который может быть конкретным значением, объектом или выражением

Вот как обычно выглядит оператор присваивания в Python:

variable = expression


Здесь variable представляет собой общую переменную Python, в то время как expression представляет любой объект Python, который вы можете предоставить в виде конкретного значения, также известного как литерал —или выражение, вычисляющее значение.

Чтобы выполнить оператор присваивания, подобный описанному выше, Python выполняет следующие действия:

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

На втором шаге показано, что переменные в Python работают иначе, чем в других языках программирования. В Python переменные не являются контейнерами для объектов. Переменные Python указывают на значение или объект через его адрес в памяти. Они хранят адреса памяти, а не объекты.

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

Оператор присваивания

Центральным компонентом оператора присваивания является оператор присваивания. Этот оператор представлен символом =, который разделяет два операнда:

  1. Переменная
  2. Значение или выражение, результатом вычисления которого является конкретное значение

Операторы - это специальные символы, которые выполняют математические,, логические и побитовые операции в языке программирования. Объекты (или object), над которыми работает оператор, называются операндами.

Унарные операторы, такие как not Логический оператор, работают с одним объектом или операндом, в то время как двоичные операторы работают с двумя. Это означает, что оператор присваивания является двоичным оператором.

Примечание: Как и C, Python использует == для сравнения на равенство и = для присваиваний. В отличие от C, Python не позволяет вам случайно использовать оператор присваивания (=) при сравнении на равенство.

Равенство - это симметричное отношение, а присваивание - нет. Например, выражение a == 42 эквивалентно 42 == a. В отличие от этого, утверждение a = 42 является правильным и законным, в то время как 42 = a недопустимо. Вы узнаете больше о незаконных назначениях позже.

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

Вот несколько примеров присваиваний в Python:

>>> number = 42
>>> greeting = "Hello, World!"

>>> total = 15 + 25
>>> is_true = 42 < 84


В первых двух примерах присваивания в этом фрагменте кода используются конкретные значения, также известные как литералы, для создания и инициализации number и greeting. В третьем примере результат математического выражения присваивается переменной total, в то время как в последнем примере используется логическое выражение .

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

Вот краткий пример того, как работает эта функция:

>>> number = 42
>>> id(number)
4311827984


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

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

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

Теперь, когда вы знаете основы как написать инструкцию присваивания, пришло время разобраться с почему вы хотели бы использовать one.

Назначения и переменные

Оператор присваивания - это явный способ связать имя с объектом в Python. Вы можете использовать этот оператор для двух основных целей:

  1. Создание и инициализация новых переменных
  2. Обновление значений существующих переменных

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

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

Операторы присваивания

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

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

В отличие от математических уравнений, в присваиваниях Python левый операнд должен быть переменной, а не выражением или значением. Например, следующая конструкция недопустима, и Python помечает ее как недопустимый синтаксис:

>>> a = 3
>>> b = 4
>>> hypotenuse ** 2 = a ** 2 + b ** 2
    ...
SyntaxError: cannot assign to expression here.
    Maybe you meant '==' instead of '='?


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

Чтобы исправить эту конструкцию и преобразовать ее в допустимое назначение, вам нужно будет сделать что-то вроде следующего:

>>> from math import sqrt

>>> a = 3
>>> b = 4
>>> hypotenuse = sqrt(a ** 2 + b ** 2)
>>> hypotenuse
5.0


В этом фрагменте кода вы сначала импортируете функцию sqrt() из модуля math. Затем вы изолируете переменную hypotenuse в исходном уравнении с помощью функции sqrt(). Теперь ваш код работает корректно.

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

Другой синтаксис присваивания

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

# Base assignments
variable = value  # A literal, such as None, 0, 3.14, "Hello", {}
variable = expression  # Math, Boolean, bitwise expression, function calls...

# Multiple assignments
variable_1 = variable_2 = ... = variable_n = expression

# Parallel assignments
variable_1, variable_2, ..., variable_n = value_1, value_2, ..., value_n
variable_1, variable_2, ..., variable_n = exp_1, exp_2, ..., exp_n

# Augmented assignments
existing_variable += value
existing_variable += expression

# Parallel assignments with iterable unpacking
variable_1, variable_2, ..., variable_n  = n_length_iterable
(variable_1, variable_2, ..., variable_n) = n_length_iterable
[variable_1, variable_2, ..., variable_n] = n_length_iterable
variable, *bag_variable, ..., variable_n = unknown_length_iterable


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

Читайте дальше, чтобы увидеть инструкции присваивания в действии!

Операторы присваивания в действии

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

Вы можете использовать операторы присваивания с простыми именами, например number или counter. Вы также можете использовать присваивания в более сложных сценариях, например, с помощью:

  • Полные имена атрибутов, например user.name
  • Индексы и фрагментов изменяемых последовательностей, таких как a_list[i] и a_list[i:j]
  • Словарь ключи, например a_dict[key]

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

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

Инициализация и обновление переменных

Наиболее простым вариантом использования оператора присваивания является создание новой переменной и ее инициализация с использованием определенного значения или выражения:

>>> counter = 0
>>> celsius = 25
>>> fahrenheit = (celsius * 9 / 5) + 32
>>> user_template = {"id": None, "name": "", "permissions": ("r",)}
>>> welcome_message = "Welcome to Real Python!"
>>> is_empty = False


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

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

Рассмотрим следующие примеры:

>>> greeting = "Hello, World!"
>>> greeting
'Hello, World!'

>>> greeting = "Hi, Pythonistas!"
>>> greeting
'Hi, Pythonistas!'


В этих примерах выполняются два последовательных присвоения одной и той же переменной. В первом примере строка "Hello, World!" присваивается новой переменной с именем greeting.

Второе присваивание обновляет значение greeting, переназначая ему строку "Hi, Pythonistas!". В этом примере исходное значение greeting — строка "Hello, World!" — потеряно и удалено в корзину. С этого момента вы не сможете получить доступ к старой строке "Hello, World!".

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

Поскольку назначения также определяют тип данных для своих целевых переменных, ваш код также может случайно изменить тип данной переменной во время выполнения. Подобное изменение может привести к возникновению ошибок, таких как AttributeError исключения. Помните, что у строк нет таких методов и атрибутов, как, например, у списков или словарей.

Создание нескольких переменных, ссылающихся на один и тот же объект

В Python вы можете заставить несколько переменных ссылаться на один и тот же объект в строке множественного назначения. Это может быть полезно, когда вы хотите инициализировать несколько похожих переменных, используя одно и то же начальное значение:

>>> letter_counter = word_counter = 0
>>> id(letter_counter) == id(word_counter)
True


В этом примере вы объединяете два оператора присваивания в одну строку. Таким образом, две ваши переменные ссылаются на одно и то же начальное значение 0. Обратите внимание, что обе переменные содержат один и тот же адрес памяти, поэтому они указывают на один и тот же экземпляр 0.

Когда дело доходит до целочисленных переменных, Python демонстрирует любопытное поведение. Он предоставляет числовой интервал, в котором множественные присваивания ведут себя так же, как независимые присваивания. Рассмотрим следующие примеры:

>>> # Independent assignments
>>> n = 42
>>> m = 42
>>> id(n) == id(m)
True

>>> # Multiple assignments
>>> x = y = 42
>>> id(x) == id(y)
True


Для создания n и m используются независимые присвоения. Следовательно, они должны указывать на разные экземпляры числа 42. Однако обе переменные содержат один и тот же объект, что вы подтверждаете, сравнивая их соответствующие адреса в памяти.

Теперь проверьте, что происходит, когда вы используете большее начальное значение:

>>> n = 300
>>> m = 300
>>> id(x) == id(y)
False

>>> x = y = 300
>>> id(x) == id(y)
True


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

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

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

# interning.py

from platform import python_version

interning = [
    x
    for x, y in zip(range(-10, 500), range(-10, 500))
    if x is y
]

print(
    f"Interning interval for Python {python_version()} is:"
    f" [{interning[0]} to {interning[-1]}]"
)


Этот скрипт поможет вам определить интервал стажировки, сравнивая целые числа от -10 до 500. Если вы запустите скрипт из командной строки, то получите результат, похожий на следующий:

$ python interning.py
Interning interval for Python 3.11.0 is: (-5 to 256)


Этот вывод означает, что если вы используете одно число между -5 и 256 для инициализации нескольких переменных в независимых инструкциях, то все эти переменные будут указывать на один и тот же объект, что поможет вам сэкономить небольшие биты данных. память в вашем коде.

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

Обновление списков с помощью индексов и срезов

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

a_list[index] = expression

a_list[start:stop:step] = expression


В первой конструкции expression может возвращать любой объект Python, включая другой список. Во второй конструкции expression должен возвращать последовательность значений в виде списка, кортежа или любой другой последовательности. Вы получите TypeError, если expression возвращает одно значение.

Примечание: При создании объектов slice можно использовать до трех аргументов. Этими аргументами являются start, stop, и step. Они определяют число, с которого начинается срез, число, на котором срез должен прекращаться для получения значений, и шаг между значениями.

Вот пример обновления отдельного значения в списке:

>>> numbers = [1, 2, 7, 4, 5]
>>> numbers
[1, 2, 7, 4, 5]

>>> numbers[2] = 3
>>> numbers
[1, 2, 3, 4, 5]


В этом примере вы обновляете значение по индексу 2, используя инструкцию присваивания. Исходный номер с этим индексом был 7, а после присвоения этот номер будет равен 3.

Примечание: Использование индексов и оператора присваивания для обновления значения в кортеже или символа в строке невозможно, поскольку кортежи и строки являются неизменяемыми типами данных в Python.

Их неизменность означает, что вы не можете изменить их элементы на месте:

>>> numbers = (1, 2, 2, 4, 5)
>>> numbers[2] = 3
Traceback (most recent call last):
    ...
TypeError: 'tuple' object does not support item assignment

>>> letters = "ABcDE"
>>> letters[2] = "C"
Traceback (most recent call last):
    ...
TypeError: 'str' object does not support item assignment


Вы не можете использовать оператор присваивания для изменения отдельных элементов в кортежах или строках. Эти типы данных являются неизменяемыми и не поддерживают присвоение элементов.

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

>>> numbers[5] = 6
Traceback (most recent call last):
    ...
IndexError: list assignment index out of range


В этом примере вы пытаетесь добавить новое значение в конец numbers, используя несуществующий индекс. Это присвоение недопустимо, поскольку нет способа гарантировать, что новые индексы будут последовательными. Если вы когда-нибудь захотите добавить одно значение в конец списка, используйте метод .append().

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

>>> letters = ["A", "b", "c", "D"]
>>> letters[1:3] = ["B", "C"]
>>> letters
['A', 'B', 'C', 'D']

>>> letters[3:] = ("F", "G")
>>> letters
['A', 'B', 'C', 'F', 'G']

>>> letters[3:3] = ["D"]
>>> letters
['A', 'B', 'C', 'D', 'F', 'G']

>>> letters[1::2] = ["b", "d", "g"]
>>> letters
['A', 'b', 'C', 'd', 'F', 'g']


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

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

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

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

Добавление и обновление ключей словаря

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

a_dict[existing_key] = expression

a_dict[new_key] = expression


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

Например, чтобы обновить существующий ключ, вы можете сделать что-то вроде этого:

>>> inventory = {"apple": 100, "orange": 80, "banana": 120}
>>> inventory
{'apple': 100, 'orange': 80, 'banana': 120}

>>> inventory["orange"] = 140
>>> inventory
{'apple': 100, 'orange': 140, 'banana': 120}


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

Хотя вы не можете добавлять новые значения в список путем присваивания, словари позволяют добавлять новые пары ключ-значение с помощью оператора присваивания. В приведенном ниже примере вы добавляете ключ lemon в inventory:

>>> inventory["lemon"] = 100
>>> inventory
{'apple': 100, 'orange': 140, 'banana': 120, 'lemon': 100}


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

Выполнение параллельных заданий

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

Вот общий синтаксис для параллельных назначений в Python:

variable_1, variable_2, ..., variable_n = value_1, value_2, ..., value_n

variable_1, variable_2, ..., variable_n = exp_1, exp_2, ..., exp_n

(variable_1, variable_2, ..., variable_n) = exp_1, exp_2, ..., exp_n

[variable_1, variable_2, ..., variable_n] = exp_1, exp_2, ..., exp_n


Обратите внимание, что левая часть инструкции может быть либо кортежем, либо списком переменных. Помните, что для создания кортежа вам просто нужен ряд элементов, разделенных запятыми. В данном случае эти элементы должны быть переменными.

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

В следующем примере вы вычисляете два решения квадратного уравнения, используя параллельное задание:

>>> from math import sqrt

>>> a, b, c = 2.0, -1.0, -4.0

>>> x1, x2 = (
...     (-b - sqrt(b**2 - 4 * a * c)) / (2 * a),
...     (-b + sqrt(b**2 - 4 * a * c)) / (2 * a),
... )

>>> f"{x1=}, {x2=}"
'x1=-1.1861406616345072, x2=1.6861406616345072'


В этом примере вы сначала импортируете sqrt() из модуля math. Затем вы инициализируете коэффициенты уравнения в параллельном задании.

Решение уравнения вычисляется в другом параллельном задании. Левый операнд содержит набор из двух переменных, x1 и x2. Правый операнд состоит из набора выражений, которые вычисляют решения уравнения. Обратите внимание, что каждый результат присваивается каждой переменной по позиции.

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

>>> previous_value = 42
>>> next_value = 43

>>> next_value, previous_value = previous_value, next_value

>>> previous_value
43
>>> next_value
42


Выделенная строка совершает волшебство и меняет местами значения previous_value и next_value одновременно. Обратите внимание, что в языке программирования, который не поддерживает такого рода присвоения, вам пришлось бы использовать временную переменную для получения того же эффекта:

>>> previous_value = 42
>>> next_value = 43

>>> temp = previous_value
>>> previous_value = next_value
>>> next_value = temp

>>> previous_value
43
>>> next_value
42


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

В качестве конкретного примера того, когда вам нужно поменять местами значения между переменными, предположим, вы изучаете, как реализовать алгоритм пузырьковой сортировки , и у вас появилась следующая функция:

>>> def bubble_sort_list(a_list):
...     n = len(a_list)
...     for i in range(n):
...         is_sorted = True
...         for j in range(n - i - 1):
...             if a_list[j] > a_list[j + 1]:
...                 a_list[j], a_list[j + 1] = a_list[j + 1], a_list[j]
...                 is_sorted = False
...         if is_sorted:
...             break
...     return a_list
...

>>> bubble_sort_list([1, 3, 2, 4, 7, 6, 3, 8, 9, 1])
[1, 1, 2, 3, 3, 4, 6, 7, 8, 9]


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

Распаковка повторяющихся объектов

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

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

Вот общий синтаксис для итеративной распаковки в Python:

variable_1, variable_2, ..., variable_n  = n_length_iterable

(variable_1, variable_2, ..., variable_n) = n_length_iterable

[variable_1, variable_2, ..., variable_n] = n_length_iterable

variable, *bag_variable, ..., variable_n = unknown_length_iterable


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

>>> numbers = [1, 2, 3, 4]

>>> one = numbers[0]
>>> two = numbers[1]
>>> three = numbers[2]
>>> four = numbers[3]

>>> one
1
>>> two
2
>>> three
3
>>> four
4


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

>>> numbers = [1, 2, 3, 4]

>>> one, two, three, four = numbers

>>> one
1
>>> two
2
>>> three
3
>>> four
4


Список numbers в правой части содержит четыре значения. Оператор присваивания преобразует эти значения в четыре переменные в левой части инструкции. Значения в numbers присваиваются переменным в том же порядке, в котором они отображаются в итерационной таблице. Присваивание выполняется по позиции.

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

В приведенном выше примере показана наиболее распространенная форма итерационной распаковки в Python. Главное условие для работы примера заключается в том, чтобы количество переменных соответствовало количеству значений в итерационной таблице.

Что делать, если вы заранее не знаете повторяемую длину? Сработает ли распаковка? Это сработает, если вы используете оператор * для упаковки нескольких значений в одну из ваших целевых переменных.

Например, предположим, что вы хотите разложить первое и второе значения в numbers на две разные переменные. Кроме того, вы хотели бы упаковать остальные значения в одну переменную, которая удобно называется rest. В этом случае вы можете использовать оператор распаковки, как в следующем коде:

>>> first, second, *rest = numbers
>>> first
1
>>> second
2
>>> rest
[3, 4]


В этом примере first и second содержат первое и второе значения в numbers соответственно. Эти значения присваиваются по позиции. Оператор * преобразует все оставшиеся значения во входной итерации в rest.

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

>>> *head, last = numbers
>>> head
[1, 2, 3]
>>> last
4

>>> head, *body, tail = numbers
>>> head
1
>>> body
[2, 3]
>>> tail
4

>>> *head, *rest = numbers
    ...
SyntaxError: multiple starred expressions in assignment


Повторяющийся оператор распаковки работает в любой позиции вашего списка переменных. Обратите внимание, что для каждого задания можно использовать только один оператор распаковки. Использование более чем одного оператора распаковки запрещено и приводит к возникновению проблемы. SyntaxError.

Удаление ненужных значений из iterable - это обычный вариант использования оператора распаковки iterable. Рассмотрим следующий пример:

>>> *_, useful = numbers
>>> useful
4
>>> _
[1, 2, 3]


В Python, если вы хотите указать, что переменная использоваться не будет, то в качестве имени переменной используется знак подчеркивания (_). В этом примере useful содержит единственное значение, которое вам нужно использовать из входной итеративной переменной. Переменная _ является заполнителем, который гарантирует, что распаковка будет работать корректно. Вы не будете использовать значения, которые попадают в эту одноразовую переменную.

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

Для этого вы можете использовать индекс -1:

>>> useful = numbers[-1]
>>> useful
4


Использование -1 дает вам доступ к последнему элементу любого типа данных последовательности. В отличие от этого, если вы имеете дело с итераторами, то вы не сможете использовать индексы. Вот тогда вам на помощь приходит синтаксис *_.

Шаблон, использованный в приведенном выше примере, удобен, когда у вас есть функция, возвращающая несколько значений, и вам нужно включить в свой код только несколько из этих значений. Хорошим примером такой ситуации может служить функция os.walk().

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

  • Путь к текущему каталогу в виде строки
  • Имена всех ближайших подкаталогов в виде списка строк
  • Имена всех файлов в текущем каталоге в виде списка строк

Теперь скажите, что вы хотите просмотреть свой домашний каталог и перечислить только файлы. Вы можете сделать что-то вроде этого:

>>> import os

>>> for content in os.walk("/path/to/your/home"):
...     *_, filenames = content
...     print(filenames)
...


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

В отличие от этого, переменная filenames будет содержать список файлов в текущем каталоге, то есть те данные, которые вам нужны. Код выведет список имен файлов. Попробуйте!

Предоставление значений аргументов по умолчанию

Оператор присваивания также полезен, когда вам нужно указать значения аргументов по умолчанию в ваших функциях и методах. Значения аргументов по умолчанию позволяют определить функции, которые принимают аргументы с разумными значениями по умолчанию. Эти значения по умолчанию позволяют вам вызывать функцию с определенными значениями или просто полагаться на значения по умолчанию.

В качестве примера рассмотрим следующую функцию:

>>> def greet(name="World"):
...     print(f"Hello, {name}!")
...


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

примечание: по ОПТОСОЗ 8, руководства по стилю для кода Python, вы не должны использовать пробелы вокруг оператора присваивания при предоставлении умолчанию значения аргументов в функции определения.

Вот как работает эта функция:

>>> greet()
Hello, World!

>>> greet("Pythonista")
Hello, Pythonista!


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

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

Расширенные операторы присваивания в Python

Python поддерживает так называемые расширенные присваивания. Расширенное присваивание объединяет оператор присваивания с другим оператором, чтобы сделать инструкцию более краткой. Большинство математических и побитовых операторов Python имеют расширенный вариант присваивания, который выглядит примерно так:

variable $= expression


Обратите внимание, что $ не является допустимым оператором Python. В данном примере это заполнитель для универсального оператора. Этот оператор работает следующим образом:

  1. Вычислите expression, чтобы получить значение.
  2. Выполните операцию, определенную оператором, который имеет префикс =, используя предыдущее значение variable и возвращаемое значение expression в качестве операндов.
  3. Присвоить результирующее значение обратно variable.

На практике расширенное задание, подобное приведенному выше, эквивалентно следующему утверждению:

variable = variable $ expression


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

Например, предположим, что вам нужно определить переменную counter для подсчета некоторых данных в вашем коде. Вы можете использовать оператор += для увеличения counter на 1, используя следующий код:

>>> counter = 0
>>> counter += 1
>>> counter
1
>>> counter += 1
>>> counter
2


В этом примере оператор +=, известный как дополненное сложение, добавляет 1 к предыдущему значению в counter каждый раз, когда вы запускаете инструкцию counter += 1.

Важно отметить, что в отличие от обычных назначений, расширенные задания не создают новых переменных. Они позволяют вам только обновлять существующие переменные. Если вы используете расширенное присваивание с неопределенной переменной, то вы получаете NameError:

>>> x += 1
Traceback (most recent call last):
    ...
NameError: name 'x' is not defined


Python вычисляет правую часть инструкции, прежде чем присвоить результирующее значение обратно целевой переменной. В этом конкретном примере, когда Python пытается вычислить x + 1, он обнаруживает, что x не определен.

Отлично! Теперь вы знаете, что расширенное присваивание заключается в объединении оператора присваивания с другим оператором, например математическим или побитовым. Чтобы продолжить обсуждение, вы узнаете, какие математические операторы имеют расширенные варианты в Python.

Расширенные математические операторы присваивания

Уравнение типа x = x + b не имеет смысла в математике. Но в программировании утверждение типа x = x + b абсолютно корректно и может быть чрезвычайно полезным. Он добавляет b к x и переназначает результат обратно на x.

Как вы уже узнали, в Python есть оператор для сокращения x = x + b. Да, оператор += позволяет вместо этого написать x += b. Python также предлагает расширенные операторы присваивания для большинства математических операций. Вот краткое описание:

Operator Description Example Equivalent
+= Adds the right operand to the left operand and stores the result in the left operand x += y x = x + y
-= Subtracts the right operand from the left operand and stores the result in the left operand x -= y x = x - y
*= Multiplies the right operand with the left operand and stores the result in the left operand x *= y x = x * y
/= Divides the left operand by the right operand and stores the result in the left operand x /= y x = x / y
//= Performs floor division of the left operand by the right operand and stores the result in the left operand x //= y x = x // y
%= Finds the remainder of dividing the left operand by the right operand and stores the result in the left operand x %= y x = x % y
**= Raises the left operand to the power of the right operand and stores the result in the left operand x **= y x = x ** y

В столбце Пример приведены общие примеры использования операторов в реальном коде. Обратите внимание, что для корректной работы операторов необходимо предварительно определить x. С другой стороны, y может быть либо конкретным значением, либо выражением, возвращающим значение.

Примечание: Оператор умножения матрицы (@) пока не поддерживает расширенные назначения.

Рассмотрим следующий пример умножения матриц с использованием массивов NumPy:

>>> import numpy as np

>>> x = np.ones(3)
>>> x
array([1., 1., 1.])

>>> m = np.eye(3)
>>> m
array([[1., 0., 0.],
       [0., 1., 0.],
       [0., 0., 1.]])

>>> x @ m
array([1., 1., 1.])

>>> x @= m
Traceback (most recent call last):
    ...
TypeError: In-place matrix multiplication is not (yet) supported.
    Use 'a = a @ b' instead of 'a @= b'.


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

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

>>> def sum_all(numbers):
...    total = 0
...    for number in numbers:
...        total += number  # Augmented addition
...    return total
...

>>> sum_all([1, 2, 3, 4])
10


В этой функции вы сначала инициализируете total значением 0. На каждой итерации цикл добавляет новое число к total, используя расширенный оператор сложения (+=). Когда цикл завершается, total содержит сумму всех входных чисел. Переменные типа total известны как накопители. Оператор += обычно используется для обновления накопителей.

Примечание: Вычисление суммы ряда числовых значений является обычной операцией в программировании. Python предоставляет встроенную функцию sum() для этого конкретного вычисления.

Еще один интересный пример использования расширенного задания - это когда вам нужно реализовать обратный отсчет while цикл для обратного выполнения итерации. В этом случае вы можете использовать оператор -=:

>>> def custom_reversed(sequence):
...     index = len(sequence) - 1
...     while index >= 0:
...         yield sequence[index]
...         index -= 1
...

>>> list(custom_reversed("12345"))
['5', '4', '3', '2', '1']


В этом примере custom_reversed() является функцией генератора, поскольку она использует yield. Вызов функции создает итератор, который возвращает элементы из входных данных в обратном порядке. Чтобы уменьшить управляющую переменную index, вы используете расширенный оператор вычитания, который вычитает 1 из переменной на каждой итерации.

Примечание: Аналогично суммированию значений в iterable, изменение значения iterable на обратное также является распространенным требованием. Python предоставляет встроенную функцию reversed() для этого конкретного вычисления, так что вам не нужно реализовывать свою собственную. Приведенный выше пример предназначен только для того, чтобы показать оператор -= в действии.

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

>>> word = "mississippi"
>>> counter = {}

>>> for letter in word:
...     if letter not in counter:
...         counter[letter] = 0
...     counter[letter] += 1
...

>>> counter
{'m': 1, 'i': 4, 's': 4, 'p': 2}


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

Счетчики настолько распространены в программировании, что Python предоставляет инструмент, специально разработанный для облегчения задачи подсчета. Ознакомьтесь с Счетчик Python: способ подсчета объектов на языке Python для получения полного руководства по использованию этого инструмента.

Расширенные задания для объединения и повторения

Расширенные операторы присваивания += и *= также работают с последовательностями, такими как списки, кортежи и строки. Оператор += выполняет расширенных объединений, в то время как оператор *= выполняет расширенных повторений.

Эти операторы по-разному работают с изменяемыми и неизменяемыми типами данных:

Operator Description Example
+= Runs an augmented concatenation operation on the target sequence. Mutable sequences are updated in place. If the sequence is immutable, then a new sequence is created and assigned back to the target name. seq_1 += seq_2
*= Adds seq to itself n times. Mutable sequences are updated in place. If the sequence is immutable, then a new sequence is created and assigned back to the target name. seq *= n

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

Рассмотрим следующие примеры и обратим внимание на результат вызова функции id():

>>> # Mutable target
>>> list_1 = [1, 2, 3]
>>> id(list_1)
4323479104

>>> list_2 = [4, 5]
>>> list_2
[4, 5]

>>> list_1 += list_2  # Concatenation...
>>> list_1
[1, 2, 3, 4, 5]
>>> id(list_1)
4323479104

>>> # Immutable target
>>> tuple_1 = (1, 2, 3)
>>> id(tuple_1)
4387336896

>>> tuple_2 = (4, 5)
>>> tuple_2
(4, 5)

>>> tuple_1 += tuple_2  # Concatenation...
>>> tuple_1
(1, 2, 3, 4, 5)
>>> id(tuple_1)
4387485104


Изменяемые последовательности, такие как списки, поддерживают оператор расширенного присваивания += с помощью метода .__iadd__(), который выполняет добавление на месте. Этот метод изменяет базовый список, добавляя новые значения в его конец.

Примечание: Если левый операнд является изменяемым, то x += y может не полностью соответствовать x = x + y. Например, если вы сделаете list_1 = list_1 + list_2 вместо list_1 += list_2, как указано выше, вы создадите новый список, а не измените существующий. Это может быть важно, если другие переменные ссылаются на тот же список.

Неизменяемые последовательности, такие как кортежи и строки, не предоставляют метода .__iadd__(). Таким образом, расширенные объединения возвращаются к методу .__add__(), который не изменяет существующую последовательность, а возвращает новую последовательность.

Существует еще одно различие между изменяемыми и неизменяемыми последовательностями, когда вы используете их в расширенной конкатенации. Рассмотрим следующие примеры:

>>> a_list = [1, 2, 3]
>>> a_list += (4, 5)
>>> a_list
[1, 2, 3, 4, 5]

>>> a_list += "67"
>>> a_list
[1, 2, 3, 4, 5, '6', '7']

>>> a_tuple = (1, 2, 3)
>>> a_tuple += [4, 5]
Traceback (most recent call last):
    ...
TypeError: can only concatenate tuple (not "list") to tuple

>>> a_string = "123"
>>> a_string += ("4", "5")
Traceback (most recent call last):
    ...
TypeError: can only concatenate str (not "tuple") to str


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

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

>>> # Mutable target
>>> a_list = [1, 2, 3]
>>> id(a_list)
4395563840

>>> n = 2
>>> a_list *= n  # Repetition...
>>> a_list
[1, 2, 3, 1, 2, 3]
>>> id(a_list)
4395563840

>>> a_list[0] is a_list[3]
True

>>> # Immutable target
>>> a_tuple = (1, 2, 3)
>>> id(a_tuple)
4393522944

>>> n = 2
>>> a_tuple *= n  # Repetition...
>>> a_tuple
(1, 2, 3, 1, 2, 3)
>>> id(a_tuple)
4394199328

>>> a_tuple[0] is a_tuple[3]
True


Когда оператор *= работает с изменяемой последовательностью, он возвращается к методу .__imul__(), который выполняет операцию на месте, изменяя базовую последовательность . Напротив, если *= работает с неизменяемой последовательностью, то вызывается .__mul__(), возвращающий новую последовательность того же типа.

Примечание: Значения n, меньшие, чем 0, обрабатываются как 0, что возвращает пустую последовательность того же значения. введите данные в качестве целевой последовательности в левой части *= операнда.

Обратите внимание, что a_list[0] is a_list[3] возвращает True. Это связано с тем, что оператор *= не создает копию повторяющихся данных. Он только отображает данные. Такое поведение может быть источником проблем при использовании оператора с изменяемыми значениями.

Например, предположим, что вы хотите создать список списков для представления матрицы, и вам нужно инициализировать список с помощью n пустых списков, как в следующем коде:

>>> n = 3
>>> matrix = [[]]
>>> matrix *= n
>>> matrix
[[], [], []]


В этом примере вы используете оператор *= для заполнения matrix тремя пустыми списками. Теперь посмотрите, что происходит, когда вы пытаетесь заполнить первый подсписок в matrix:

>>> matrix[0].append(1)
>>> matrix[0].append(2)
>>> matrix[0].append(3)
>>> matrix
[[1, 2, 3], [1, 2, 3], [1, 2, 3]]


Добавленные значения отображаются в трех подсписках. Это происходит потому, что оператор *= не создает копии данных, которые вы хотите повторить. Он отображает только данные. Следовательно, каждый подсписок в matrix указывает на один и тот же объект и адрес памяти.

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

>>> n = 3
>>> matrix = [[] for _ in range(n)]
>>> matrix
[[], [], []]

>>> matrix[0].append(1)
>>> matrix[0].append(2)
>>> matrix[0].append(3)
>>> matrix
[[1, 2, 3], [], []]


На этот раз, когда вы заполняете первый подсписок из matrix, ваши изменения не распространяются на другие подсписки. Это происходит потому, что все подсписки представляют собой разные объекты, которые находятся в разных адресах памяти.

Расширенные операторы побитового присваивания

Побитовые операторы также имеют свои расширенные версии. Логика, лежащая в их основе, аналогична логике математических операторов. В следующей таблице приведены расширенные побитовые операторы, предоставляемые Python:

Operator Operation Example Equivalent
&= Augmented bitwise AND (conjunction) x &= y x = x & y
|= Augmented bitwise OR (disjunction) x |= y x = x | y
^= Augmented bitwise XOR (exclusive disjunction) x ^= y x = x ^ y
>>= Augmented bitwise right shift x >>= y x = x >> y
<<= Augmented bitwise left shift x <<= y x = x << y

Расширенные операторы побитового присваивания выполняют требуемую операцию, принимая текущее значение левого операнда в качестве отправной точки для вычисления. Рассмотрим следующий пример, в котором используются операторы & и &=:

>>> number_1 = 123
>>> bin(number_1)
'0b1111011'

>>> number_2 = 456
>>> bin(number_2)
'0b111001000'

>>> # Bitwise AND
>>> #     0b1111011 (int 123)
>>> # & 0b111001000 (int 456)
>>> # -------------
>>> #     0b1001000 (int 72)

>>> number_1 & number_2
72
>>> bin(number_1 & number_2)
'0b1001000'

>>> number_1 &= number_2
>>> number_1
72
>>> bin(number_1)
'0b1001000'


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

Например, предположим, что вы внедряете систему разрешений в стиле Unix, позволяющую вашим пользователям получать доступ к данному ресурсу. В этом случае вы можете использовать символы "r" для чтения, "w" для записи и "x" для разрешений на выполнение, соответственно. Однако использование битовых разрешений может повысить эффективность использования памяти:

>>> r = 0b100
>>> w = 0b010
>>> x = 0b001

>>> # Assign permissions to users with |
>>> admin = r | w | x
>>> bin(admin)
'0b111'

>>> # Assign permissions to users with |=
>>> user = r
>>> user |= w
>>> bin(user)
'0b110'

>>> # Check permission with & and bool()
>>> bool(r & user)
True
>>> bool(x & user)
False


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

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

Другие варианты назначения

Итак, вы узнали, что операторы присваивания в Python и оператор присваивания присутствуют во многих различных сценариях и вариантах использования. Эти варианты использования включают создание и инициализацию переменных, параллельные назначения, итеративную распаковку, расширенные назначения и многое другое.

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

Вкратце, вы узнаете о:

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

Аннотированные операторы присваивания

PEP 526 ввел специальный синтаксис для аннотаций переменных еще в Python 3.6. Синтаксис состоит из имени переменной, за которым следует двоеточие (:), и типа переменной:

>>> counter: int
>>> name: str
>>> fruits: list[str]


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

>>> counter += 1
Traceback (most recent call last):
    ...
NameError: name 'counter' is not defined


Если вы попытаетесь использовать одну из ранее объявленных переменных в расширенном присваивании, то получите NameError, поскольку синтаксис аннотации не определяет переменную. Чтобы на самом деле определить это, вам нужно использовать присваивание.

Хорошей новостью является то, что вы можете использовать синтаксис аннотации переменной в операторе присваивания с помощью оператора =:

>>> counter: int = 0
>>> counter += 1
>>> counter += 1
>>> counter
2


Первый оператор в этом примере - это то, что вы можете назвать аннотированным оператором присваивания в Python. Вы можете спросить себя, зачем вам использовать аннотации типов в этом типе задания, если все могут видеть, что counter содержит целое число. Ты прав. В этом примере тип переменной однозначен.

Однако представьте, что произошло бы, если бы вы нашли инициализацию переменной, подобную следующей:

>>> class User:
...     # Class implementation...
...     pass
...

>>> users = []


Каким будет тип данных каждого пользователя в users? Если инициализация users далека от определения класса User, то нет быстрого способа ответить на этот вопрос. Чтобы прояснить эту двусмысленность, вы можете предоставить соответствующую подсказку по типу для users:

>>> class User:
...     # Class implementation...
...     pass
...

>>> users: list[User] = []


Теперь вы четко указываете, что users будет содержать список User экземпляров. Использование подсказок о типах в операторах присваивания, которые инициализируют переменные в пустые типы данных коллекции, такие как списки, кортежи или словари, позволяет вам предоставить больше информации о том, как работает ваш код. Это сделает ваш код более понятным и менее подверженным ошибкам.

Выражения присваивания с помощью оператора Walrus

До этого момента вы знали, что обычные инструкции присваивания с оператором = не имеют возвращаемого значения. Они просто создают или обновляют переменные. Следовательно, вы не можете использовать обычное присваивание для присвоения значения переменной в контексте выражения.

Python 3.8 изменил это, введя новый тип оператора присваивания в PEP 572. Это новое выражение известно как выражение присваивания или именованное выражение.

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

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

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

(variable := expression)


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

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

Примечание: Выражения присваивания с оператором walrus имеют несколько практических вариантов использования. У них также есть несколько ограничений. Например, они недопустимы в определенных контекстах, таких как lambda функции, параллельные назначения и расширенные назначения.

Чтобы более подробно ознакомиться с этим особым типом присваивания, ознакомьтесь с Оператором Walrus: выражениями присваивания в Python..

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

>>> def mean(sample):
...     n = len(sample)
...     if n == 0:
...         raise ValueError("input data required")
...     return sum(sample) / n
...

>>> mean([1, 2, 3, 4, 5])
3.0


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

Вы можете избежать этого дополнительного шага, объединив его с первым использованием целевого значения len(sample), используя выражение присваивания, подобное следующему:

>>> def mean(sample):
...     if (n := len(sample)) == 0:
...         raise ValueError("input data required")
...     return sum(sample) / n
...

>>> mean([1, 2, 3, 4, 5])
3.0


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

Поскольку выражение присваивания в любом случае возвращает размер выборки, условие может проверить, равен ли этот размер 0 или нет, а затем предпринять определенные действия в зависимости от результата этой проверки. Оператор return вычисляет среднее значение выборки и отправляет результат обратно вызывающей функции.

Назначение управляемых атрибутов

Python предоставляет несколько инструментов, которые позволяют вам точно настроить операции, стоящие за назначением атрибутов. Атрибуты, которые выполняют неявные операции с назначениями, обычно называются управляемыми атрибутами.

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

Чтобы понять, что означает тонкая настройка операции, стоящей за присваиванием, предположим, что вам нужен Point класс, который допускает только числовые значения для своих координат, x и y. Чтобы написать этот класс, вы должны настроить механизм проверки, чтобы отклонять нечисловые значения. Вы можете использовать свойства, чтобы добавить функциональность проверки поверх x и y.

Вот как вы можете написать свой класс:

# point.py

class Point:

    @property
    def x(self):
        return self._x

    @x.setter
    def x(self, value):
        try:
            self._x = float(value)
        except ValueError:
            raise ValueError('"x" must be a number') from None

    @property
    def y(self):
        return self._y

    @y.setter
    def y(self, value):
        try:
            self._y = float(value)
        except ValueError:
            raise ValueError('"y" must be a number') from None


В Point используются свойства для координат .x и .y. У каждого свойства есть получатель и метод установки. Метод получения возвращает соответствующий атрибут. Метод setter выполняет проверку введенных данных с использованием блока try ... except и встроенной функции float(). Затем метод присваивает результат фактическому атрибуту.

Вот как работает ваш класс на практике:

>>> from point import Point

>>> point_1 = Point()
>>> point_1.x = 1
>>> point_1.y = 2
>>> point_1.x, point_1.y
1.0 2.0

>>> point_2 = Point()
>>> point_2.x = "one"
Traceback (most recent call last):
    ...
ValueError: "x" must be a number


Когда вы используете атрибут, основанный на свойстве, в качестве левого операнда в операторе присваивания, Python автоматически вызывает метод setter для этого свойства, выполняя с его помощью любые вычисления.

Поскольку и .x, и .y являются свойствами, проверка ввода выполняется всякий раз, когда вы присваиваете значение любому из атрибутов. В первом примере входные значения являются допустимыми числами, и проверка проходит успешно. В последнем примере "one" не является допустимым числовым значением, поэтому проверка завершается неудачей.

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

Дескриптор - это класс, реализующий протокол дескриптора, который состоит из четырех специальных методов:

  1. .__get__() запускается при обращении к атрибуту, представленному дескриптором.
  2. .__set__() выполняется при использовании атрибута в инструкции присваивания.
  3. .__delete__() выполняется при использовании атрибута в инструкции del.
  4. .__set_name__() задает имя атрибута, создавая атрибут, зависящий от имени.

Вот как может выглядеть ваш код, если вы используете дескриптор для представления координат вашего класса Point:

# point.py

class Coordinate:
    def __set_name__(self, owner, name):
        self._name = name

    def __get__(self, instance, owner):
        return instance.__dict__[self._name]

    def __set__(self, instance, value):
        try:
            instance.__dict__[self._name] = float(value)
        except ValueError:
            raise ValueError(f'"{self._name}" must be a number') from None

class Point:
    x = Coordinate()
    y = Coordinate()


Вы удалили повторяющийся код, определив Coordinate в качестве дескриптора, который управляет проверкой ввода в одном месте. Продолжайте и запустите следующий код, чтобы опробовать новую реализацию Point:

>>> from point import Point

>>> point_1 = Point()
>>> point_1.x = 1
>>> point_1.y = 2
>>> point_1.x, point_1.y
1.0 2.0

>>> point_2 = Point()
>>> point_2.x = "one"
Traceback (most recent call last):
    ...
ValueError: "x" must be a number


Отлично! Класс работает так, как ожидалось. Благодаря дескриптору Coordinate теперь у вас есть более сжатая и неповторяющаяся версия исходного кода.

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

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

>>> from collections import UserDict

>>> class LowerCasedDict(UserDict):
...     def __setitem__(self, key, value):
...         key = key.lower()
...         super().__setitem__(key, value)
...

>>> numbers = LowerCasedDict()
>>> numbers["ONE"] = 1
>>> numbers["Two"] = 2
>>> numbers["Three"] = 3

>>> numbers
{'one': 1, 'two': 2, 'three': 3}


В этом примере вы создаете класс, подобный словарю, путем создания подкласса UserDict из collections. Ваш класс реализует метод .__setitem__(), который принимает key и value в качестве аргументов. Метод использует str.lower() для преобразования key в строчные буквы перед сохранением в базовом словаре.

Python неявно вызывает .__setitem__() каждый раз, когда вы используете ключ в качестве левого операнда в операторе присваивания. Такое поведение позволяет вам настроить процесс присвоения ключей в вашем пользовательском словаре.

Неявные присваивания в Python

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

Всякий раз, когда вы выполняете действие из следующего списка, Python выполняет для вас неявное назначение:

  • Определите или вызовите функцию
  • Определите или создайте экземпляр класса
  • Используйте текущий экземпляр , self
  • Импорт модулей и объектов
  • Используйте декоратор
  • Используйте управляющую переменную в цикле for или для понимания
  • Используйте определитель as в операторах with, imports и try ... except blocks
  • Получить доступ к специальной переменной _ в интерактивном сеансе

За кулисами Python выполняет задание в каждой из вышеперечисленных ситуаций. В следующих подразделах вы познакомитесь со всеми этими ситуациями.

Определить или вызвать функцию

Когда вы определяете функцию, ключевое слово def неявно присваивает объекту функции имя вашей функции. Вот пример:

>>> def greet(name):
...    print(id(name))
...    print(f"Hello, {name}!")
...

>>> greet
<function greet at 0x105e9bb00>

>>> id(greet)
4394171136

>>> fellow = "Pythonista"
>>> greet(fellow)
4381781552
Hello, Pythonista!

>>> id(fellow)
4381781552


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

Если вы вызываете свою функцию greet() с fellow в качестве аргумента, то Python неявно присваивает значение входного аргумента параметру name в определении функции. Параметр будет содержать ссылку на входные аргументы.

Работа с Классами

Когда вы определяете класс с помощью ключевого слова class, вы присваиваете конкретное имя объекту класса . Позже вы сможете использовать это имя для создания экземпляров этого класса. Рассмотрим следующий пример:

>>> class User:
...     def __init__(self, name, job):
...         self.name = name
...         self.job = job
...

>>> User
<class '__main__.User'>
>>> id(User)
5035278528

>>> john = User("John Doe", "Python Dev")
>>> john.name
'John Doe'
>>> john.job
'Python Dev'


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

Другим примером неявных назначений является текущий экземпляр класса, который в Python называется self по соглашению. Это имя неявно содержит ссылку на текущий объект всякий раз, когда вы создаете экземпляр класса. Благодаря этому неявному присваиванию вы можете получить доступ к .name и .job из класса, не получая NameError в своем коде.

Импорт модулей и объектов

Инструкции Import - это еще один вариант неявных назначений в Python. С помощью инструкции import вы присваиваете имя объекту модуля, классу, функции или любому другому импортируемому объекту. Затем это имя создается в вашем текущем пространстве имен, чтобы вы могли получить к нему доступ позже в своем коде:

>>> dir()
['__builtins__', '__doc__', ..., '__spec__', 'help']

>>> import sys
>>> dir()
['__builtins__', '__doc__', ..., '__spec__', 'help', 'sys']

>>> sys
<module 'sys' (built-in)>


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

Используйте декоратор

Вы также выполняете неявное присваивание, когда используете декоратор в своем коде. Синтаксис декоратора - это просто ярлык для формального присваивания, подобного следующему:

func = decorator(func)


Здесь вы вызываете decorator(), используя объект функции в качестве аргумента. Этот вызов обычно добавляет функциональность поверх существующей функции func() и возвращает объект функции, которому затем присваивается имя func.

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

@decorator
def func():
    # Implementation here...
    pass


Несмотря на то, что этот новый код внешне сильно отличается от приведенного выше задания, в нем неявно выполняются те же действия.

Доступ к управляющей переменной осуществляется в цикле for или в режиме понимания

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

>>> for control_variable in range(5):
...     print(f"{control_variable=} {id(control_variable)=}")
...
control_variable=0 id(control_variable)=4376944840
control_variable=1 id(control_variable)=4376944872
control_variable=2 id(control_variable)=4376944904
control_variable=3 id(control_variable)=4376944936
control_variable=4 id(control_variable)=4376944968


Адрес памяти control_variable изменяется на каждой итерации цикла. Это происходит потому, что Python внутренне присваивает новое значение из iterable цикла управляющей переменной цикла в каждом цикле.

То же самое происходит в понятиях:

>>> [
...     f"{control_variable=} {id(control_variable)=}"
...     for control_variable in range(5)
... ]
[
    'control_variable=0 id(control_variable)=4376944840',
    'control_variable=1 id(control_variable)=4376944872',
    'control_variable=2 id(control_variable)=4376944904',
    'control_variable=3 id(control_variable)=4376944936',
    'control_variable=4 id(control_variable)=4376944968'
]


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

Используйте ключевое слово as

Ключевое слово as в with утверждения, except предложений и import утверждений - это еще одно пример неявного присваивания в Python. На этот раз назначение не является полностью неявным, поскольку ключевое слово as предоставляет явный способ определения целевой переменной.

В инструкции with целевая переменная, следующая за ключевым словом as, будет содержать ссылку на контекстный менеджер, с которым вы работаете. В качестве примера предположим, что у вас есть hello.txt файл со следующим содержимым:

Hello, Pythonista!
Welcome to Real Python!


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

В приведенном ниже примере вы выполняете это. Вы также добавляете несколько вызовов в print(), которые отображают информацию о целевой переменной, определенной ключевым словом as:

>>> with open("hello.txt", mode="r") as hello:
...     print(f"File object: {hello}")
...     print(f"Memory address: {id(hello)}")
...     print("File content:")
...     for line in hello:
...        print("> ", line)
...
File object: <_io.TextIOWrapper name='hello.txt' mode='r' encoding='UTF-8'>
Memory address: 4372378896
File content:
>  Hello, Pythonista!
>  Welcome to Real Python!


Этот оператор with использует функцию open() для открытия hello.txt. Функция open() является контекстным менеджером, который возвращает объект текстового файла, представленный экземпляром io.TextIOWrapper.

Поскольку вы определили целевую переменную hello с ключевым словом as, теперь эта переменная содержит ссылку на сам файловый объект. Вы подтверждаете это, выводя объект и его адрес в памяти. Наконец, цикл for выполняет итерацию по строкам и выводит это содержимое на экран.

Когда дело доходит до использования ключевого слова as в контексте предложения except, целевая переменная будет содержать объект exception, если возникнет какое-либо исключение:

>>> try:
...     5 / 0
... except ZeroDivisionError as error:
...     print(f"Exception: {error}")
...     print(f"Memory address: {id(error)}")
...
Exception: division by zero
Memory address: 4382188704

>>> error
Traceback (most recent call last):
    ...
NameError: name 'error' is not defined


В этом примере выполняется разделение, при котором возникает ZeroDivisionError. Ключевое слово as присваивает возникшему исключению значение error. Обратите внимание, что при печати объекта exception вы получаете только сообщение, потому что исключения имеют пользовательский метод .__str__(), который поддерживает это поведение.

Есть еще одна деталь, о которой следует помнить при использовании спецификатора as в блоке try ... except, подобном приведенному в примере выше. Как только вы покидаете блок except, целевая переменная выходит за пределы области , и вы больше не можете ее использовать.

Наконец, операторы import в Python также поддерживают ключевое слово as. В этом контексте вы можете использовать as для импорта объектов с другим именем:

>>> import numpy as np
>>> import pandas as pd

>>> dir()
['__builtins__', '__doc__', ..., 'help', 'np', 'pd']


В этих примерах вы используете ключевое слово as для импорта пакета numpy с именем np и pandas с именем pd. Если вы вызовете dir(), то поймете, что np и pd теперь находятся в вашем пространстве имен. Однако имена numpy и pandas таковыми не являются.

Использование ключевого слова as в вашем импорте удобно, когда вы хотите использовать более короткие имена для своих объектов или когда вам нужно использовать разные объекты, которые изначально имели одинаковое имя в вашем коде. Это также полезно, если вы хотите сделать импортированные имена непубличными, используя начальное подчеркивание, как в import sys as _sys.

Доступ к специальной переменной _ в интерактивном сеансе

Окончательное неявное назначение, о котором вы узнаете в этом руководстве, выполняется только при использовании Python в интерактивном сеансе. Каждый раз, когда вы запускаете инструкцию, возвращающую значение, интерпретатор сохраняет результат в специальной переменной, обозначаемой одним символом подчеркивания (_).

Вы можете получить доступ к этой специальной переменной так же, как к любой другой переменной:

>>> # Expressions
>>> 5 < 7
True
>>> _
True
>>> 12 + 30
42
>>> _
42

>>> # Function calls
>>> sum([1, 2, 3, 4])
10
>>> _
10
>>> print("Hello, Pythonista!")
Hello, Pythonista!
>>> _
10

>>> # Assignments
>>> counter = 0
>>> _
10

>>> # Variable accesses
>>> counter
0
>>> _
0


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

Когда дело доходит до вызовов функций, обратите внимание, что если ваша функция возвращает результативное значение, то оно будет сохранено в _. Напротив, если ваша функция возвращает значение None,, то переменная _ останется нетронутой.

Следующий пример состоит из обычной инструкции присваивания. Как вы уже знаете, обычные присваивания не возвращают никакого значения, поэтому переменная _ не обновляется после выполнения этих инструкций. Наконец, обратите внимание, что при обращении к переменной в интерактивном сеансе возвращается значение, сохраненное в целевой переменной. Затем это значение присваивается переменной _.

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

>>> numbers = [1, 2, 3, 4]

>>> len(numbers)
4

>>> sum(numbers) / _
2.5


В этом примере сначала создается список значений. Затем вызывается len(), чтобы получить количество значений в списке. Python автоматически сохраняет это значение в переменной _. Наконец, вы используете _ для вычисления среднего значения из вашего списка значений.

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

Незаконные и опасные задания в Python

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

В следующих разделах вы узнаете, когда использование инструкций присваивания запрещено в Python. Вы также узнаете о некоторых ситуациях, в которых следует избегать использования присваиваний, если вы хотите сохранить согласованность и надежность своего кода.

Ключевые слова

Вы не можете использовать ключевые слова Python в качестве имен переменных в операторах присваивания. Этот вид присваивания явно запрещен. Если вы попытаетесь использовать ключевое слово в качестве имени переменной в задании, то получите SyntaxError:

>>> class = "Economy"
  File "<stdin>", line 1
    class = "Economy"
          ^
SyntaxError: invalid syntax

>>> global = 42
  File "<input>", line 1
    global = 42
           ^
SyntaxError: invalid syntax


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

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

>>> class_ = "Economy"
>>> class_
'Economy'

>>> global_ = 42
>>> global_
42


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

Примечание: Несмотря на то, что добавление символа подчеркивания в конце имени является официально рекомендуемой практикой , иногда это может привести к путанице. Поэтому старайтесь найти альтернативное название или использовать синоним всякий раз, когда вы сталкиваетесь с использованием этого соглашения.

Например, вы можете написать что-то вроде этого:

>>> booking_class = "Economy"
>>> booking_class
'Economy'


В этом примере использование имени booking_class для вашей переменной намного понятнее и нагляднее, чем использование class_.

Вы также обнаружите, что в операторе присваивания можно использовать только несколько ключевых слов как часть правильного операнда. Эти ключевые слова обычно определяют простые операторы, которые возвращают значение или объект. К ним относятся lambda, and, or, not, True, False, None, in, и is. Вы также можете использовать ключевое слово for, когда оно является частью понимания, и ключевое слово if, когда оно используется как часть троичного оператора .

В присваивании вы никогда не сможете использовать составной оператор в качестве правильного операнда. Составные инструкции - это те, для которых требуется блок с отступом, такие как for и while циклы, условные выражения, with инструкции, try ... except блоки и определения классов или функций.

Встроенные объекты

Иногда вам нужно присвоить переменным имена, но желаемое или идеальное имя уже занято и используется как встроенное. Если это ваш случай, подумайте хорошенько и найдите другое имя. Не затеняйте встроенное устройство.

Скрытие встроенных имен может привести к трудноопределимым проблемам в вашем коде. Распространенным примером этой проблемы является использование list или dict для присвоения имен пользовательским переменным. В этом случае вы переопределяете соответствующие встроенные имена, которые не будут работать должным образом, если вы будете использовать их позже в своем коде.

Рассмотрим следующий пример:

>>> list = [1, 2, 3, 4]

>>> squares = list(map(lambda x: x ** 2, [1, 2, 3, 4]))
Traceback (most recent call last):
    ...
TypeError: 'list' object is not callable


Исключение в этом примере может показаться неожиданным. Почему вы не можете использовать list() для создания списка из вызова map(), который возвращает генератор квадратных чисел?

Используя имя list для идентификации вашего списка чисел, вы скрыли встроенное имя list. Теперь это имя указывает на объект списка, а не на встроенный класс. Объекты списка недоступны для вызова, поэтому ваш код больше не работает.

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

Именованные константы

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

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

Чтобы сообщить другим программистам на Python, что данная переменная должна рассматриваться как константа, вы должны написать название своей переменной заглавными буквами с подчеркиванием, разделяющим слова. Это соглашение об именовании было принято сообществом Python и является рекомендацией, которую вы найдете в разделе Константы в PEP 8.

В следующих примерах вы определяете некоторые константы в Python:

>>> PI = 3.14
>>> MAX_SPEED = 300
>>> WIDTH = 20
>>> BASE_URL = "https://api.example.com"


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

>>> PI = 3.141592653589793
>>> MAX_SPEED = 1_000


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

Единственный способ сделать это - никогда не использовать именованные константы в операторе присваивания, кроме определения константы.

Заключение

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

В этом руководстве вы узнали, как:

  • Напишите инструкции присваивания, используя операторы присваивания в Python
  • Работа с дополненными заданиями в Python
  • Изучите варианты присвоения, такие как выражение присвоения и управляемые атрибуты
  • Выявлять незаконные и опасные назначения в Python

Изучение оператора присваивания Python и того, как его использовать в операторах присваивания, является фундаментальным навыком в Python. Это позволит вам писать надежный и эффективный код на Python.

Бесплатный исходный код: Нажмите здесь, чтобы скачать бесплатный исходный код оператора присваивания, который вы будете использовать для написания инструкций присваивания, позволяющих вам создавать, инициализировать и обновлять переменные в вашем коде.

<статус завершения article-slug="python-assignment-operator" class="btn-group mb-0" data-api-article-bookmark-url="/api/v1/articles/python-assignment-operator/bookmark/" данные-api-article-завершение-status-url="/api/v1/articles/python-оператор присваивания/завершение_статуса/"> <кнопка поделиться bluesky-text="Интересная статья на #Python от @realpython.com :" email-body="Ознакомьтесь с этой статьей о Python:%0A%0apython's Assignment Operator: Пишите надежные задания" email-subject="Статья о Python для вас" twitter-text="Интересная статья о #Python от @realpython:" url="https://realpython.com/python-assignment-operator /" url-title="Оператор присваивания в Python: запись надежных присваиваний"> Back to Top