Розбираємося, як працює вбудована функція zip у Python, і пишемо свою реалізацію за допомогою list comprehension


Дізнайтесь більше про нові кар'єрні можливості в EchoUA. Цікаві проекти, ринкова оплата, гарний колектив. Надсилайте резюме та приєднуйтеся до нас.

Расказывает Рювен Лернер, викладач


Багато хто чув про функцію zip у Python, а хтось навіть регулярно нею користується. Сьогодні ми (через цікавість і для загального розвитку) опишемо, як можна реалізувати її самому з допомогою list comprehensions.

Спершу поясню, що взагалі робить функція zip, для тих, хто з нею раніше не стикався:

>>> s = 'abc'>>> t =  (10, 20, 30) >>> zip (s, t)[('a', 10)]

Функція бере на вхід декілька списків і створює з них список (у Python 3 створюється не list, а спеціальний zip-об’єкт) кортежів, такий, що перший елемент отриманого списку містить кортеж з перших елементів усіх списків-аргументів. Таким чином, якщо їй передати три списки, то вона відпрацює таким чином:

>>> s = 'abc'>>> t =  (10, 20, 30) >>> u =  (- 5, - 10, - 15) >>> list (zip (s, t, u))[('a', 10, - 5)]

Загалом, функція відпрацює навіть для одного iterable-об’єкта, результатом буде послідовність із кортежів, в кожному з яких буде по одному елементу. Це, мабуть, не найпоширеніший спосіб застосування zip. Я часто використовую zip, наприклад, для створення словників:

>>> names =['Tom', 'Dick', 'Harry']>>> ages =[50, 35, 60]>>> dict (zip (names, ages)) {'Harry': 60, 'Dick': 35, 'Tom': 50}

Це дуже зручно, чи не так? Щоразу, коли я розповідаю про zip на своїх уроках, у мене запитують про те, що буде, якщо у функцію передати масиви різної довжини. Відповідь проста – переможе коротший:

>>> s = 'abc'>>> t =  (10, 20, 30, 40) >>> list (zip (s, t))[('a', 10)]

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

Є одна можливість у Python, яка мені подобається навіть більше, ніж zip. Це спискове включення (англ. list comprehension). Саме тому, коли студент нещодавно запитав мене: “Чи можемо ми реалізувати zip самі за допомогою спискових включень?” – я просто не зміг встояти.

Як же нам цього добитися? Розпочнемо з першого, що спадає на думку:

[(s[i], t[i]) # створюємо кортеж з двох елементів for i in range (len (s))] # для індексів від 0 до len (s) - 1

Загалом все! Це працює, але є декілька моментів, які все ж варто допрацювати в цьому методі.

По-перше, оригінальна функція могла працювати з масивами різної довжини. Тому замість range (len (s)) нам варто використати range (len (x)), де x – найбільш коротка послідовність. Для цього достатньо помістити всі послідовності в один список, відсортувати цей список по довжині елементів і з’ясувати довжину елемента, що виявився під нульовим індексом:

>>> s = 'abcd'>>> t =  (10, 20, 30)
>>> sorted ((s, t)key=len)[(10, 20, 30)abcd']

Поєднуємо це з попереднім кодом:

>>> s = 'abcd'>>> t =  (10, 20, 30) >>> sorted ((s, t)key=len)[(10, 20, 30)abcd']

Це ще не все доопрацювання, а вираз вже виходить занадто довгим. Мабуть, з’ясування найменшої довжини варто винести в окрему функцію (заразом зробимо так, щоб вона обчислювала найкоротшу послідовність з необмеженої кількості аргументів):

>>> def shortest_sequence_range (*args): return range (len (sorted (args, key=len)[0])) >>> [(s[i], t[i]) for i in shortest_sequence_range (s, t) ]

Що залишилося тепер? Як вже говорилося вище, Python 3 створює не список, а спеціальний zip-об’єкт, повертаючи ітератор від нього. Це зроблено для того, щоб код не ламався при обробці винятково довгих послідовностей. Це можна реалізувати, але вже не за допомогою спискового включення (яке завжди повертає список), а за допомогою генератора. На щастя, для цього доситатньо замінити квадратні дужки на круглі:

>>> def shortest_sequence_range (*args): return range (len (sorted (args, key=len)[0])) >>> g =  ((s[i], t[i]) for i in shortest_sequence_range (s, t) ) >>> for item in g: print (item) ('a', 10) ('b', 20) ('c', 30)

Готово! Ми реалізували свій повністю робочий zip. Ви можете потренуватися і самостійно подумати, як ще можна поліпшити цей алгоритм.

Переклад публікації “Implementing ” zip” with list comprehensions”

Київ, Харків, Одеса, Дніпро, Запоріжжя, Кривий Ріг, Вінниця, Херсон, Черкаси, Житомир, Хмельницький, Чернівці, Рівне, Івано-Франківськ, Кременчук, Тернопіль, Луцьк, Ужгород, Кам'янець-Подільський, Стрий - за статистикою саме з цих міст програмісти найбільше переїжджають працювати до Львова. А Ви розглядаєте relocate?


Залишити відповідь

Ваша e-mail адреса не оприлюднюватиметься. Обов’язкові поля позначені *