Python单例数据类

8

我有一个类似这样的类:

from __future__ import annotations

import os
from dataclasses import dataclass


@dataclass(frozen=True)
class Config:
    name: str
    age: str

    @staticmethod
    def init() -> Config:
        return Config(
            name=...
            age=...
        )

我希望确保init方法始终返回Config的相同实例。

我可以通过以下方式实现:

@dataclass(frozen=True)
class Config:
    name: str
    age: str

    @staticmethod
    def init() -> Config:
        if not _private_instance:
            global _private_instance = Config(
                name=...
                age=...
            )
        return _private_instance

_private_instance: Optional[Config] = None

But I am wondering if there is a more Pythonic way of doing this.  Thanks

这个回答解决了你的问题吗?在Python中创建单例模式 - quamrana
你的方法没有问题。我想说,像这样的单例模式并不是没有它的批评者...而且它在Python中并不是很常见。 - juanpa.arrivillaga
@quamrana - 感谢提供链接。在发布问题之前,我已经阅读了那个答案。但是那个问题并不适用于我的问题。我主要想知道如何在使用@dataclass的情况下完成这个操作。 - sixtyfootersdude
1个回答

4
我发现实现单例数据类最Pythonic的方法是使用单例元类,正如@quamrana在答案评论中建议的那样,并添加默认值以防止类型检查器在稍后调用数据类时引发警告。
from dataclasses import dataclass


class Singleton(type):
    _instances = {}

    def __call__(cls, *args, **kwargs):
        if cls not in cls._instances:
            cls._instances[cls] = super().__call__(*args, **kwargs)
        return cls._instances[cls]


@dataclass(frozen=True)
class Config(metaclass=Singleton):
    name: str = ''
    age: str = ''


p1 = Config('John', '25')
print(p1)  # Config(name='John', age='25')
p2 = Config()
print(p2)  # Config(name='John', age='25')

print(p1 == p2)  # True
print(p1 is p2)  # True

这段代码唯一的问题在于你不必填写数据类的所有参数。如果你想强制执行类型检查,可以删除默认值,但要注意,如果以后没有提供这些值,它会引发警告。或者,你可以在特殊的“post_init”方法中检查是否设置了适当的值:
@dataclass(frozen=True)
class Config(metaclass=Singleton):
    name: str = ''
    age: str = ''
    
    def __post_init__(self):
        if self.name == '' or self.age == '':
            raise ValueError('name or age is empty')

但这只会在运行时引发。

我在你的问题中看到了全局变量。你应该避免使用它。 例如(这不是单例数据类的好方法,只是修复你的示例而不使用全局变量):

from __future__ import annotations

from dataclasses import dataclass, field
from typing import Optional


@dataclass(frozen=True)
class Config:
    name: str
    age: str
    _private_instance: Optional[Config] = field(init=False, repr=False)

    @classmethod
    def init(cls) -> Config:
        if not cls._private_instance:
            cls._private_instance = Config(
                name=...,
                age=...,
            )
            return cls._private_instance

另外:您的数据类用于配置,也许您应该考虑使用OmegaConf库。


在Python3.11中,返回错误:"raise error, type object 'Config' has no attribute '_private_instance' in line if not cls._private_instance"。 - Panic
你能看一下这个问题吗?https://stackoverflow.com/questions/76585659/how-to-elegant-use-python-dataclass-singleton-for-config ,谢谢。 - Panic

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接