{ "cells": [ { "cell_type": "markdown", "metadata": {}, "source": [ "## Pure Python Metaclass" ] }, { "cell_type": "code", "execution_count": 1, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "Initializing singleton: 21aac41a-ee95-40c0-88f1-f55eaf81ac60\n", "MySingleton(uuid=UUID('21aac41a-ee95-40c0-88f1-f55eaf81ac60'))\n", "MySingleton(uuid=UUID('21aac41a-ee95-40c0-88f1-f55eaf81ac60'))\n", "MySingleton(uuid=UUID('21aac41a-ee95-40c0-88f1-f55eaf81ac60'))\n", "MySingleton(uuid=UUID('21aac41a-ee95-40c0-88f1-f55eaf81ac60'))\n", "MySingleton(uuid=UUID('21aac41a-ee95-40c0-88f1-f55eaf81ac60'))\n" ] } ], "source": [ "from dataclasses import dataclass, field\n", "from uuid import UUID, uuid4\n", "\n", "\n", "# Metaclass definition\n", "class Singleton(type):\n", " _instances = {}\n", "\n", " def __call__(cls, *args, **kwargs):\n", " if cls not in cls._instances:\n", " cls._instances[cls] = super().__call__(*args, **kwargs)\n", " return cls._instances[cls]\n", "\n", "\n", "# gives an example of inheritance\n", "@dataclass\n", "class ParentClass:\n", " uuid: UUID = field(init=False, default_factory=uuid4)\n", "\n", "\n", "# Main singleton class\n", "@dataclass\n", "class MySingleton(ParentClass, metaclass=Singleton):\n", " def __post_init__(self):\n", " print(f'Initializing singleton: {self.uuid}')\n", "\n", "\n", "# Initialize the singleton\n", "MySingleton()\n", "\n", "# \"Initialize\" a few more times\n", "for _ in range(5):\n", " print(MySingleton())" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## Pydantic Metaclass" ] }, { "cell_type": "code", "execution_count": 2, "metadata": {}, "outputs": [], "source": [ "from typing import Any, Optional, Self\n", "\n", "from pydantic import BaseModel, Field\n", "from pydantic._internal._model_construction import ModelMetaclass\n", "\n", "\n", "class PydanticSingleton(ModelMetaclass):\n", " _instances = {}\n", "\n", " def __call__(cls, *args, **kwargs):\n", " if model := cls._instances.get(cls):\n", " model_dict = model.model_dump()\n", " model_dict.update(kwargs)\n", " model = model.model_validate(model_dict)\n", " cls._instances[cls] = model\n", " else:\n", " cls._instances[cls] = super(PydanticSingleton, cls).__call__(*args, **kwargs)\n", "\n", " return cls._instances[cls]\n", "\n", "\n", "class MyPydanticSingleton(BaseModel, metaclass=PydanticSingleton):\n", " id: UUID = Field(default_factory=uuid4)\n", " required_field: str\n", " foo: Optional[str] = None" ] }, { "cell_type": "code", "execution_count": 3, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "id=UUID('9965c864-20b7-4809-b73a-c16c352fea20') required_field='Iteration 0' foo=None\n", "id=UUID('9965c864-20b7-4809-b73a-c16c352fea20') required_field='Iteration 1' foo=None\n", "id=UUID('9965c864-20b7-4809-b73a-c16c352fea20') required_field='Iteration 2' foo=None\n", "id=UUID('9965c864-20b7-4809-b73a-c16c352fea20') required_field='Iteration 3' foo=None\n", "id=UUID('9965c864-20b7-4809-b73a-c16c352fea20') required_field='Iteration 4' foo=None\n" ] } ], "source": [ "s = [MyPydanticSingleton(required_field=f'Iteration {i}') for i in range(5)]\n", "print('\\n'.join(map(str, s)))" ] }, { "cell_type": "code", "execution_count": 4, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "MyPydanticSingleton(id=UUID('9965c864-20b7-4809-b73a-c16c352fea20'), required_field='Iteration 4', foo='bar')" ] }, "execution_count": 4, "metadata": {}, "output_type": "execute_result" } ], "source": [ "MyPydanticSingleton(foo='bar')" ] } ], "metadata": { "kernelspec": { "display_name": "appdaemon", "language": "python", "name": "python3" }, "language_info": { "codemirror_mode": { "name": "ipython", "version": 3 }, "file_extension": ".py", "mimetype": "text/x-python", "name": "python", "nbconvert_exporter": "python", "pygments_lexer": "ipython3", "version": "3.11.8" } }, "nbformat": 4, "nbformat_minor": 2 }