Русский
Русский
English
Статистика
Реклама

Перевод 12 примеров улучшения кода с помощью dataclass

В рамках курса Python Developer. Basic подготовили для вас перевод полезного материала.

Также приглашаем всех желающих на
открытый вебинар по теме Три кита: map(), filter() и zip(). Можно ли писать код, требующий циклов, но без циклов? Можно. Может ли он быть быстрее, чем, если бы мы использовали циклы в Python? Может. Для реализации задуманного понадобится знание слов "callback", "iterator" и "lambda". Будет сложно, но интересно. Присоединяйтесь.


Мы добавляем алгоритмы кластеризации с помощью пакетов scikit-learn, Keras и других в пакет Photonai. На 12 примерах мы покажем, как @dataclass улучшает код на Python. Для этого мы используем код из пакета Photonai для Machine Learning.

Обновитесь до Python 3.7 или более поздней версии

Декоратор @dataclass был добавлен в Python 3.7. Можно использовать Python 3.7 из Docker-образа, добавив в файл следующие команды /.bashrc_profile или /bashrc.txt.

devdir='<path-to-projects>/photon/photonai/dockerSeasons/dev/'testdir='<path-to-projects>/photon/photonai/dockerSeasons/test/'echo $devdirecho $testdirexport testdirexport devdir#alias updev="(cd $devdir; docker-compose up) &"alias downdev="(cd $devdir; docker-compose down) &"alias builddev="(cd $devdir; docker-compose build) &"#alias uptest="(cd $testdir; docker-compose up) & "alias downtest="(cd $testdir; docker-compose down) &"alias buildtest="cd $testdir; docker-compose build) &"

Если вы не можете найти файл /bashrc.txt создайте его самостоятельно с помощью touch/bashrc.txt. (в случае MacOS или одной из разновидностей операционных систем Linux или Unix.)

Примечание: Не забудьте указать в качестве исходника /.bashrc_profile или /bashrc.txt, когда закончите их редактировать.

Здесь вы найдете более подробную информацию о реализации Docker, которой я пользуюсь.

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

Добавьте подсказки типов

Python язык с динамической типизацией. В версиях Python от 3.5 есть подсказки типов (PEP 484). Я подчеркиваю, что именно подсказки, поскольку они не влияют на работу интерпретатора Python. Насколько вам известно, интерпретатор Python вообще их игнорирует.

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

В Python 3.7 подсказки типов нужны для полей в определении класса при использовании декоратора @dataclass.

Я добавляю подсказки типов во все приведенные примеры @dataclass. Если вы хотите узнать о них больше, рекомендую почитать:

  1. https://medium.com/swlh/future-proof-your-python-code-20ef2b75e9f5

  2. https://realpython.com/python-type-checking/

  3. https://docs.python.org/3/library/typing.html

Декоратор @dataclass уменьшает шаблонность

@dataclass был добавлен в Python 3.7. Основной движущей силой было желание избавиться от шаблонности, связанной с состоянием в определении класса def.

Классы могут существовать без состояния с одними лишь методами, но какой в этом смысл? Классы нужны для инкапсуляции состояния (полей данных) и методов, которые работают с полями данных. Если нет состояния, которое нужно инкапсулировать, можно преобразовать методы в функции.

Примечание: Если вы не используете pandas, можно ускорить выполнение этих функции, с помощью быстрой вставки @jit из пакета numba.

@dataclass декорирует определение класса def и автоматически генерирует 5 методов init(), repr(), str, eq(), и hash().

Примечание: он генерирует и другие методы, но об этом позже.

Обратите внимание, что все эти 5 методов работают непосредственно с инкапсуляцией состояния класса. @dataclass практические полностью убирает повторяющийся шаблонный код, необходимый для определения базового класса.

Пример коротенького класса в photon/photonai/base/hyperpipe.py, декорированный с помощью @dataclass.

### Example #1class Data:    def __init__(self, X=None, y=None, kwargs=None):        self.X = X        self.y = y        self.kwargs = kwargs

Пример 1, после декорации =>

from dataclasses import dataclassfrom typing import Dictimport numpy as np@dataclassclass Data:    X: np.ndarray = None  # The field declaration: X    y: np.array = None    # The field declaration: y    kwargs: Dict = None   # The field declaration: kwargs

Примечание: Если тип не является частью объявления, то поле игнорируется. Используйте тип any для подстановки типа, если он меняется или во время выполнения неизвестен.

Сгенерировался ли код eq()?

### Example #2data1 = Data()data2 = Data()data1 == data1

Пример 2, вывод =>

True

Да! А что насчет методов repr() и str?

### Example #3print(data1)data1

Пример , вывод =>

Data(X=None, y=None, kwargs=None)Data(X=None, y=None, kwargs=None)

Да! А методы hash() и init?

Example #4@dataclass(unsafe_hash=True)class Data:    X: np.ndarray = None    y: np.array = None    kwargs: Dict = None        data3 = Data(1,2,3){data3:1}

Пример 4, вывод =>

{Data(X=1, y=2, kwargs=3): 1}

Да!

Примечание: У сгенерированного метода init все еще сигнатура (X, y, kwargs). Кроме того, обратите внимание, что подсказки типов были проигнорированы интерпретатором Python 3.7.

Примечание: У init(), repr(), strи eq() значение ключевого слова по умолчанию True, тогда как у hash() по умолчанию False.

Вы можете использовать inspect также, как и для любого другого экземпляра.

### Example #5from inspect import signatureprint(signature(data3.__init__))

Пример 5, вывод =>

(X: numpy.ndarray = None, y: <built-in function array> = None, kwargs: Dict = None) -> None

Круто!

Более длинный пример из photon/photonai/base/hyperpipe.py

### Example #6class CrossValidation:    def __init__(self, inner_cv, outer_cv,                 eval_final_performance, test_size,                 calculate_metrics_per_fold,                 calculate_metrics_across_folds):        self.inner_cv = inner_cv        self.outer_cv = outer_cv        self.eval_final_performance = eval_final_performance        self.test_size = test_size        self.calculate_metrics_per_fold = calculate_metrics_per_fold        self.calculate_metrics_across_folds =            calculate_metrics_across_folds        self.outer_folds = None        self.inner_folds = dict()Example #6 Output=>

Пример 6, после декорации =>

from dataclasses import dataclass@dataclassclass CrossValidation:    inner_cv: int    outer_cv: int    eval_final_performance: bool = True    test_size: float = 0.2    calculate_metrics_per_fold: bool = True    calculate_metrics_across_folds: bool = FalseNote:(Example #6) As any signature, keyword arguments fields with default values must be declared last.Note:(Example #6)  class CrossValidation: Readability has increased substantially by using @dataclass and type hinting.
### Example #7cv1 = CrossValidation()

Пример 7, вывод =>

TypeError: __init__() missing 2 required positional arguments: 'inner_cv' and 'outer_cv'Note:(Example #7) inner_cv and outer_cv are positional arguments. With any signature, you declare a non-default field after a default one. (Hint: If this were allowed, inheritance from a parent class breaks.)((Why? Goggle interview question #666.))
### Example #8cv1 = CrossValidation(1,2)cv2 = CrossValidation(1,2)cv3 = CrossValidation(3,2,test_size=0.5)print(cv1)cv3

Пример 8, вывод =>

CrossValidation(inner_cv=1, outer_cv=2, eval_final_performance=True, test_size=0.2, calculate_metrics_per_fold=True, calculate_metrics_across_folds=False)CrossValidation(inner_cv=3, outer_cv=2, eval_final_performance=True, test_size=0.5, calculate_metrics_per_fold=True, calculate_metrics_across_folds=False)
### Example #9cv1 == cv2

Пример 9, вывод =>

True
### Example #10cv1 == cv3

Пример 10, вывод =>

False
### Example #11from inspect import signatureprint(signature(cv3.__init__))cv3

Пример 11, вывод =>

(inner_cv: int, outer_cv: int, eval_final_performance: bool = True, test_size: float = 0.2, calculate_metrics_per_fold: bool = True, calculate_metrics_across_folds: bool = False) -> NoneCrossValidation(inner_cv=3, outer_cv=2, eval_final_performance=True, test_size=0.5, calculate_metrics_per_fold=True, calculate_metrics_across_folds=False)Note: (Example #11) The inspect function shows the signature of the class object while the__str__ default shows the instance state variables and their values.

Очень круто!

Упс, а что насчет:

self.outer_folds = Noneself.inner_folds = dict()

У нас есть переменные состояния, но они не создаются при вызове. Не волнуйтесь, @dataclass справится и с этим. Покажу в следующем разделе.

Обработка после инициализации

Существует такой метод, как post-init, который является частью определения @dataclass. Метод post_init выполняется после init, сгенерированного @dataclass. Он включает обработку после установки состояния сигнатуры.

Мы завершаем преобразование установив оставшееся состояние CrossValidation:

### Example 12from dataclasses import dataclass@dataclassclass CrossValidation:    inner_cv: int    outer_cv: int    eval_final_performance: bool = True    test_size: float = 0.2    calculate_metrics_per_fold: bool = True    calculate_metrics_across_folds: bool = False    def __post_init__(self):        self.outer_folds = None        self.inner_folds = dict()

Источники

Здесь вы найдете отличные варианты использования декоратора @dataclass:

  1. https://realpython.com/python-data-classes/

  2. https://blog.usejournal.com/new-buzzword-in-python-is-here-dataclasses-843dd1d372a5

Заключение

На 12 примерах до и после я показал, как @dataclass преобразует классы в пакете Photonai Machine Learning. Мы видели, как @dataclass повысил производительность и читаемость кода.

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

Добавление @dataclass и подсказок типов демонстрирует, что Python продолжает расти и развиваться.

Примечание: Вы можете добавить обновленный код Photonai в свой проект из клонируемого репозитория на GitHub.

Я показал далеко не все возможности @dataclass. Поскольку мы только добавляем кластеризацию, я продолжу документировать изменения в photonai.


Узнать подробнее о курсе Python Developer. Basic.

Смотреть открытый вебинар по теме Три кита: map(), filter() и zip().

Источник: habr.com
К списку статей
Опубликовано: 10.02.2021 18:20:17
0

Сейчас читают

Комментариев (0)
Имя
Электронная почта

Блог компании otus

Python

Программирование

Машинное обучение

Callback

Iterator

Lambda

Machinelearning

Data science

Readability

Категории

Последние комментарии

  • Имя: Макс
    24.08.2022 | 11:28
    Я разраб в IT компании, работаю на арбитражную команду. Мы работаем с приламы и сайтами, при работе замечаются постоянные баны и лаги. Пацаны посоветовали сервис по анализу исходного кода,https://app Подробнее..
  • Имя: 9055410337
    20.08.2022 | 17:41
    поможем пишите в телеграм Подробнее..
  • Имя: sabbat
    17.08.2022 | 20:42
    Охренеть.. это просто шикарная статья, феноменально круто. Большое спасибо за разбор! Надеюсь как-нибудь с тобой связаться для обсуждений чего-либо) Подробнее..
  • Имя: Мария
    09.08.2022 | 14:44
    Добрый день. Если обладаете такой информацией, то подскажите, пожалуйста, где можно найти много-много материала по Yggdrasil и его уязвимостях для написания диплома? Благодарю. Подробнее..
© 2006-2024, personeltest.ru