Рубрики
Без рубрики

7 способов реализации DTOS в Python и что иметь в виду

Объекты передачи данных являются просто структурами данных, обычно используемые для передачи данных между слоем приложений … Помечено Python, 5days5blogPosts, программирование, обучение.

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

Самая простая форма DTO в Python может быть просто Словарь :

itemdto = {
    "name": "Potion",
    "location": Location("Various"),
    "description": "Recover 20 HP",
}

Вопросы (среди прочего): Мультипликация, отсутствие печатания, отсутствие специфичности.

Мы будем использовать здесь Место расположения Класс, который может быть модельным классом, управляемым ORM в реальном сценарии:

class Location:
    def __init__(self, name: str) -> None:
        self.name = name

    def __repr__(self) -> str:
        return f"Location(name={self.name})"

    def __eq__(self, other: Location) -> bool:
        return self.name == other.name

Если мы хотим точнее настроить атрибуты DTO, мы можем определить отдельный класс :

class ItemDto:
    def __init__(self, name: str, location: Location, description: str = "") -> None:
        self.name = name
        self.location = location
        self.description = description

В качестве альтернативы, мы можем использовать kwargs и а .получить () Способ для дополнительных параметров:

class ItemDto:
    def __init__(self, **kwargs) -> None:
        self.name = kwargs["name"]
        self.location = kwargs["location"]
        self.description = kwargs.get("description", "")

itemdto = ItemDto(
    name="Super Potion",
    location=Location("Various"),
    description="Recover 70 HP"
)

Хорошо определенные DTOS могут дать нам больше преимуществ, таких как облегчение выполнения сериализации или проверки. Вот несколько примеров использования различных особенностей библиотеки Python Standard и 3-х пакетов для создания лучших DTO.

STDLIB Solutions

Dataclasses.

  • добавлен в Python 3.7 (а затем приндоннул в Python 3.6)

  • создан с помощью @dataclass декоратор

  • По умолчанию добавьте автоматически сгенерированные методы дублирования __init__ , __repr__ и __eq__

  • __init__ Метод принимает все поля в качестве параметров метода и устанавливает их значения для атрибутов экземпляра с тем же именами:

from dataclasses import dataclass

@dataclass
class ItemDto:
    name: str
    location: Location
    description: str = ""


# support both positional and keyword args
itemdto = ItemDto(
    name="Old Rod",
    location=Location("Vermillion City"),
    description="Fish for low-level Pokemon",
)
  • сгенерировано __repr__ Метод Возвращает строку, содержащую имя класса, имена поля и Представление поля
>>> print(itemdto)
ItemDto(name='Old Rod', location=Location(name=Vermillion City), description='Fish for low-level Pokemon')
  • сгенерировано __eq__ Метод сравнивает кортежи класса, содержащие значения поля текущего и другого экземпляра.
itemdto2 = ItemDto(
    name="Old Rod",
    location=Location("Vermillion City"),
    description="Fish for low-level Pokemon",
)

>>> itemdto == itemdto2
True
  • __eq__ Метод работает так же, как если бы мы четко заявляем это так:
def __eq__(self, other):
    if other.__class__ is self.__class__:
        return (self.name, self.location, self.description) == (other.name, other.location, other.description)
    return NotImplemented
  • Это может быть хорошая идея, чтобы сделать неизведанные экземпляры DTO. Возможно, установив аргумент замороженные к Истинный :
@dataclass(frozen=True)
class ItemDto:
    name: str
    location: Location
    description: str = ""
  • Не повторяется:
...: for field in itemdto:
...:     print(field)
...: 
TypeError: 'ItemDto' object is not iterable

Больше на Dataclasses:

NamedTupes.

  • NamedTuple подкласс обычного кортежа
  • Введен в Python 3.0 в качестве фабричного метода в модуле коллекций:
from collections import namedtuple

ItemDto = namedtuple("ItemDto", ["name", "location", "description"])
  • Добавлено в Python 3.5 в качестве напечатанной версии в типизированном модуле и позже расширена с вариабельной синтаксисом аннотаций в Python 3.6:
from typing import NamedTuple

class ItemDto(NamedTuple):
    name: str
    location: Location
    description: str = ""


# support both positional and keyword args
itemdto = ItemDto(
    "X Speed", "Temporarily raise Speed in battle", Location("Celadon Dept. Store")
)

>>> print(itemdto)
ItemDto(name='X Speed', location='Temporarily raise Speed in battle', description='Celadon Dept. Store')
  • неизменный
  • __repr__ и __eq__ обрабатывается
  • потенциал
...: for field in itemdto:
...:     print(field)
...: 
X Speed
Temporarily raise Speed in battle
Location(name=Celadon Dept. Store)
  • Поддержите значения по умолчанию, хотя они должны быть определены после любых полей без значения по умолчанию.

Больше на NamedTupes:

Напечатает

  • Доступно, поскольку Python 3.8:
from typing import TypedDict

class ItemDto(TypedDict):
    name: str
    location: Location
    description: str


itemdto = ItemDto(
    name="Escape Rope,",
    location=Location("Various"),
    description="Teleport to last visited Pokemon Center",
)

>>> print(itemdto)
{'name': 'Escape Rope,', 'location': Location(name=Various), 'description': 'Teleport to last visited Pokemon Center'}
  • музей
  • __repr__ и __eq__ обрабатывается
  • ИТИТЕРАЛ в обдумывать вид пути
  • Не поддерживайте значения по умолчанию
  • может обеспечить набрав для существующих словарей
  • Поскольку это все еще словари, в конце концов, они могут быть непосредственно сериализованы для структур данных JSON (Хотя в этом примере мы должны предоставить пользовательский кодировщик для location класс).

Больше на Typeddicts:

3-й пакетные пакеты

привлекать

  • Зависимость от Ptyest , так что есть шанс у вас уже есть в вашем проекте
  • Подобно Dataclasses, на самом деле, библиотека ATTRS была основой для разработки данных о Dataclasses:
import attr

@attr.s(frozen=True)
class ItemDto:
    name: str = attr.ib()
    location: Location = attr.ib()
    description: str = attr.ib(default="")

# also, the dataclasses syntax!
@attr.dataclass(frozen=True)
class ItemDto:
    name: str
    location: Location
    description: str = ""
  • ATTrs предоставляют некоторые дополнительные функциональные возможности на вершине тех, которые предлагают Dataclasses, такие как проверка времени выполнения и оптимизация памяти (щелевые классы).

Вот пример проверки времени выполнения:

@attr.s(frozen=True)
class PokemonDto:
    name: str = attr.ib()
    type: str = attr.ib(
        validator=attr.validators.in_(
            [
                "Fire",
                "Water",
                "Electric",
                "Poison",  # ...
            ]
        )
    )

>>> PokemonDto("Charmander", "Fire")
PokemonDto(name='Charmander', type='Fire')

>>> PokemonDto("Charmander", "Gyarados")
ValueError: 'type' must be in ['Fire', 'Water', 'Electric', 'Poison'] (got 'Gyarados')

Будьте выбирать attrs или dataclasses – это в конечном итоге зависит от вашего конкретного случая использования, и если вы можете использовать пакеты 3-го вечеринок в вашем проекте.

Больше на привлечении:

Pydantic

  • Fastapi использует pydantic для определения схемы и проверки данных

  • Пядинтинтские применения типа подсказки во время выполнения

  • Рекомендуемый способ создания пидантических моделей – подкласс Пидантик. Базомодель Поэтому все модели наследуют некоторые методы:

from pydantic import BaseModel

class PokemonDto(BaseModel):
    name: str
    type: str

    class Config:
        allow_mutation = False


# enforced keyword arguments in case of BaseModel subclass
pokemondto = PokemonDto(name="Charizard", type="Fire")
  • Как атрибуты, Pydantic также поддерживает ваниль Python Dataclasses:
import pydantic

@pydantic.dataclasses.dataclass(frozen=True)
class PokemonDto:
    name: str
    type: str

# in this case positional args are allowed
PokemonDto("Charizard", "Fire")
  • Включает (рекурсивные) проверки данных:
from enum import Enum

from pydantic import BaseModel

class TypeEnum(str, Enum):
    fire = "Fire"
    water = "Water"
    electric = "Electric"
    poison = "Poison"
    # ...


class PokemonDto(pydantic.BaseModel):
    name: str
    type: TypeEnum

    class Config:
        allow_mutation = False


>>> PokemonDto(name="Charizard", type="Fire")
PokemonDto(name='Charizard', type=)

>>> PokemonDto(name="Charizard", type="Charmeleon")
ValidationError: 1 validation error for PokemonDto
type
  value is not a valid enumeration member; permitted: 'Fire', 'Water', 'Electric', 'Poison' (type=type_error.enum; enum_values=[, , , ])
  • Включает JSON (DE) сериализацию:
>>> PokemonDto(name="Charizard", type="Fire").json()
'{"name": "Charizard", "type": "Fire"}'

Больше на Pydantic:

Резюме

Ваш выбор о том, как реализовать DTOS зависят от нескольких обстоятельств – необходим ли вам, среди прочего:

  • неподумность
  • Поддержка значений по умолчанию
  • потери правности
  • сериализация
  • Проверка типа выполнения
  • Оптимизация производительности
  • Другое, более продвинутая конфигуративность.

Пост изначально вдохновлен Это Reddit Thread.

Оригинал: “https://dev.to/izabelakowal/some-ideas-on-how-to-implement-dtos-in-python-be3”