Эта история о повышении качества вашего кода и способах уменьшения количества шаблонов при работе с 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__()
, генерируются автоматически.
Их цель - найти более простой способ написания классов в стандартной библиотеке, не несущий проблем namedtuple
s. Классы данных предотвращают злоупотребление namedtuple
и позволяют сделать код Python намного чище.
Давайте реорганизуем наш код, чтобы использовать классы данных:
Для меня это одно из лучших, если не лучшее решение. Он использует стандартную библиотеку и обеспечивает удобочитаемость и побуждает разработчиков последовательно разделять код.
Я не буду вдаваться в подробности о классах данных, так как думаю, вы уже можете начать их использовать. Medium содержит отличные статьи об их использовании. Вы можете ознакомиться с этой замечательной статьей Халил Йылдырым, в которой есть все, что вам нужно знать, чтобы использовать их правильно.
Attrs
Attrs - это сторонняя библиотека, которая позволила нам делать то, что предлагают классы данных, с более продвинутыми функциями.
Вот как будет выглядеть наш пример кода с attrs:
Обратите внимание, что этот синтаксис доступен только для версий python старше 3.6. Предположим, вы хотите использовать его со старой версией, ваши классы будут выглядеть примерно так:
Attrs по-прежнему является одним из решений, предлагающих больше функций, чем классы данных. Это может быть то, что вам нужно, если:
- Вам необходимо поддерживать широкий спектр версий Python.
- Требуются расширенные функции для таких значений, как валидаторы и преобразователи.
- Хотите объединить attrs с cattrs, чтобы эффективно структурировать и неструктурировать данные.
Вы можете посмотреть attrs by Example, чтобы получить краткое руководство по использованию различных функций библиотеки. Документация attrs также довольно полная.
Заключение
Запустите новую оболочку Python и оцените:
>>> import this
Прочтите 7-ю строчку вслух. Хорошая работа ㊗️ ! путь к тому, чтобы стать мастером программного обеспечения, долог и может занять почти всю жизнь. Я надеюсь, что благодаря этой статье вы станете лучшим разработчиком Python.