Attr - одна из лучших практик объектно-ориентированного Python


Известно, что Python очень гибкий язык, который может использоваться в функциональном, процедурном и объектно-ориентированном программировании. Честно говоря, я пишу на нем классы только при необходимости, просто потому что не хочу переоценивать проблемы. Например, не имеет большого смысла использовать ООП, когда Python применяется для выполнения специального анализа данных.

Однако все изменилось, когда я узнал про библиотеку “Attrs”. Она упрощает программирование на Python в объектно-ориентированном режиме еще сильнее (думаю, что ООП в Python и так очень лаконичен и легок). В этой статье я расскажу о том, как эта библиотека может облегчить ООП в стиле Python.attrsRelease v19.3.0 (). is the Python package that will bring back the joy of writing classes by relieving you from the…www.attrs.org

Простой старт

Фото Danielle MacInnes на Unsplash

Как и большинство библиотек Python, мы можем просто установить attrs с помощью pip.

pip install attrs

Теперь напишем код класса на Python без каких-либо библиотек.

class Person(object): def __init__(self, name, age): self.name = name self.age = age p1 = Person('Chris', 32) p2 = Person('Chris', 32)

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

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

Что же attrs может сделать для нас? Давайте выполним то же самое здесь.

from attr import attrs, attrib @attrs class Person(object): name = attrib() age = attrib() p1 = Person('Chris', 32) p2 = Person('Chris', 32)

Предполагаю, что нужно дать простое объяснение. Во-первых, @attrs — это аннотация, которая сообщает, что этот класс будет объявлен с помощью библиотеки attrs. Затем каждый атрибут нужно определить как attrib(), потому что нет необходимости использовать метод __init__(). После этого мы можем просто создать экземпляр класса точно так же, как если бы существовал метод __init__.

Давайте также попробуем вывод и тестирование на равенство.

Очевидно, что у нас есть значимый вывод объекта, а также мы можем проверить равенство между объектами.

Атрибуты без инициализации

Фото NordWood Themes на Unsplash

Теперь у вас может возникнуть вопрос. Что делать, если существует такой атрибут, который мы не хотим инициализировать во время создания экземпляра, но, вероятно, присвоим ему значение позже? Конечно, если вы все еще используете attrib(), как указано ниже, он не будет работать.

С помощью attrs можно достичь этого, передав аргумент init, используя False.

@attrs class Person(object): name = attrib() age = attrib() skills = attrib(init=False) p1 = Person('Chris', 32)

Как было показано выше, изначально нам не нужно присваивать значение skills, но можем сделать это позже.

Значения по умолчанию

Фото Келли Сиккема на Unsplash

Вы также можете спросить, как насчет того, чтобы дать атрибутам значения по умолчанию? Да, с attrs сделать это легко.

@attrs class Person(object): name = attrib(default='Chris') age = attrib(default=32) p1 = Person() p2 = Person('Chris', 32)

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

Что делать, если мы хотим установить атрибут с пустой коллекцией в качестве значения по умолчанию? Обычно мы не хотим передавать [] в качестве аргумента, это одна из известных ловушек Python, которая может вызвать много неожиданных проблем. Не волнуйтесь, attrs предоставляет нам “фабричный метод”.

@attrs class Person(object): name = attrib(default='Chris') age = attrib(default=32) skills = attrib(factory=list)

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

Проверка атрибутов

Фото Markus Winkler на Unsplash

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

@attrs class Student(object): name = attrib() age = attrib() student_id = attrib() @student_id.validator def check_student_id(self, attribute, value): if len(str(value)) != 6: raise ValueError(f'student_id must be 6 characters! got {len(str(value))}')

В приведенном выше примере мы объявили класс “Student” с атрибутом “student_id”. Предположим, что нужно проверить длину студенческого билета, которая должна быть равна 6 символам.

Обратите внимание, что для этого нам нужно определить функцию. Она должна быть аннотирована как @<attribute_name>.validator. В нашем случае — @student_id.validator. Затем мы можем вызвать исключение в этой функции проверки, как показано выше.

Проведем простой тест.

Первый экземпляр s1 не имеет никаких проблем, потому что его student_id имеет длину 6, но второй экземпляр s2 не пройдет, потому что его длина равна 5, и исключение с заранее определенным сообщением об ошибке отображается правильно.

Подклассы

Фото Clker-Free-Vector-Images на Pixabay

В простом Python мы должны использовать функцию super() в функции __init__() для реализации наследования. Это будет очень сложно, если нужно осуществить мульти-наследование. attrs делает этот процесс чрезвычайно легким.

@attrs class Person(object): name = attrib() age = attrib() def get_name(self): return self.name @attrs class User(object): user_id = attrib() def get_user_id(self): return self.user_id @attrs class Student(Person, User): student_id = attrib() def get_student_id(self): return self.student_id student = Student(name='Chris', age=32, user_id='ctao', student_id=123456)

В приведенном выше примере Student наследует как от Person, так и от User. В классе Student нам нужно только определить его конкретный атрибут student_id, а другие атрибуты как от Person, так и от User будут автоматически унаследованы непосредственно без каких-либо подробных определений.

Как было показано выше, функции также наследуются без проблем.

Сериализация в словарь

Фото libellule789 на Pixabay

Библиотека attrs также помогает нам легко сериализовать экземпляры в словари, которые затем можно использовать в JSON и делать с ними все, что вы захотите.

attr.asdict(student)

Перевод статьи Christopher Tao: Probably the Best Practice of Object-Oriented Python — Attr


Поделиться статьей:


Вернуться к статьям

Комментарии

    Ничего не найдено.