Оператор Walrus: выражения присваивания в Python
Оглавление
- Основы работы с моржами
- Примеры использования оператора Walrus
- Синтаксис оператора Walrus
- Подводные камни оператора "Морж"
- Заключение
- Часто задаваемые вопросы
Смотрите сейчас, к этому уроку прилагается соответствующий видеокурс, созданный командой Real Python. Посмотрите его вместе с письменным руководством, чтобы углубить свое понимание: Выражения присваивания в Python и использование оператора Walrus
Оператор walrus в Python (:=) позволяет присваивать значения переменным как части выражения. Он может упростить ваш код, объединив присваивание и вычисление в одном операторе. Вы используете его для упрощения таких конструкций, как списки, циклы и условные выражения, что делает ваш код более кратким и удобочитаемым. Оператор walrus особенно полезен, когда вы хотите избежать повторяющихся вызовов функций или вычислений.
К концу этого урока вы поймете, что:
- Оператор walrus (
:=) - это синтаксис для назначения переменных в выражениях в Python. - Выражения присваивания используют оператор walrus в Python.
- Оператор walrus можно использовать для упрощения кода, например, в циклах или условных выражениях.
- Выражения присваивания возвращают присвоенное значение, в отличие от обычных присваиваний.
- Обычные задания не возвращают значений, чтобы предотвратить непреднамеренное поведение и ошибки.
Понимание оператора walrus повысит вашу способность писать эффективный и лаконичный код на Python, особенно при работе со сложными условиями и циклами.
Получите свой код: Нажмите здесь, чтобы загрузить бесплатный пример кода, который показывает, как использовать оператор walrus в Python.
<отметить класс="marker-highlight"> Пройдите тест: отметить> Проверьте свои знания с помощью нашего интерактивного теста ”Оператор Walrus: выражения присваивания в Python". По завершении вы получите оценку, которая поможет вам отслеживать прогресс в обучении:
<время работы/>
Интерактивная викторина
Оператор Walrus: выражения присваивания в PythonВ этом тесте вы проверите свое понимание оператора walrus в Python. Этот оператор был представлен в Python 3.8, и его понимание может помочь вам написать более краткий и эффективный код.
Основы работы с моржами
Сначала рассмотрим несколько различных терминов, которые программисты используют для обозначения этого нового синтаксиса. Некоторые из них вы уже видели в этом руководстве.
Оператор := официально известен как оператор выражения присваивания. В ходе ранних обсуждений он был назван оператором моржа, потому что синтаксис := напоминает глаза и клыки моржа, лежащего на боку. Вы также можете увидеть оператор :=, называемый двоеточием и равный оператору. Еще одним термином, используемым для выражений присваивания, является именованных выражений.
Привет, Морж!
Чтобы получить первое представление о том, что такое выражения присваивания, запустите REPL и поиграйте со следующим кодом:
1>>> walrus = False 2>>> walrus 3False 4 5>>> (walrus := True) 6True 7>>> walrus 8Trueпредварительно> кодовый блок>В строке 1 показан традиционный оператор присваивания, в котором значение
Falseприсваиваетсяwalrus. Далее, в строке 5, вы используете выражение присваивания, чтобы присвоить значениеTruewalrus. После обеих строк 1 и 5 вы можете ссылаться на присвоенные значения, используя имя переменнойwalrus.Возможно, вам интересно, почему вы используете круглые скобки в строке 5, и вы узнаете, зачем они нужны позже в этом руководстве.
Примечание: Оператор в Python - это единица кода. Выражение - это специальный оператор, который может быть вычислен с некоторым значением.
Например,
1 + 2- это выражение, которое вычисляет значение3, в то время какnumber = 1 + 2- это оператор присваивания, который не вычисляет значение. Хотя выполнение инструкцииnumber = 1 + 2не приводит к вычислению значения3, она присваивает значению3значениеnumber.В Python вы часто видите простые операторы, такие как
returnоператоры иimportутверждений, а также составных утверждений, таких какifутверждения и определения функций. Все это утверждения, а не выражения.Существует небольшая, но важная разница между двумя типами присваиваний с помощью переменной
walrus. Выражение присваивания возвращает значение, в то время как традиционное присваивание этого не делает. Вы можете увидеть это в действии, когда REPL не выводит никакого значения послеwalrus = Falseв строке 1, но выводитTrueпосле выражения присваивания в строке 5.В этом примере вы можете увидеть еще один важный аспект, связанный с операторами walrus. Хотя это может показаться новым, оператор
:=выполняет , а не все, что без него невозможно. Это только делает некоторые конструкции более удобными и иногда может более четко передать цель вашего кода.Теперь у вас есть общее представление о том, что такое оператор
:=и что он может делать. Это оператор, используемый в выражениях присваивания, который может возвращать присваиваемое значение, в отличие от традиционных операторов присваивания. Чтобы углубиться и по-настоящему узнать о walrus operator, продолжайте читать, чтобы узнать, где вам следует, а где не следует его использовать.Реализация
Как и большинство новых функций в Python, выражения присваивания были введены в рамках Предложения по улучшению Python (PEP). PEP 572 описывает мотивацию для внедрения оператора walrus, детали синтаксиса и примеры, в которых оператор
:=может быть использован для улучшения вашего кода.Этот отрывок был первоначально написан Крисом Анджелико в феврале 2018 года. После бурного обсуждения ОПТОСОЗ 572 был принят Гвидо ван Россумом в июле 2018 года.
С тех пор Гвидо объявил, что он уходит со своего поста пожизненного доброжелательного диктатора (BDFL). С начала 2019 года язык Python управляется избранным руководящим советом вместо этого.
Оператор walrus был реализован Эмили Морхаус и стал доступен в первом альфа-релизе версии Python 3.8.
Мотивация
Во многих языках, включая C и его производные, операторы присваивания также являются выражениями. Это может быть как очень эффективным, так и источником сбивающих с толку ошибок. Например, следующий код является допустимым C, но выполняется не так, как предполагалось:
int x = 3, y = 8; if (x = y) { printf("x and y are equal (x = %d, y = %d)", x, y); }предварительно> кодовый блок>Здесь значение
if (x = y)будет равно true, и фрагмент кода выдаст значениеx and y are equal (x = 8, y = 8). Это тот результат, которого вы ожидали? Вы пытались сравнитьxиy. Как изменилось значениеxс3на8?Проблема в том, что вы используете оператор присваивания (
=) вместо оператора сравнения на равенство (==). В языке Сиx = y- это выражение, которое принимает значениеy. В этом примереx = yоценивается как8, что считается верным в контекстеifинструкции.Взгляните на соответствующий пример на Python. Этот код вызывает
SyntaxError:x, y = 3, 8 if x = y: print(f"x and y are equal ({x = }, {y = })")предварительно> кодовый блок>В отличие от примера на C, этот код на Python выдает явную ошибку вместо бага.
Различие между операторами присваивания и выражениями присваивания в Python полезно для того, чтобы избежать такого рода труднодоступных ошибок. PEP 572 утверждает, что Python лучше подходит для использования другого синтаксиса для операторов присваивания и выражений, чем для преобразования существующих операторов присваивания в выражения.
Один из принципов разработки, лежащих в основе оператора walrus, заключается в том, что не существует идентичных контекстов кода, в которых были бы допустимы как оператор присваивания, использующий оператор
=, так и выражение присваивания, использующее оператор:=. Например, вы не можете выполнить простое присваивание с помощью оператора walrus:>>> walrus := True File "<stdin>", line 1 walrus := True ^ SyntaxError: invalid syntaxпредварительно> кодовый блок>Во многих случаях вы можете добавить круглые скобки (
()) вокруг выражения присваивания, чтобы сделать его допустимым в Python:>>> (walrus := True) # Valid, but regular assignments are preferred Trueпредварительно> кодовый блок>Запись традиционного оператора присваивания с помощью
=в таких круглых скобках недопустима. Это поможет вам выявить возможные ошибки.Далее в этом руководстве вы узнаете больше о ситуациях, когда использование walrus operator запрещено, но сначала вы узнаете о ситуациях, когда вам может понадобиться его использовать.
Варианты использования оператора Walrus
В этом разделе вы увидите несколько примеров, в которых оператор walrus может упростить ваш код. Общая идея всех этих примеров заключается в том, что вы сможете избежать различного рода повторений:
- Повторяющиеся вызовы функций могут сделать ваш код медленнее, чем это необходимо.
- Повторяющиеся инструкции могут затруднить поддержку вашего кода.
- Повторяющиеся вызовы, приводящие к исчерпанию итераторов, могут сделать ваш код чрезмерно сложным.
Вы увидите, как оператор walrus может помочь в каждой из этих ситуаций.
Отладка
Возможно, один из лучших вариантов использования оператора walrus - это отладка сложных выражений. Допустим, вы хотите найти расстояние между двумя точками на поверхности земли. Один из способов сделать это - использовать формулу хаверсина:
![]()
θ представляет широту, а λ представляет долготу каждого местоположения. Чтобы продемонстрировать эту формулу, вы можете рассчитать расстояние между Осло (59.9° 10,8°северной широты) и Ванкувер (49.3° N 123,1°W) следующим образом:
>>> from math import asin, cos, radians, sin, sqrt >>> # Approximate radius of Earth in kilometers >>> rad = 6371 >>> # Locations of Oslo and Vancouver >>> ϕ1, λ1 = radians(59.9), radians(10.8) >>> ϕ2, λ2 = radians(49.3), radians(-123.1) >>> # Distance between Oslo and Vancouver >>> 2 * rad * asin( ... sqrt( ... sin((ϕ2 - ϕ1) / 2) ** 2 ... + cos(ϕ1) * cos(ϕ2) * sin((λ2 - λ1) / 2) ** 2 ... ) ... ) ... 7181.7841229421165предварительно> кодовый блок>Как вы можете видеть, расстояние от Осло до Ванкувера составляет чуть менее 7200 километров.
Примечание: Исходный код Python обычно пишется с использованием UTF-8 Unicode. Это позволяет вам использовать в вашем коде греческие буквы, такие как
ϕиλ, что может быть полезно при переводе математических формул. Википедия показывает несколько альтернативных вариантов использования Юникода в вашей системе.Хотя поддерживается UTF-8 (например, в строковых литералах), в именах переменных Python используется более ограниченный набор символов. Например, вы не можете использовать эмодзи при присвоении имен своим переменным. Это хорошее ограничение!
Теперь предположим, что вам нужно перепроверить вашу реализацию и вы хотите посмотреть, насколько общие условия влияют на конечный результат. Вы можете скопировать и вставить термин из вашего основного кода, чтобы оценить его отдельно. Однако вы также можете использовать оператор
:=, чтобы присвоить имя интересующему вас подвыражению:>>> 2 * rad * asin( ... sqrt( ... (ϕ_hav := sin((ϕ2 - ϕ1) / 2) ** 2) ... + cos(ϕ1) * cos(ϕ2) * sin((λ2 - λ1) / 2) ** 2 ... ) ... ) ... 7181.7841229421165 >>> ϕ_hav 0.008532325425222883предварительно> кодовый блок>Преимущество использования оператора walrus заключается в том, что вы вычисляете значение полного выражения и одновременно отслеживаете значение
ϕ_hav. Это позволяет вам убедиться, что вы не допустили никаких ошибок при отладке.Списки и словари
Списки - это мощные структуры данных в Python, которые часто представляют собой ряд связанных атрибутов. Аналогично, словари используются во всем Python и отлично подходят для структурирования информации.
Иногда при настройке этих структур данных вам приходится выполнять одну и ту же операцию несколько раз. В качестве первого примера, вычислите некоторые базовые описательные статистические данные из списка чисел и сохраните их в словаре:
>>> numbers = [2, 8, 0, 1, 1, 9, 7, 7] >>> description = { ... "length": len(numbers), ... "sum": sum(numbers), ... "mean": sum(numbers) / len(numbers), ... } >>> description {'length': 8, 'sum': 35, 'mean': 4.375}предварительно> кодовый блок>Обратите внимание, что как сумма, так и длина списка
numbersвычисляются дважды. В этом простом примере последствия не так уж плохи, но если бы список был больше или вычисления были более сложными, вам, возможно, потребовалось бы оптимизировать код. Чтобы сделать это, вы можете сначала удалить вызовы функций из определения словаря:>>> numbers = [2, 8, 0, 1, 1, 9, 7, 7] >>> num_length = len(numbers) >>> num_sum = sum(numbers) >>> description = { ... "length": num_length, ... "sum": num_sum, ... "mean": num_sum / num_length, ... } >>> description {'length': 8, 'sum': 35, 'mean': 4.375}предварительно> кодовый блок>Переменные
num_lengthиnum_sumиспользуются только для оптимизации вычислений внутри словаря. Используя оператор walrus, вы можете сделать эту роль более понятной:>>> numbers = [2, 8, 0, 1, 1, 9, 7, 7] >>> description = { ... "length": (num_length := len(numbers)), ... "sum": (num_sum := sum(numbers)), ... "mean": num_sum / num_length, ... } >>> description {'length': 8, 'sum': 35, 'mean': 4.375}предварительно> кодовый блок>Теперь вы определили
num_lengthиnum_sumвнутри определенияdescription. Это явный намек для любого, кто читает этот код, что эти переменные используются только для оптимизации вычислений и больше не используются в дальнейшем.Примечание: Область действия переменных
num_lengthиnum_sumодинакова в примере с оператором walrus и в примере без него. Это означает, что в обоих примерах переменные доступны после определенияdescription.Несмотря на то, что оба примера функционально очень похожи, преимущество использования выражений присваивания заключается в том, что оператор
:=передает назначение этих переменных в качестве одноразовой оптимизации.В следующем примере вы будете работать с простой реализацией утилиты
wcдля подсчета строк, слов и символов в текстовом файле:wc.pyкодовый блок>1import pathlib 2import sys 3 4for filename in sys.argv[1:]: 5 path = pathlib.Path(filename) 6 counts = ( 7 path.read_text().count("\n"), # Number of lines 8 len(path.read_text().split()), # Number of words 9 len(path.read_text()), # Number of characters 10 ) 11 print(*counts, path)Этот скрипт может считывать один или несколько текстовых файлов и сообщать, сколько строк, слов и символов содержит каждый из них. Вот краткое описание того, что происходит в коде:
- Строка 4 повторяет каждое имя файла, указанное пользователем. Список
sys.argvсодержит все аргументы, указанные в командной строке, начиная с названия вашего скрипта. Для получения дополнительной информации оsys.argvвы можете ознакомиться с Аргументами командной строки Python.- Строка 5 преобразует каждую строку имени файла в
pathlib.Pathобъект. Сохранение имени файла в объектеPathпозволяет вам удобно читать текстовый файл в следующих строках.- В строках с 6 по 10 создается набор значений, представляющих количество строк, слов и символов в одном текстовом файле.
- Строка 7 считывает текстовый файл и вычисляет количество строк путем подсчета новых строк.
- Строка 8 считывает текстовый файл и вычисляет количество слов путем разделения на пробелы.
- Строка 9 считывает текстовый файл и вычисляет количество символов, определяя длину строки.
- Строка 11 выводит все три параметра вместе с именем файла на консоль. Синтаксис
*countsраспаковывает кортежcounts. В этом случае операторprint()эквивалентенprint(counts[0], counts[1], counts[2], path).Чтобы увидеть
wc.pyв действии, вы можете использовать сам скрипт следующим образом:$ python wc.py wc.py 11 32 307 wc.pyпредварительно> кодовый блок>Другими словами, файл
wc.pyсостоит из 11 строк, 32 слов и 307 символов.Если вы внимательно посмотрите на эту реализацию, то заметите, что она далека от оптимальной. В частности, в ней трижды повторяется вызов
path.read_text(). Это означает, что программа трижды считывает каждый текстовый файл. Вы можете использовать оператор walrus, чтобы избежать повторения:wc.pyкодовый блок>import pathlib import sys for filename in sys.argv[1:]: path = pathlib.Path(filename) counts = ( (text := path.read_text()).count("\n"), # Number of lines len(text.split()), # Number of words len(text), # Number of characters ) print(*counts, path)Вы присваиваете содержимому файла значение
text, которое вы повторно используете в следующих двух вычислениях. Обратите внимание на расположение круглых скобок, которые помогают понять, чтоtextбудет относиться к тексту в файле, а не к количеству строк.Программа работает по-прежнему, хотя количество слов и символов изменилось:
$ python wc.py wc.py 11 34 293 wc.pyпредварительно> кодовый блок>Как и в предыдущих примерах, альтернативный подход заключается в определении
textперед определениемcounts:wc.pyкодовый блок>import pathlib import sys for filename in sys.argv[1:]: path = pathlib.Path(filename) text = path.read_text() counts = ( text.count("\n"), # Number of lines len(text.split()), # Number of words len(text), # Number of characters ) print(*counts, path)Хотя эта реализация на одну строку длиннее предыдущей, она, вероятно, обеспечивает наилучший баланс между удобочитаемостью и эффективностью. Оператор выражения присваивания
:=не всегда является наиболее удобным для чтения решением, даже если он делает ваш код более кратким.Перечислите значения
Использование списков отлично подходит для создания и фильтрации списков. В них четко указано назначение кода, и они обычно выполняются довольно быстро.
Есть один вариант использования для понимания списка, в котором оператор walrus может быть особенно полезен. Допустим, вы хотите применить некоторую вычислительно затратную функцию
slow()к элементам вашего списка и отфильтровать полученные значения. Вы могли бы сделать что-то вроде следующего:slow_calculations.pyкодовый блок>numbers = [7, 6, 1, 4, 1, 8, 0, 6] results = [slow(num) for num in numbers if slow(num) > 0]Здесь вы фильтруете список
numbersи оставляете положительные результаты примененияslow(). Проблема с этим кодом заключается в том, что эта дорогостоящая функция вызывается дважды.Очень распространенным решением для такого рода ситуаций является переписывание вашего кода с использованием явного цикла
for:slow_calculations.pyкодовый блок>results = [] for num in numbers: value = slow(num) if value > 0: results.append(value)Это приведет к вызову
slow()только один раз. К сожалению, код стал более подробным, и его назначение стало сложнее понять. Понимание списка явно сигнализировало о том, что вы создаете новый список, в то время как в явном циклеforэто более скрыто, поскольку создание списка и использование.append()разделены несколькими строками кода. Кроме того, просмотр списка выполняется быстрее, чем повторные обращения к.append().Вы можете закодировать некоторые другие решения, используя
filter()выражение или что-то вроде двойного списка:slow_calculations.pyкодовый блок># Using filter results = filter(lambda value: value > 0, (slow(num) for num in numbers)) # Using a double list comprehension results = [value for num in numbers for value in [slow(num)] if value > 0]Хорошей новостью является то, что для каждого числа существует только один вызов
slow(). Плохая новость заключается в том, что в обоих выражениях код стал менее читабельным.Чтобы понять, что на самом деле происходит при использовании двойного списка, нужно изрядно поломать голову. По сути, второй оператор
forиспользуется только для присвоения имениvalueвозвращаемому значениюslow(num). К счастью, это похоже на то, что вы можете выполнить с помощью выражения присваивания!Вы можете переписать понимание списка с помощью оператора walrus следующим образом:
slow_calculations.pyкодовый блок>results = [value for num in numbers if (value := slow(num)) > 0]Обратите внимание, что круглые скобки вокруг
value := slow(num)обязательны. Эта версия эффективна и удобочитаема, а также хорошо передает цель кода.Примечание: Вам необходимо добавить выражение присваивания в предложение
ifдля понимания списка. Если вы попытаетесь определитьvalueс помощью другого вызоваslow(), то это не сработает:>>> results = [(value := slow(num)) for num in numbers if value > 0] NameError: name 'value' is not definedпредварительно> кодовый блок>Это вызовет
NameError, потому что предложениеifвычисляется перед выражением в начале понимания.Далее рассмотрим несколько более сложный и практичный пример. Скажите, что вы хотите использовать Канал Real Python, чтобы найти названия последних эпизодов подкаста Real Python.
Вы можете использовать Real Python Feed Reader для загрузки информации о последних публикациях Real Python. Чтобы найти названия эпизодов подкаста, вы будете использовать сторонний пакет Parse. Начните с создания виртуальной среды и установки обоих пакетов:
(venv) $ python -m pip install realpython-reader parseпредварительно> кодовый блок>Теперь вы можете ознакомиться с последними публикациями, опубликованными Real Python:
>>> from reader import feed >>> feed.get_titles() ['The Walrus Operator: Python's Assignment Expressions', 'Python News Roundup: August 2024', 'The Real Python Podcast – Episode #216: The Black Python Devs Community', 'Asynchronous Iterators and Iterables in Python', ...]предварительно> кодовый блок>Названия подкастов начинаются с
"The Real Python Podcast", поэтому здесь вы можете создать шаблон, который Parse сможет использовать для их идентификации:>>> import parse >>> pattern = parse.compile( ... "The Real Python Podcast – Episode #{num:d}: {name}" ... )предварительно> кодовый блок>Предварительная компиляция шаблона ускоряет последующие сравнения, особенно если вы хотите сопоставлять один и тот же шаблон снова и снова. Вы можете проверить, соответствует ли строка вашему шаблону, используя либо
pattern.parse(), либоpattern.search():>>> pattern.parse( ... "The Real Python Podcast – Episode #216: " ... "The Black Python Devs Community" ... ) ... <Result () {'num': 216, 'name': 'The Black Python Devs Community'}>предварительно> кодовый блок>Обратите внимание, что Parse может определить номер эпизода подкаста и название эпизода. Эпизод количество преобразуется в число тип данных, потому что вы использовали
:dспецификатор формата.Пора вернуться к текущей задаче. Чтобы просмотреть список всех последних названий подкастов, вам нужно проверить, соответствует ли каждая строка вашему шаблону, а затем разобрать название эпизода. Первая попытка может выглядеть примерно так:
>>> import parse >>> from reader import feed >>> pattern = parse.compile( ... "The Real Python Podcast – Episode #{num:d}: {name}" ... ) >>> podcasts = [ ... pattern.parse(title)["name"] ... for title in feed.get_titles() ... if pattern.parse(title) ... ] >>> podcasts[:3] ['The Black Python Devs Community', 'Using GraphQL in Django With Strawberry & Prototype Purgatory', 'Build Captivating Display Tables in Python With Great Tables']предварительно> кодовый блок>Несмотря на то, что это работает, вы можете заметить ту же проблему, с которой сталкивались ранее. Вы анализируете каждое название дважды, потому что отфильтровываете заголовки, соответствующие вашему шаблону, а затем используете этот же шаблон для выбора названия эпизода.
Как вы делали ранее, вы можете избежать двойной работы, переписав понимание списка, используя либо явный цикл
for, либо двойное понимание списка. Однако использовать оператор walrus еще проще:>>> podcasts = [ ... podcast["name"] ... for title in feed.get_titles() ... if (podcast := pattern.parse(title)) ... ]предварительно> кодовый блок>Выражения присваивания хорошо подходят для упрощения работы со списками такого рода. Они обеспечивают удобочитаемость кода и избавляют вас от повторного выполнения потенциально дорогостоящей операции.
Примечание: У настоящего подкаста на Python есть своя отдельная RSS-лента, которую вам следует использовать, если вы хотите поиграться с информацией только о подкасте. Вы можете получить все названия эпизодов со следующим кодом:
>>> from reader import feed >>> podcasts = feed.get_titles("https://realpython.com/podcasts/rpp/feed")предварительно> кодовый блок>Смотрите Настоящий подкаст на Python, чтобы узнать, как прослушать его с помощью проигрывателя подкастов.
В этом разделе вы сосредоточились на примерах, в которых вы можете переписать значения списка, используя оператор walrus. Те же принципы применяются и в том случае, если вы видите, что вам нужно повторить операцию в режиме понимание по словарю, установить понимание или генераторное выражение.
В следующем примере используется генераторное выражение для вычисления средней длины названий эпизодов, длина которых превышает
50символов:>>> import statistics >>> statistics.mean( ... title_length ... for title in podcasts ... if (title_length := len(title)) > 50 ... ) 61.525предварительно> кодовый блок>Выражение генератора использует выражение присваивания, чтобы избежать двойного вычисления длины заголовка каждого эпизода.
В то время как циклы
В Python есть две разные конструкции циклов:
forциклы иwhileциклы. Обычно вы используете циклfor, когда вам нужно выполнить итерацию по известной последовательности элементов. Циклwhile, с другой стороны, предназначен для случаев, когда вы заранее не знаете, сколько раз вам нужно будет повторить цикл.В циклах
whileвам нужно определить и проверить конечное условие в начале цикла. Иногда это приводит к появлению неудобного кода, когда вам нужно выполнить некоторую настройку перед выполнением проверки. Вот фрагмент из программы-викторины с несколькими вариантами ответов, в которой пользователю предлагается ответить на вопрос одним из нескольких допустимых вариантов ответа:walrus_quiz.pyкодовый блок>question = "Do you use the walrus operator?" valid_answers = {"yes", "Yes", "y", "Y", "no", "No", "n", "N"} user_answer = input(f"\n{question} ") while user_answer not in valid_answers: print(f"Please answer one of {', '.join(valid_answers)}") user_answer = input(f"\n{question} ")Это работает, но содержит неудачное повторение двух одинаковых строк
input(). Необходимо получить хотя бы один ответ от пользователя, прежде чем проверять, корректен он или нет. Затем у вас есть второй вызовinput()внутри циклаwhile, чтобы запросить второй ответ в случае, если исходныйuser_answerбыл неверным.Если вы хотите сделать свой код более удобным в обслуживании, то довольно часто переписывать логику такого рода с помощью
while Trueцикла. Вместо того, чтобы делать проверку частью основного оператораwhile, проверка выполняется позже в цикле вместе с явнымbreak:walrus_quiz.pyкодовый блок># ... while True: user_answer = input(f"\n{question} ") if user_answer in valid_answers: break print(f"Please answer one of {', '.join(valid_answers)}")Преимущество этого метода в том, что он позволяет избежать повторения. Однако фактическую проверку теперь труднее обнаружить.
Выражения присваивания могут упростить такого рода циклы. В этом примере теперь вы можете вернуть проверку вместе с
whileтам, где это имеет больше смысла:walrus_quiz.pyкодовый блок># ... while (user_answer := input(f"\n{question} ")) not in valid_answers: print(f"Please answer one of {', '.join(valid_answers)}")Оператор
whileстал немного плотнее, но теперь код передает намерение более четко, без повторяющихся строк или кажущихся бесконечными циклов.Вы можете развернуть поле ниже, чтобы увидеть полный код программы викторины с несколькими вариантами ответов, и самостоятельно задать пару вопросов об операторе walrus.
Этот скрипт запускает тест с несколькими вариантами ответов. Вам будут заданы все вопросы по порядку, но порядок ответов каждый раз будет меняться:
walrus_quiz.pyкодовый блок>import random import string QUESTIONS = { "What is the formal name of PEP 572?": [ "Assignment Expressions", "Named Expressions", "The Walrus Operator", "The Colon Equals Operator", ], "Which one of these is an invalid use of the walrus operator?": [ "[y**2 for x in range(10) if y := f(x) > 0]", "print(y := f(x))", "(y := f(x))", "any((y := f(x)) for x in range(10))", ], } num_correct = 0 for question, answers in QUESTIONS.items(): correct = answers[0] random.shuffle(answers) coded_answers = dict(zip(string.ascii_lowercase, answers)) valid_answers = sorted(coded_answers.keys()) for code, answer in coded_answers.items(): print(f" {code}) {answer}") while (user_answer := input(f"\n{question} ")) not in valid_answers: print(f"Please answer one of {', '.join(valid_answers)}") if coded_answers[user_answer] == correct: print(f"Correct, the answer is {user_answer!r}\n") num_correct += 1 else: print(f"No, the answer is {correct!r}\n") print(f"You got {num_correct} correct out of {len(QUESTIONS)} questions")Обратите внимание, что первый ответ считается правильным, в то время как остальные служат отвлекающими факторами. Вы можете добавить дополнительные вопросы к тесту самостоятельно. Не стесняйтесь делиться своими вопросами с сообществом в разделе комментариев под руководством!
Смотрите раздел Создание приложения для викторин на Python, если вы хотите глубже изучить использование Python для проведения викторин для себя или своих друзей. Вы также можете проверить свои знания об операторе walrus:
<отметить класс="marker-highlight"> Пройдите тест: отметить> Проверьте свои знания с помощью нашего интерактивного теста ”Оператор Walrus: выражения присваивания в Python". По завершении вы получите оценку, которая поможет вам отслеживать прогресс в обучении:
<время работы/>![]()
Интерактивная викторина
Оператор Walrus: выражения присваивания в PythonВ этом тесте вы проверите свое понимание оператора walrus в Python. Этот оператор был представлен в Python 3.8, и его понимание может помочь вам написать более краткий и эффективный код.
Часто можно упростить циклы
while, используя выражения присваивания. В исходном документе PEP показан пример из стандартной библиотеки, который подтверждает ту же мысль.Свидетели и контрпримеры
В примерах, которые вы видели до сих пор, оператор выражения присваивания
:=выполняет, по сути, ту же работу, что и оператор присваивания=в вашем старом коде. Вы видели, как упростить код, и теперь узнаете о другом типе вариантов использования, которые делает возможным этот оператор.В этом разделе вы узнаете, как найти свидетелей при вызове
any()используя умный трюк, который не сразу становится возможным без использования оператора walrus. В данном контексте свидетель - это элемент, который удовлетворяет проверке и возвращает значениеany()True.Применяя аналогичную логику, вы также узнаете, как можно найти контрпримеры при работе с
all(). Контрпримером в данном контексте является элемент, который не удовлетворяет проверке и вызывает возвратall()False.Чтобы иметь некоторые данные для работы, определите следующий список названий городов:
>>> cities = ["Vancouver", "Oslo", "Berlin", "Krakow", "Graz", "Belgrade"]предварительно> кодовый блок>Вы можете использовать
any()иall()для ответа на вопросы о ваших данных:>>> # Does ANY city name start with "B"? >>> any(city.startswith("B") for city in cities) True >>> # Does ANY city name have at least 10 characters? >>> any(len(city) >= 10 for city in cities) False >>> # Do ALL city names contain "A" or "L"? >>> all(set(city.lower()) & set("al") for city in cities) True >>> # Do ALL city names start with "B"? >>> all(city.startswith("B") for city in cities) Falseпредварительно> кодовый блок>В каждом из этих случаев
any()иall()дают вам простыеTrueилиFalseответы. Что, если вам также интересно посмотреть пример или контрпример с названиями городов? Было бы неплохо посмотреть, что является причиной вашего результатаTrueилиFalse:
Начинается ли название любого города на
"B"?Да, потому что
"Berlin"начинается с"B".Сделать все названия городов начинаются на
"B"?Нет, потому что
"Oslo"начинается не с"B".Другими словами, вам нужен свидетель или контрпример для обоснования ответа.
Захват свидетеля для выражения
any()в более ранних версиях Python не был интуитивно понятным. Если вы вызывалиany()по списку, а затем поняли, что вам также нужен свидетель, вам, как правило, нужно переписать свой код:>>> witnesses = [city for city in cities if city.startswith("B")] >>> if witnesses: ... print(f"{witnesses[0]} starts with B") ... else: ... print("No city name starts with B") ... Berlin starts with Bпредварительно> кодовый блок>Здесь вы сначала записываете все названия городов, которые начинаются с
"B". Затем, если есть хотя бы одно такое название города, вы выводите первое название города, начинающееся с"B". Обратите внимание, что здесь вы на самом деле не используетеany(), хотя выполняете аналогичную операцию с пониманием списка.Используя оператор
:=, вы можете найти свидетелей непосредственно в вашихany()выражениях:>>> if any((witness := city).startswith("B") for city in cities): ... print(f"{witness} starts with B") ... else: ... print("No city name starts with B") ... Berlin starts with Bпредварительно> кодовый блок>Вы можете зафиксировать свидетеля внутри выражения
any(). Причина, по которой это работает, немного тонка и основана наany()иall(), использующих оценку короткого замыкания: они проверяют только столько элементов, сколько необходимо для определения результата.Примечание: Если вы хотите проверить, начинаются ли все названия городов на букву
"B", то вы можно поискать контрпример, заменивany()наall()и обновив функцииprint(), чтобы сообщить о первом элементе, который не прошел проверку.Вы можете более четко увидеть, что происходит, заключив
.startswith("B")в функцию, которая также выводит, какой элемент проверяется:>>> def starts_with_b(name): ... print(f"Checking {name}: {(result := name.startswith('B'))}") ... return result ... >>> any(starts_with_b(city) for city in cities) Checking Vancouver: False Checking Oslo: False Checking Berlin: True Trueпредварительно> кодовый блок>Обратите внимание, что
any()на самом деле проверяет не все элементы вcities. Он проверяет элементы только до тех пор, пока не найдет тот, который удовлетворяет условию. Объединение операторов:=иany()работает путем итеративного присвоения каждому проверяемому элементу значенияwitness. Однако сохраняется только последний такой элемент, который показывает, какой элемент был проверен пользователем в последний раз.any().Даже если
any()возвращаетFalse, свидетель найден:>>> any(len(witness := city) >= 10 for city in cities) False >>> witness 'Belgrade'предварительно> кодовый блок>Однако в этом случае
witnessне дает никакой информации.'Belgrade'не содержит десяти или более символов. В качестве свидетеля отображается только то, какой элемент был оценен последним.Синтаксис оператора Walrus
Одна из основных причин, по которой присваивания изначально не были выражениями в Python, заключается в том, что визуальное сходство оператора присваивания (
=) и оператора сравнения равенства (==) потенциально может привести к ошибкам.При внедрении выражений присваивания разработчики много думали о том, как избежать подобных ошибок с оператором walrus. Как упоминалось ранее, одной из важных особенностей является то, что оператор
:=никогда не допускается в качестве прямой замены оператора=и наоборот.Как вы видели в начале этого руководства, вы не можете использовать простое выражение присваивания для присвоения значения:
>>> walrus := True File "<stdin>", line 1 walrus := True ^ SyntaxError: invalid syntaxпредварительно> кодовый блок>Синтаксически допустимо использовать выражение присваивания только для присвоения значения, но вам нужно добавить круглые скобки:
>>> (walrus := True) Trueпредварительно> кодовый блок>Несмотря на то, что это возможно, это яркий пример того, как вам следует избегать оператора walrus и вместо этого использовать традиционный оператор присваивания.
В PEP 572 приведены несколько других примеров, в которых оператор
:=либо запрещен, либо не рекомендуется. Все приведенные ниже примеры вызываютSyntaxError:>>> lat = lon := 0 SyntaxError: invalid syntax >>> angle(phi = lat := 59.9) SyntaxError: invalid syntax >>> def distance(phi = lat := 0, lam = lon := 0): SyntaxError: invalid syntaxпредварительно> кодовый блок>Во всех этих случаях лучше использовать
=. Следующие примеры аналогичны и представляют собой юридический код. Однако оператор walrus не улучшает ваш код ни в одном из этих случаев:>>> lat = (lon := 0) # Discouraged >>> angle(phi = (lat := 59.9)) # Discouraged >>> def distance(phi = (lat := 0), lam = (lon := 0)): # Discouraged ... pass ...предварительно> кодовый блок>Ни один из этих примеров не делает ваш код более читабельным. Вместо этого вам следует выполнить дополнительное присваивание отдельно, используя традиционный оператор присваивания. Смотрите PEP 572 для получения более подробной информации об обосновании.
Есть один вариант использования, когда последовательность символов
:=уже является допустимой в Python. В f-строках используется двоеточие (:), чтобы отделить значения от их спецификации формата. Например:>>> x = 3 >>> f"{x:=8}" ' 3'предварительно> кодовый блок>
:=В этом случае действительно выглядит как оператор "морж", но эффект совершенно иной. Чтобы интерпретироватьx:=8внутри f-строки, выражение разбивается на три части:x,:, и=8.Здесь
x- это значение,:служит разделителем, а=8- это спецификация формата. В соответствии с мини-языком спецификации формата Python , в этом контексте=определяет параметр выравнивания. В этом случае значение заполняется пробелами в поле шириной8.Чтобы использовать выражения присваивания внутри f-строк, вам необходимо добавить круглые скобки:
>>> x = 3 >>> f"{(x := 8)}" '8' >>> x 8предварительно> кодовый блок>Это обновит значение
x, как и ожидалось. Однако, вероятно, вам лучше использовать традиционные назначения за пределами ваших f-строк.Теперь рассмотрим некоторые другие ситуации, в которых выражения присваивания недопустимы:
Присвоение атрибутов и элементов: Вы можете присваивать только простые имена, а не имена с пунктиром или индексом:
>>> (mapping["hearts"] := "♥") SyntaxError: cannot use assignment expressions with subscript >>> (number.answer := 42) SyntaxError: cannot use assignment expressions with attributeЭто приводит к сбою с описательным сообщением об ошибке. Простого обходного пути не существует.
Повторяемая распаковка: Вы не можете распаковать файл при использовании оператора walrus:
кодовый блок>>>> lat, lon := 59.9, 10.8 SyntaxError: invalid syntaxЕсли вы заключите все выражение в круглые скобки, то Python интерпретирует его как кортеж из 3 элементов с тремя элементами
lat,59.9, и10.8.Расширенное присваивание: Вы не можете использовать оператор walrus в сочетании с операторами расширенного присваивания, такими как
+=. В результате возникает проблемаSyntaxError:>>> count +:= 1 SyntaxError: invalid syntaxСамым простым решением было бы выполнить расширение явно. Вы могли бы, например, сделать так
(count := count + 1). В PEP 577 изначально описывалось, как добавлять расширенные выражения присваивания в Python, но предложение было отозвано.Когда вы используете оператор walrus, он во многом будет вести себя аналогично традиционным операторам присваивания:
Область действия цели назначения такая же, как и для назначений. Это будет соответствовать правилу LEGB. Как правило, присвоение происходит в локальной области видимости, но если целевое имя уже объявлено
globalилиnonlocal, это объявление выполняется.Приоритет оператора walrus может вызвать некоторую путаницу. Он связан менее жестко, чем все другие операторы, за исключением запятой, поэтому вам могут понадобиться круглые скобки для разграничения присваиваемого выражения. В качестве примера обратите внимание, что происходит, когда вы не используете круглые скобки:
>>> number = 3 >>> if square := number ** 2 > 5: ... print(square) ... True
squareпривязан ко всему выражениюnumber ** 2 > 5. Другими словами,squareполучает значениеTrue, а не значениеnumber ** 2, что и было задумано. В этом случае вы можете заключить выражение в круглые скобки:>>> number = 3 >>> if (square := number ** 2) > 5: ... print(square) ... 9Круглые скобки делают утверждение
ifболее понятным и фактически правильным.Есть еще один нюанс. При назначении кортежа с помощью оператора walrus всегда необходимо заключать кортеж в круглые скобки. Сравните следующие назначения:
>>> walrus = 3.7, False >>> walrus (3.7, False) >>> (walrus := 3.8, True) (3.8, True) >>> walrus 3.8 >>> (walrus := (3.8, True)) (3.8, True) >>> walrus (3.8, True)Обратите внимание, что во втором примере
walrusпринимает значение3.8, а не весь кортеж3.8, True. Это связано с тем, что оператор:=связывает более жестко, чем запятая. Это может показаться немного раздражающим. Однако, если оператор:=связан менее жестко, чем запятая, то было бы невозможно использовать оператор walrus в вызовах функций с более чем одним аргументом.Рекомендации по стилю для оператора walrus в основном такие же, как и для оператора
=, используемого для назначения. Во-первых, всегда добавляйте в свой код пробелы вокруг оператора:=. Во-вторых, при необходимости заключайте выражение в круглые скобки, но избегайте добавления лишних скобок, которые вам не нужны.Общая схема выражений присваивания заключается в том, чтобы упростить их использование, когда они полезны, но избегать чрезмерного использования, когда они могут загромождать ваш код.
Подводные камни оператора Walrus
Оператор walrus - это более новый синтаксис, доступный только в Python 3.8 и более поздних версиях. Это означает, что любой написанный вами код, использующий синтаксис
:=, будет работать только в этих версиях Python.Если вам нужна поддержка устаревших версий Python, вы не сможете отправлять код, использующий выражения присваивания. Как вы узнали из этого руководства, вы всегда можете писать код без оператора walrus и оставаться совместимым со старыми версиями.
Опыт работы с оператором walrus показывает, что
:=не произведет революцию в Python. Вместо этого использование выражений присваивания там, где они полезны, может помочь вам внести несколько небольших улучшений в ваш код, которые могут принести пользу вашей работе в целом.Вы столкнетесь с несколькими ситуациями, когда сможете использовать оператор walrus, но это не обязательно улучшит читаемость или эффективность вашего кода. В таких случаях вам лучше писать свой код более традиционным способом.
Заключение
Теперь вы знаете, как работает оператор walrus и как вы можете использовать его в своем собственном коде. Используя синтаксис
:=, вы можете избежать различного рода повторений в своем коде и сделать его более эффективным, а также более простым для чтения и сопровождения. В то же время не следует повсеместно использовать выражения присваивания. Они помогут вам только в определенных случаях использования.В этом руководстве вы узнали, как:
- Определите оператора walrus и поймите его значение
- Понять варианты использования для оператора walrus
- Избегайте повторяющегося кода, используя оператор walrus
- Преобразование между кодом, использующим оператор walrus, и кодом, использующим другие методы присвоения
- Используйте соответствующий стиль в ваших выражениях присваивания
Чтобы узнать больше о деталях выражений присваивания, смотрите PEP 572. Вы также можете ознакомиться с докладом на конференции PyCon 2019 PEP 572: Оператор Walrus, где Дастин Ингрэм рассказывает как об операторе walrus, так и о дискуссии вокруг ВООДУШЕВЛЕНИЕ.
Получите свой код: Нажмите здесь, чтобы загрузить бесплатный пример кода, который показывает, как использовать оператор walrus в Python.
Часто задаваемые вопросы
Теперь, когда у вас есть некоторый опыт работы с оператором walrus в Python, вы можете воспользоваться приведенными ниже вопросами и ответами, чтобы проверить свое понимание и резюмировать то, что вы узнали.
Эти часто задаваемые вопросы относятся к наиболее важным понятиям, которые вы рассмотрели в этом руководстве. Нажмите на переключатель Показывать/скрывать рядом с каждым вопросом, чтобы открыть ответ.
Оператор walrus (
:=) - это оператор, который позволяет присваивать значения переменным как части выражения.
Да. Или, более правильно, оператор walrus - это другой термин для обозначения оператора выражения присваивания, который позволяет присваивать значения в выражении присваивания.
Оператор walrus используется, когда требуется упростить код за счет уменьшения количества повторений, особенно в циклах, при работе со списками и условными выражениями.
Выражения присваивания возвращают присваиваемое значение, что позволяет использовать их в выражениях, в то время как обычные присваивания значения не возвращают.
Используйте оператор walrus, чтобы сделать ваш код более кратким и читабельным, но избегайте чрезмерного использования его, когда традиционные задания более понятны и уместны.
<отметить класс="marker-highlight"> Пройдите тест: отметить> Проверьте свои знания с помощью нашего интерактивного теста ”Оператор Walrus: выражения присваивания в Python". По завершении вы получите оценку, которая поможет вам отслеживать прогресс в обучении:
<время работы/>![]()
Интерактивная викторина
Оператор Walrus: выражения присваивания в PythonВ этом тесте вы проверите свое понимание оператора walrus в Python. Этот оператор был представлен в Python 3.8, и его понимание может помочь вам написать более краткий и эффективный код.
<статус завершения article-slug="python-walrus-operator" class="btn-group mb-0" data-api-article-bookmark-url="/api/v1/articles/python-walrus-operator/bookmark/" data-api-article-завершение-status-url="/api/v1/articles/python-walrus-operator/завершение_статуса/"> статус завершения> <кнопка поделиться bluesky-text="Интересная статья на #Python от @realpython.com :" email-body="Ознакомьтесь с этой статьей о Python:%0A%0 Оператор Walrus: выражения присваивания в Python" email-subject="Статья о Python для вас" twitter-text="Интересная статья о Python" автор @realpython:" url="https://realpython.com/python-walrus-operator /" url-title="Оператор Walrus: выражения присваивания в Python"> кнопка поделиться>Смотрите сейчас, к этому уроку прилагается соответствующий видеокурс, созданный командой Real Python. Посмотрите его вместе с письменным руководством, чтобы углубить свое понимание: Выражения присваивания в Python и использование оператора Walrus
Back to Top