diff --git a/anb_python_components/extensions/type_extension.py b/anb_python_components/extensions/type_extension.py new file mode 100644 index 0000000..e32f061 --- /dev/null +++ b/anb_python_components/extensions/type_extension.py @@ -0,0 +1,75 @@ +# anb_python_components/extensions/type_extension.py + +import datetime +from typing import Any + + +# Базовая структура для представления расширенного типа +class TypeExtension: + """ + Класс для расширения типов. + """ + + def __init__(self): + """ + Инициализирует экземпляр класса. + """ + pass + + @staticmethod + def to_dict(instance: Any) -> dict[str, Any]: + """ + Преобразует экземпляр объекта в словарь. + + :param instance: Экземпляр класса. + :return: Словарь представлений всех полей. + """ + # Создаём словарь + result = {} + + # Перебираем поля экземпляра + for key, value in vars(instance).items(): + # - если значение является экземпляром datetime, преобразуем его в timestamp + if isinstance(value, datetime.datetime): + result[key] = int(value.timestamp()) + # - если значение является словарем, вызываем рекурсивно функцию to_dict + elif hasattr(value, '__dict__'): + result[key] = TypeExtension.to_dict(value) + # - иначе просто добавляем значение + else: + result[key] = value + + # Возвращаем словарь + return result + + @staticmethod + def from_dict(data: dict, cls=None) -> Any: + """ + Восстанавливает объект из словаря. + + :param data: Словарь, представляющий поля объекта. + :param cls: Класс для восстановления объекта (необязательный параметр, равный None по умолчанию). + :return: Восстановленный объект. + """ + + # Проверяем, что класс указан и является типом + if cls is None or not isinstance(cls, type): + # - если класс не указан, бросаем исключение + raise TypeError('Класс для восстановления не указан.') + + # Создаём объект класса + obj = cls.__new__(cls) + + # Перебираем поля словаря + for key, value in data.items(): + # - если значение является словарем, вызываем рекурсивно функцию from_dict и устанавливаем результат в поле объекта + if isinstance(value, int) and hasattr(obj, key) and isinstance(getattr(obj, key), datetime.datetime): + setattr(obj, key, datetime.datetime.fromtimestamp(value)) + elif isinstance(value, dict): + nested_cls = getattr(obj, key).__class__ + setattr(obj, key, TypeExtension.from_dict(value, nested_cls)) + else: + setattr(obj, key, value) + + # Возвращаем восстановленный объект + return obj diff --git a/help/class_desc/extensions/type_extension.md b/help/class_desc/extensions/type_extension.md new file mode 100644 index 0000000..29ff18a --- /dev/null +++ b/help/class_desc/extensions/type_extension.md @@ -0,0 +1,77 @@ +# Класс `TypeExtension` + +Класс `TypeExtension` предназначен для работы с объектами Python и их сериализацией / десериализацией. Он поддерживает +удобное преобразование объектов в словари и восстановление объектов из словарей, включая вложенные структуры и специфику +обработки дат. + +## Основная информация + +- **Имя файла**: anb_python_components\extensions\type_extension.py +- **Автор**: Александр Бабаев +- **Версия**: 1.0.0 +- **Дата начала поддержки**: с версии 1.0 + +## Атрибуты и методы класса + +### Метод `to_dict` + +Преобразует экземпляр произвольного объекта в словарь, поддерживая рекурсию для вложенных структур и специальное +преобразование экземпляров классов `datetime` в целые числа (timestamp). + +**Параметры**: + +- `instance`: Объект, подлежащий преобразованию. + +**Пример использования**: + +```python +import datetime + +from anb_python_components.extensions.type_extension import TypeExtension + + +class SomeClass: + def __init__(self, field1, field2): + self.field1 = field1 + self.field2 = field2 + + +obj = SomeClass(field1="value1", field2=datetime.datetime.now()) +data = TypeExtension.to_dict(obj) +print(data) +``` + +### Метод `from_dict` + +Восстанавливает объект из словарного представления, также поддерживая вложенные объекты и специальную десериализацию +экземпляров класса `datetime`. + +**Параметры**: + +- `data`: Словарь, содержащий поля восстанавливаемого объекта. +- `cls`: Класс, экземпляр которого необходимо восстановить. + +**Пример использования**: + +```python +from anb_python_components.extensions.type_extension import TypeExtension + + +class SomeClass: + def __init__(self, field1, field2): + self.field1 = field1 + self.field2 = field2 + + +data = {'field1': 'Иван', 'field2': 20} + +restored_obj = TypeExtension.from_dict(data, SomeClass) +print(restored_obj.field1) # Иван +``` + +## Заключение + +Класс `TypeExtension` значительно упрощает процесс сериализации и десериализации объектов, что позволяет сохранять +состояние объектов в JSON или других форматов и загружать его обратно в приложение. + +[На главную](../../index.md) \ No newline at end of file diff --git a/help/index.md b/help/index.md index 30122b4..a5bdb99 100644 --- a/help/index.md +++ b/help/index.md @@ -20,4 +20,5 @@ - [класс `StringExtensionConstants`](class_desc/extensions/string_extension_constant.md) - [класс `StringExtension`](class_desc/extensions/string_extension.md) -- [класс `BoolExtension`](class_desc/extensions/bool_extension.md) \ No newline at end of file +- [класс `BoolExtension`](class_desc/extensions/bool_extension.md) +- [класс `TypeExtension`](class_desc/extensions/type_extension.md) \ No newline at end of file diff --git a/tests/extensions/type_extension_test.py b/tests/extensions/type_extension_test.py new file mode 100644 index 0000000..c9b69dd --- /dev/null +++ b/tests/extensions/type_extension_test.py @@ -0,0 +1,36 @@ +# type_extension_test.py + +import unittest + +from anb_python_components.extensions.type_extension import TypeExtension + + +class DemoClass: + def __init__(self, name): + self.name = name + self.age = 20 + + +class TypeExtensionTest(unittest.TestCase): + + def test_to_dict(self): + converted = TypeExtension.to_dict(DemoClass('Иван')) + self.assertEqual(converted, {'name': 'Иван', 'age': 20}) + + def test_from_dict(self): + # Представим данные в виде словаря + data = {'name': 'Иван', 'age': 20} + + # Преобразуем данные в объект DemoClass + converted = TypeExtension.from_dict(data, DemoClass) + + # Проверяем, что полученный объект является экземпляром DemoClass + self.assertIsInstance(converted, DemoClass) + + # Проверяем, что объект содержит ожидаемые значения + self.assertEqual(converted.name, 'Иван') + self.assertEqual(converted.age, 20) + + +if __name__ == '__main__': + unittest.main()