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

Dunderless Python = Python - методы Dunder

Мы все любим Python

Python - один из языков программирования, которые я использую чаще всего. Я начал использовать его в основном для CTF (для фантастического фреймворка pwntools) и в конечном итоге использовал его, когда это было возможно.

Все мы любим Python, но даже в Python есть некоторые подводные камни.

Двойное подчеркивание следует произносить как «dunder». Итак, __init__ - это «dunder init dunder» или просто «dunder init». - Нед Батчелдер

Я вижу основные проблемы с Python:

  • При ООП разделение логики на несколько подклассов очень утомительно, так как вам нужно создавать классы и определять dunder-методы для каждого из них. Обычно это отталкивало меня от применения SRP для экономии времени.
  • Если у вас есть сложные данные, которые необходимо выразить, сделайте ошибку, используя языковые структуры данных, и в итоге вы получите код, который выглядит примерно так data["results_tweets"].get("hits", [])[3][USER_ID]. Даже если вы определяете собственные классы, вы будете в любом случае придется писать много шаблонного кода и логики инициализации.
  • Использовать такие структуры данных, как List и Dict, очень просто, поэтому мы в конечном итоге используем их повсюду. Поэтому вашей команде очень сложно выяснить, какие значения находятся внутри (особенно если вы не используете подсказки типа).

Как оказалось, решение всех этих проблем сводится к улучшению простой задачи: Создание класса или контейнера данных простым и понятным способом, что означает отсутствие шаблонов или усилий для записи или чтения. протоколы объектов (__init__ = под init под под).

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

Сделаем ваш Python идеальным

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

Пример использования

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

Company X sells business laptops. It sells to companies like Company P. Employees of company P can buy the laptops with a 50% reduction. Company X needs to access personal information about company P employees E (like their credit cards CC information).

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

Списки, кортежи и словари

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

Давайте попробуем смоделировать наш вариант использования только с базовыми структурами данных:

Попробуем распечатать контакты кредитной карты для john doe:

Также попробуем собрать пины кредитной карты как List:

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

  • Читаемость: [credit_card[2] for credit_card in john_doe[0].get('credit_cards')] - это не мое определение читаемого кода, даже если он очень читаемый язык, такой как Python. Эти методы доступа не масштабируются, и их становится все труднее и труднее читать, если ваша модель расширяется.
  • Расширение: поскольку модель кредитной карты реализована с использованием кортежа. Мы используем индекс для доступа к значениям (например, credit_card[2]). Предположим, вы изменили кортеж, добавив или удалив поля. Вам нужно будет везде изменить индекс доступа к нему, и то же правило применяется, если вы используете распаковку.
  • Побочные эффекты. В этом примере мы использовали словари и списки для моделирования более высоких понятий, таких как компания и сотрудник и их отношения. Помните, что списки и словари изменяемы. По своему замыслу они описывают значения, которые можно добавлять и удалять, а не фиксированные. Существуют более подходящие альтернативы моделированию фиксированных полей 👊.

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

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

NamedTuples

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

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

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

Кажется, идеальное решение, правда? К сожалению, это не так. За практичность именованных кортежей приходится платить:

  • Расширение: расширение namedtuples осуществляется путем создания подклассов. Вы вынуждены использовать наследование, которое не является наиболее согласованной моделью модульности. Даже если вы выберете его, вы получите класс с двумя Employee в его __mro__. Это может стать проблемой, если вы собираетесь использовать sphinx для автоматического создания документов для ваших классов.
▶ (python) Employee.__mro__
(<class ‘__main__.Employee’>, <class ‘__main__.Employee’>, <class ‘tuple’>, <class ‘object’>)
  • Побочные эффекты: он всегда неизменяем (внутренне это просто кортеж). Вы не можете изменить это, делая некоторые варианты использования (например, банковские транзакции) невозможными для правильной реализации с помощью этого решения.
  • Другие соображения: поля принудительно доступны как нумерованные индексы, у вас не может быть частных атрибутов, потому что они открываются через общедоступный интерфейс __getitem__, и он сравнивается с необработанным кортежем тех же значений, поэтому вы могут возникнуть проблемы с причудливым типом.

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

Классы данных

Новый декоратор dataclass() позволяет объявлять классы данных. Класс данных описывает свои атрибуты с помощью аннотаций переменных класса. Его конструктор и другие магические методы, такие как __repr__(), __eq__() и __hash__(), генерируются автоматически.

Их цель - найти более простой способ написания классов в стандартной библиотеке, не несущий проблем namedtuples. Классы данных предотвращают злоупотребление namedtuple и позволяют сделать код Python намного чище.

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

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

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



Attrs

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

Вот как будет выглядеть наш пример кода с attrs:

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

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

  • Вам необходимо поддерживать широкий спектр версий Python.
  • Требуются расширенные функции для таких значений, как валидаторы и преобразователи.
  • Хотите объединить attrs с cattrs, чтобы эффективно структурировать и неструктурировать данные.

Вы можете посмотреть attrs by Example, чтобы получить краткое руководство по использованию различных функций библиотеки. Документация attrs также довольно полная.

Заключение

Запустите новую оболочку Python и оцените:

>>> import this

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

Ссылки