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

Моя цель - предложение широкого ассортимента товаров и услуг на постоянно высоком качестве обслуживания по самым выгодным ценам.

Улучшите качество кода, украсив его оператором match и срезами объектов.

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

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

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

Оператор match

Оператором match (оператором сопоставления с шаблонами) можно пользоваться начиная с Python 3.10. Это — механизм, который позволяет проверять выполнение условий и предпринимать некие действия при выполнении того или иного условия. Если вы пришли в Python из другого языка, вроде C или JavaScript — вы, возможно, уже знакомы с идеей, лежащей в основе оператора switch.

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

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

name = "Yen"

if name == "Yen":
    print("This is your account.")
elif name == "Ben":
    print("This is your sister's account.")
else:
    print("Fraud attempt.")

Если переписать это с использованием match, то получится следующее:

match name:
    case "Yen":
        print("This is your account.")
    case "Ben":
        print("This is your sister's account.")
    case _:
        print("Fraud attempt.")

Разберём этот код:

  • Первая строка и в первом, и во втором варианте будет одной и той же. Это — определение переменной name.

  • В начале оператора match используется ключевое слово match.

  • Затем, при проверке отдельных условий, мы, вместо того, чтобы явным образом что‑то с чем‑то сравнивать, используем оператор case, что позволяет сравнить переменную с одним из возможных значений. В результате конструкцию вида case "Yen" можно воспринимать как проверку того, что содержимое переменной name, которую мы исследуем, равняется "Yen".

  • И наконец, в последнем блоке case, используется символ универсального сопоставления. Он выглядит как знак подчёркивания (_) и играет роль ветви else условного оператора.

Теперь у вас может появиться вопрос о том, зачем может понадобиться использовать match вместо традиционного условного оператора. У меня поначалу возникал такой же вопрос. Меня даже раздражал код, в котором вместо стандартной конструкции if‑else использовался оператор match. Но дело тут в том, что у match есть определённые преимущества перед условным оператором.

Первое преимущество заключается в том, что match — это просто более аккуратная конструкция, позволяющая достичь той же цели, что и if‑else. «Аккуратность кода» как преимущество новой конструкции может показаться чем‑то несерьёзным, но это, на самом деле, очень важно. Весь Python пронизан стремлением к возможности написания чистого, лаконичного кода (если вы мне не верите — введите в своём Python-интерпретаторе import this и нажмите Enter).

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

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

  • Можно автоматически проверять типы объектов (устраняется необходимость в ручной проверке типов).

  • Можно автоматически обращаться к атрибутам объекта в блоках case.

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

class Honda:
    # Ниже вы найдёте пояснения по поводу __match_args__
    __match_args__ = ("year", "model", "cost")
    def __init__(self, year, model, cost):
        self.year = year
        self.model = model
        self.cost = cost

class Subaru:
    __match_args__ = ("year", "model", "cost")
    def __init__(self, year, model, cost):
        self.year = year
        self.model = model
        self.cost = cost
        
car = Subaru(2021, "Outback", 18000)

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

if isinstance(car, Honda):
    print("Honda " + car.model)
elif isinstance(car, Subaru):
    print("Subaru " + car.model)
else:
    print("Failure :(")

В ходе выполнения этого кода при работе с переменной car будет выведено "Subaru Outback". Если переписать пример с использованием оператора match, то получится следующий код, который оказывается проще, чем предыдущий вариант программы:

match car:
    case Honda(year, model, cost):
        print("Honda " + model)
    case Subaru(year, model, cost):
        print("Subaru " + model)
    case _:
        print("Failure")

Возможности оператора match по сопоставлению шаблонов позволяют Python автоматически проверять типы объектов в блоках case, а так же позволяют напрямую обращаться к атрибутам. Обратите внимание на то, что это возможно благодаря тому, что в определение класса включена конструкция __match_args__. Здесь перечислены позиционные Python‑аргументы. В документации по Python рекомендуется, чтобы эта конструкция имитировала бы ту, что применяется в конструкторе __init__ при назначении атрибутов self.

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

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

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

Срезы строк и списков

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

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

>>> my_str = "hello"
>>> my_str[1:3]
'el'

Синтаксические правила описания срезов требуют использования квадратных скобок, содержащих начальный и конечный индексы, разделённые двоеточием. Помните о том, что в Python индексация начинается с нуля, поэтому индекс 1 в нашем примере соответствует букве 'e'. Кроме того, обратите внимание на то, что при формировании среза элемент, на который указывает индекс, находящийся справа, не включается в результат работы этого механизма. Поэтому система доходит до элемента 3, но не включает его в вывод. В результате мы и получаем 'el', а не 'ell'.

Если нужно начать формирование среза с начала строки или списка, или пройти весь объект до конца — соответствующий индекс можно оставить пустым:

>>> my_lst[:3]
['apple', 'orange', 'blackcurrant']
>>> my_lst[2:]
['blackcurrant', 'mango', 'pineapple']

А если оставить пустыми оба индекса — система выдаст копию всего объекта:

>>> my_str[:]
'hello'
>>> my_lst[:]
['apple', 'orange', 'blackcurrant', 'mango', 'pineapple']

Обратите внимание на то, что и при работе со списками, и при работе со строками их срезы — это совершенно новые объекты, отличающиеся от исходных объектов:

>>> new_lst = my_lst[2:]
>>> new_lst
['blackcurrant', 'mango', 'pineapple']
>>> my_lst
['apple', 'orange', 'blackcurrant', 'mango', 'pineapple']

А теперь — самое интересное. Используя механизм формирования срезов можно применять и отрицательные индексы. Если вы с таким приёмом не знакомы — знайте, что он позволяет начинать отсчёт элементов не от начала, а от конца списка или строки. Последняя буква строки имеет индекс -1, предпоследняя — -2 и так далее.

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

>>> my_str[:-1]
'hell'

И наконец — одна из самых недооценённых возможностей формирования срезов. Она заключается в том, что в квадратных скобках можно указывать не два, а три числа. Третье число описывает нечто вроде «шага», совершаемого при выдаче среза. Это легче всего объяснить на примере:

>>> my_long_lst = ['apple', 'orange', 'blackcurrant', 'mango', 'pineapple', 'grapes', 'kiwi', 'papaya', 'coconut']
>>> my_long_lst[1:-1:2]
['orange', 'mango', 'grapes', 'papaya']

Разберём то, что здесь происходит:

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

  • В выражении формирования среза первые два числа — это 1 и -1. Как мы уже видели — такая конструкция описывает первый и последний элементы обрабатываемого объекта, которым в данном случае является список my_long_list.

  • И наконец — после дополнительного двоеточия, в конце, мы записываем число 2. Всё это сообщает Python о том, что мы хотим взять элементы списка, начиная с первого индекса и заканчивая последним, но при этом нам нужен лишь каждый второй элемент. Если тут воспользоваться числом 3 — будет взят каждый третий элемент, если написать 4 — будет выдан каждый четвёртый элемент и так далее.

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

>>> my_long_lst[-1:1:-2]
['coconut', 'kiwi', 'pineapple', 'blackcurrant']

# Для того чтобы успешно осуществить обход списка в обратном порядке, значение "шага" должно быть отрицательным
# В противном случае получится пустой список
>>> my_long_lst[-1:1:2]
[]

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

>>> my_lst
['apple', 'orange', 'blackcurrant', 'mango', 'pineapple']
>>> my_lst[::-1]
['pineapple', 'mango', 'blackcurrant', 'orange', 'apple']

Поняли — что тут происходит? В качестве упражнения вы можете, вспомнив всё то, что узнали о формировании срезов, самостоятельно проанализировать этот код. Вот вам подсказка: взгляните на описание того, что происходит в том случае, если индексы первого и последнего элемента оставляют пустыми.

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

Какая от всего этого польза дата-сайентисту?

Если говорить о программировании в целом, то надо отметить, что при написании Python‑кода важно учитывать его читабельность и чистоту. Использование вышеописанных возможностей во многом этому способствует. Как мы уже говорили, оператор match, в плане читабельности и чистоты кода, имеет несколько важных преимуществ перед условными конструкциями. А если коснуться срезов списков, то их применение позволяет создавать гораздо более чистый код, чем тот, что получился бы при попытке достичь того же эффекта, применяя какие‑нибудь сложные циклы.

А теперь отойдём от этих соображений, справедливых для любого Python‑программиста, и поговорим о том, что всё это значит для дата‑сайентистов.

С практической точки зрения, если вы работаете дата‑сайентистом, то весьма высока вероятность того, что вы получали формальное образование в сфере, отличной от информатики. Это, скорее всего, была сфера статистики, математики, или даже сфера data science, науки о данных (если вам очень повезло найти соответствующую учебную программу). В ходе освоения подобных учебных программ информационные технологии обычно изучают лишь как инструменты для достижения неких целей.

Основное внимание уделяется изучению базовых принципов программирования, освоению того, что достаточно для обработки данных, выполнения их анализа, создания моделей разных масштабов. В результате не так много времени остаётся для изучения тем наподобие «полезные синтаксические конструкции Python». Да что там говорить — такие темы часто отсутствуют даже там, где учат программистов.

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

Пусть код всегда пребывает на вашей стороне. До новых встреч, друзья.

О, а приходите к нам работать?
Источник: https://habr.com/ru/companies/wunderfund/articles/775052/


Интересные статьи

Интересные статьи

Быстрый тур по новым, готовым к работе функциям при обновлении с Java 11 до Java 17.Через три года после Java 11 - на данный момент последней версии с долгосрочной поддержкой (LTS), Java 17 LTS будет ...
В 2021 версиях управлять данными стало еще проще, а сэкономленное при этом время инженеры могут посвятить повышению качества продукции, которую они разрабатывают. В семей...
С момента выхода стандарта ECMAScript 2015 (его ещё называют ES6) JavaScript серьёзно изменился и улучшился. Это очень хорошая новость для всех JS-разработчиков. Более того, теперь новая версия E...
После недавнего выпуска 3CX v16 мы уже подготовили первое обновление 3CX V16 Update 1 Beta. В нем реализованы новые возможности корпоративного чата и обновленный сервис Call Flow Service, который...
Всем привет! В данной статье, описаны шаги которые необходимо выполнить, для добавления к вашему WDS, возможности загрузки в режиме UEFI. Т.е. инструкция в данной статье, предполагает, что у ва...