- Оригинальный адрес:Multiple assignment and tuple unpacking improve Python code readability
- Оригинальный автор:Trey Hunner
- Перевод с:Программа перевода самородков
- Постоянная ссылка на эту статью:GitHub.com/rare earth/gold-no…
- Переводчик:lsvih
- Корректор:Zheaoli
Независимо от того, обучаю ли я новичков или опытных программистов на Python, я считаю, чтоМногие программисты Python не пользуются преимуществами функции множественного назначения..
Множественное присваивание (также часто называемое распаковкой кортежей или итерируемой распаковкой) позволяет присваивать значения нескольким переменным одновременно в рамках одной строки кода. Эта функция кажется простой в освоении, но может быть сложно вспомнить, когда вам действительно нужно ее использовать.
В этой статье я расскажу, что такое множественное присваивание, приведу несколько распространенных примеров множественного присваивания и узнаю о некоторых меньших и часто упускаемых из виду способах множественного присваивания.
Обратите внимание, что в этой статье мы будем использоватьf-stringsЭта функция доступна только в Python 3.6 и выше.Если ваша версия Python старше, вы можете использовать строкуformat
Метод для замены этой функции.
Как реализовать множественное присваивание
В этой статье я буду использовать разные слова, такие как «множественное присваивание», «распаковка кортежа», «итеративная распаковка объекта» и т. д., но на самом деле они означают одно и то же.
Множественное присваивание в Python выглядит так:
>>> x, y = 10, 20
Здесь мы будемx
установлен в10
,y
установлен в20
.
С точки зрения более низкого уровня мы фактически создаем10, 20
кортеж, затем пройтись по этому кортежу и присвоить два полученных числа, чтобыx
иy
.
Это должно быть легче понять, написав следующий синтаксис:
>>> (x, y) = (10, 20)
В Python скобки вокруг кортежей игнорируются, поэтому их можно опустить для «множественного присваивания» (написанного выше в синтаксисе, подобном кортежу). Следующие строки кода эквивалентны:
>>> x, y = 10, 20
>>> x, y = (10, 20)
>>> (x, y) = 10, 20
>>> (x, y) = (10, 20)
Множественное присваивание часто называют просто "распаковкой кортежа", поскольку в большинстве случаев оно используется для кортежей. Но на самом деле мы можем выполнять множественное присваивание с любым итерируемым объектом, кроме кортежей. Вот результат использования списка (списка):
>>> x, y = [10, 20]
>>> x
10
>>> y
20
Вот результат использования строк:
>>> x, y = 'hi'
>>> x
'h'
>>> y
'i'
Все, что можно использовать в цикле, можно «развернуть» так же, как при распаковке кортежа и множественном присваивании.
Вот еще один пример, демонстрирующий, что множественное присваивание можно использовать для любого числа, любой переменной (даже объектов, которые мы создаем сами):
>>> point = 10, 20, 30
>>> x, y, z = point
>>> print(x, y, z)
10 20 30
>>> (x, y, z) = (z, y, x)
>>> print(x, y, z)
30 20 10
Обратите внимание, что в последней строке приведенного выше примера мы только поменяли местами имена переменных. Множественное присваивание позволяет нам легко реализовать эту ситуацию.
Ниже мы обсудим, как использовать множественное присваивание.
Распаковать в цикле for
ты будешь частоfor
Смотрите несколько назначений в циклах. Следующие примеры иллюстрируют:
Сначала создайте словарь (dict):
>>> person_dictionary = {'name': "Trey", 'company': "Truthful Technology LLC"}
Следующий метод перебора словаря встречается относительно редко:
for item in person_dictionary.items():
print(f"Key {item[0]} has value {item[1]}")
Но вы часто будете видеть, как программисты Python пишут это с помощью множественного присваивания:
for key, value in person_dictionary.items():
print(f"Key {key} has value {value}")
когда пишешь в цикле forfor X in Y
, вы на самом деле говорите Python исправитьX
Выполните задание. с использованием=
Как и символическое присваивание, здесь также может использоваться множественное присваивание.
Такой способ написания:
for key, value in person_dictionary.items():
print(f"Key {key} has value {value}")
По сути, это то же самое, что и этот способ записи:
for item in person_dictionary.items():
key, value = item
print(f"Key {key} has value {value}")
По сравнению с предыдущим примером мы убрали лишнее лишнее присваивание.
Поэтому множественное присваивание полезно для распаковки элементов словаря в пары ключ-значение. Кроме того, он также доступен в других местах:
во встроенной функцииenumerate
Это также полезный сценарий для множественного присваивания, когда значение разбито на пары:
for i, line in enumerate(my_file):
print(f"Line {i}: {line}")
иzip
функция:
for color, ratio in zip(colors, ratios):
print(f"It's {ratio*100}% {color}.")
for (product, price, color) in zip(products, prices, colors):
print(f"{product} is {color} and costs ${price:.2f}")
если ты правenumerate
иzip
Незнакомо, см. предыдущую статью автораlooping with indexes in Python.
Некоторые новички в Python частоfor
Видя множественное присваивание в цикле, а затем думая, что его можно использовать только с циклами. Но на самом деле множественное присваивание можно использовать не только в циклическом присваивании, но и в любом другом месте, где требуется присваивание.
Альтернатива жестко запрограммированному индексу
Мало кто жестко прописывает индекс в коде (например,point[0]
,items[1]
,vals[-1]
):
print(f"The first item is {items[0]} and the last item is {items[-1]}")
Когда вы видите жестко заданные индексы в коде Python, вы обычно можетеИспользуйте множественное присваивание, чтобы сделать ваш код более читабельным.
Вот некоторый код, который использует жестко заданные индексы:
def reformat_date(mdy_date_string):
"""Reformat MM/DD/YYYY string into YYYY-MM-DD string."""
date = mdy_date_string.split('/')
return f"{date[2]}-{date[0]}-{date[1]}"
Мы можем присвоить значения трем переменным месяца, дня и года с помощью нескольких присваиваний, чтобы сделать код более читабельным:
def reformat_date(mdy_date_string):
"""Reformat MM/DD/YYYY string into YYYY-MM-DD string."""
month, day, year = mdy_date_string.split('/')
return f"{year}-{month}-{day}"
Поэтому, когда вы будете готовы жестко закодировать индекс, остановитесь и подумайте, следует ли вам использовать множественное присваивание, чтобы улучшить читабельность вашего кода.
Множественное назначение очень строго
Когда мы распаковываем итерируемые объекты, условия множественного присваивания очень строгие.
Если вы распаковываете большую итерацию в меньший набор объектов, вы получите следующую ошибку:
>>> x, y = (10, 20, 30)
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
ValueError: too many values to unpack (expected 2)
Если вы распаковываете меньший итерируемый объект в больший набор объектов, вы получите следующую ошибку:
>>> x, y, z = (10, 20)
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
ValueError: not enough values to unpack (expected 3, got 2)
Такой строгий лимит на самом деле хорош, если у нас будет неожиданное количество объектов при обработке элементов, множественное присваивание напрямую сообщит об ошибке, чтобы мы могли найти некоторые ошибки, которые еще не были обнаружены.
Например. Предположим, у нас есть простая программа командной строки, которая принимает аргументы в необработанном виде, например:
import sys
new_file = sys.argv[1]
old_file = sys.argv[2]
print(f"Copying {new_file} to {old_file}")
Эта программа ожидает принять два аргумента, как показано ниже:
$ my_program.py file1.txt file2.txt
Copying file1.txt to file2.txt
Но если ввести три параметра при запуске программы, ошибки не будет:
$ my_program.py file1.txt file2.txt file3.txt
Copying file1.txt to file2.txt
Поскольку мы не проверяли, что полученные параметры равны 2, об ошибке не будет сообщено.
Если вместо жестко запрограммированных индексов используется несколько присваиваний, то присваивание проверит, действительно ли программа получила ожидаемое количество аргументов:
import sys
_, new_file, old_file = sys.argv
print(f"Copying {new_file} to {old_file}")
Уведомление:Мы использовали_
переменная, то есть мы не хотим сосредотачиваться наsys.argv[0]
(соответствует названию нашей программы). использовать_
Объект для игнорирования переменных, не представляющих интереса, является распространенным синтаксисом.
Альтернативное разделение массива
Из вышеизложенного мы пришли к выводу, что вместо жестко закодированных индексов можно использовать множественные присваивания, и это строго зависит от гарантии того, что мы имеем дело с кортежами или итерируемыми объектами правильного размера.
Кроме того, вместо жестко закодированного разделения массива можно использовать множественное присваивание.
«Разделить» — это метод ручного удаления частей списка или другой последовательности,
Вот один из способов «жестко закодировать» разделение с помощью числового индекса:
all_after_first = items[1:]
all_but_last_two = items[:-2]
items_with_ends_removed = items[1:-1]
Когда вы разбиваете и обнаруживаете, что переменная не используется в разделенном индексе, вы можете заменить ее множественным присвоением. Чтобы реализовать множественное присваивание для разделения массива, мы будем использовать функцию, не упомянутую ранее:*
символ.
*
В синтаксис множественного присваивания в Python 3 была добавлена нотация, позволяющая получить «оставшиеся» элементы при распаковке:
>>> numbers = [1, 2, 3, 4, 5, 6]
>>> first, *rest = numbers
>>> rest
[2, 3, 4, 5, 6]
>>> first
1
следовательно,*
Позволяет нам заменить жестко заданные разбиения при взятии конца массива.
Следующие две строки эквивалентны:
>>> beginning, last = numbers[:-1], numbers[-1]
>>> *beginning, last = numbers
Следующие две строки также эквивалентны:
>>> head, middle, tail = numbers[0], numbers[1:-1], numbers[-1]
>>> head, *middle, tail = numbers
имеют*
А после множественного присваивания можно заменить все аналогично следующему коду:
main(sys.argv[0], sys.argv[1:])
Это может быть записано как более понятный код, например:
program_name, *arguments = sys.argv
main(program_name, arguments)
В заключение, если вы пишете жестко закодированный код разбиения, учтите, что вы можете использовать множественные присваивания, чтобы сделать логику этих разбиений более понятной.
глубокая распаковка
Это то, что программисты на Python долгое время часто упускали из виду. Хотя это не так распространено, как многократные повторяющиеся значения, о которых я упоминал ранее, вы по достоинству оцените его преимущества, когда будете его использовать.
Ранее мы видели множественное присваивание, используемое для распаковки кортежей или других итераций, но еще не видели этого.Сделайте еще один шаг впередглубокая распаковка.
Множественное присвоение в следующем примеремелкий, потому что он выполняет только один слой распаковки:
>>> color, point = ("red", (1, 2, 3))
>>> color
'red'
>>> point
(1, 2, 3)
И следующее многократное присвоение можно рассматривать какглубина, потому что это будетpoint
Кортеж также далее распаковывается вx
,y
,z
Переменная:
>>> color, (x, y, z) = ("red", (1, 2, 3))
>>> color
'red'
>>> x
1
>>> y
2
Приведенный выше пример может сбить с толку, поэтому давайте проясним его, добавив круглые скобки вокруг оператора присваивания:
>>> (color, (x, y, z)) = ("red", (1, 2, 3))
Вы можете видеть, что при распаковке первого слоя получаются два объекта, но этот оператор снова распаковывает второй объект, в результате чего получаются еще три объекта. Затем назначьте первый объект и три новых решенных объекта новому объекту (color
,x
,y
,z
).
Вот пример этих двух списков:
start_points = [(1, 2), (3, 4), (5, 6)]
end_points = [(-1, -2), (-3, 4), (-6, -5)]
Следующий код является примером обработки двух приведенных выше списков с поверхностной распаковкой:
for start, end in zip(start_points, end_points):
if start[0] == -end[0] and start[1] == -end[1]:
print(f"Point {start[0]},{start[1]} was negated.")
Проделаем то же самое с глубокой распаковкой:
for (x1, y1), (x2, y2) in zip(start_points, end_points):
if x1 == -x2 and y1 == -y2:
print(f"Point {x1},{y1} was negated.")
Обратите внимание, что во втором примере при работе с объектами тип объекта значительно более нагляден и понятен. Глубокая распаковка делает очевидным, что в каждом цикле мы получаем два кортежа.
Глубокая распаковка часто используется во вложенных циклах, которые одновременно получают несколько элементов. Например, вы можете использовать обаenumerate
иzip
при применении глубокого множественного присвоения:
items = [1, 2, 3, 4, 2, 1]
for i, (first, last) in enumerate(zip(items, reversed(items))):
if first != last:
raise ValueError(f"Item {i} doesn't match: {first} != {last}")
Я упоминал ранее, что множественное присваивание очень строго относится к размеру итерируемых объектов и размеру распаковки Мы также можем использовать это при повторной распаковке.Строго контролируйте размер итерируемого объекта.
Такая запись работает нормально:
>>> points = ((1, 2), (-1, -2))
>>> points[0][0] == -points[1][0] and points[0][1] == -point[1][1]
True
Этот глючно выглядящий код также отлично работает:
>>> points = ((1, 2, 4), (-1, -2, 3), (6, 4, 5))
>>> points[0][0] == -points[1][0] and points[0][1] == -point[1][1]
True
Этот способ записи также работает:
>>> points = ((1, 2), (-1, -2))
>>> (x1, y1), (x2, y2) = points
>>> x1 == -x2 and y1 == -y2
True
Но это не работает:
>>> points = ((1, 2, 4), (-1, -2, 3), (6, 4, 5))
>>> (x1, y1), (x2, y2) = points
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
ValueError: too many values to unpack (expected 2)
При присвоении нескольких значений переменным мы фактически делаем специальное утверждение об итерируемом объекте. Таким образом, множественное присваивание облегчает другим разобраться в вашем коде (из-за лучшей читаемости кода), а также позволяет компьютеру лучше понимать ваш код (Поскольку код был подтвержден для обеспечения правильности).
Использовать синтаксис типа списка
Все множественные присваивания, о которых я упоминал выше, используют синтаксис, подобный кортежу, но на самом деле множественные присваивания можно использовать с любым итерируемым объектом. И этот синтаксис, подобный кортежу, также делает множественное присваивание, часто называемое «распаковкой кортежа». Точнее, множественное присваивание следует называть "итерируемой распаковкой объекта".
Я не упоминал ранее, что множественные присваивания могут быть записаны в синтаксисе, подобном списку.
Вот простейший пример множественного присваивания с использованием синтаксиса списка:
>>> [x, y, z] = 1, 2, 3
>>> x
1
Такой способ написания кажется странным. Зачем разрешать этот синтаксис списка в дополнение к синтаксису кортежа?
Я также редко использую эту функцию, но в некоторых особых случаях она может усложнить код.лаконичный.
Например, допустим, у меня есть такой код:
def most_common(items):
return Counter(items).most_common(1)[0][0]
Наши коллеги из лучших побуждений решили использовать глубокое множественное присваивание, чтобы реорганизовать код так, чтобы он выглядел так:
def most_common(items):
(value, times_seen), = Counter(items).most_common(1)
return value
Видите последнюю запятую слева от оператора присваивания? Его легко пропустить, а запятая делает код невзрачным. Что делает запятая в этом коде?
Завершающая запятая здесь фактически создает кортеж из одного элемента, а затем глубоко распаковывает его.
Приведенный выше код можно записать по-другому:
def most_common(items):
((value, times_seen),) = Counter(items).most_common(1)
return value
Такой способ написания делает синтаксис глубокой распаковки более очевидным. Но я предпочитаю такой способ написания:
def most_common(items):
[(value, times_seen)] = Counter(items).most_common(1)
return value
Синтаксис списка в присваивании делает его более понятным: хорошо видно, что мы распаковываем одноэлементный итерируемый объект, а распаковываем и присваиваем одиночный элементvalue
иtimes_seen
объект.
Когда я вижу такой код, я почти уверен, что мы распаковываем список групп ячеек (и на самом деле это то, что код делает). Мы используем модуль коллекций здесьCounterобъект.Counter
объектmost_common
Метод позволяет нам указать длину возвращаемого списка. Здесь мы ограничиваем список, чтобы он возвращал только один элемент.
Когда вы распаковываете структуру со многими значениями (например, список) или структуру с определенным количеством значений (например, кортеж), вы можете рассмотреть возможность использования синтаксиса списка для распаковки этих спископодобных структур, что позволяет Код более "синтаксически правильный".
Если хотите, вы также можете применить некоторый синтаксис списка при распаковке спископодобной структуры с использованием спискового синтаксиса (распространенный пример — использование нескольких присваиваний*
символ):
>>> [first, *rest] = numbers
На самом деле, я не часто использую этот способ письма, потому что у меня нет такой привычки. Но если вы найдете эту нотацию полезной, подумайте об использовании ее в своем собственном коде.
Вывод: когда вы используете множественное присваивание в своем коде, подумайте, когда использовать синтаксис списка, чтобы сделать ваш код более понятным и кратким. Это также иногда улучшает читаемость кода.
Не забывайте об использовании этих множественных назначений
Множественное присваивание может улучшить читабельность и правильность кода. это позволяет вам кодироватьболее информативным, но и для распаковываемого итерируемого объектаразмерДелайте неявные утверждения.
Из того, что я заметил, люди часто забывают, что множественное назначение можетЗаменить жестко заданный индекс,а такжеЗаменить жестко запрограммированные разделения(использовать*
грамматика). Глубокое множественное присваивание и одновременное использование синтаксиса кортежа и синтаксиса списка также часто упускается из виду.
Распознавание и запоминание всех случаев использования множественного назначения может оказаться сложной задачей. Не стесняйтесь использовать эту статью в качестве справочного руководства по использованию множественного назначения.
Если вы обнаружите ошибки в переводе или в других областях, требующих доработки, добро пожаловать наПрограмма перевода самородковВы также можете получить соответствующие бонусные баллы за доработку перевода и PR. начало статьиПостоянная ссылка на эту статьюЭто ссылка MarkDown этой статьи на GitHub.
Программа перевода самородковэто сообщество, которое переводит высококачественные технические статьи из Интернета сНаггетсДелитесь статьями на английском языке на . Охват контентаAndroid,iOS,внешний интерфейс,задняя часть,блокчейн,продукт,дизайн,искусственный интеллектЕсли вы хотите видеть более качественные переводы, пожалуйста, продолжайте обращать вниманиеПрограмма перевода самородков,официальный Вейбо,Знай колонку.