【问题标题】:Python Singleton DataclassPython 单例数据类
【发布时间】:2021-08-02 05:13:32
【问题描述】:

我有这样的课:

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

【问题讨论】:

  • 这能回答你的问题吗? Creating a singleton in Python
  • 你的方法没有错。我想说,像这样的单例模式并非没有它的批评者......而且它在 Python 中并不是很常见
  • @quamrana - 感谢您的链接。我在发布之前通读了该答案。这个问题真的不适合我的问题。我主要对如何在使用@dataclass 时执行此操作感兴趣。

标签: python singleton python-dataclasses


【解决方案1】:

我发现实现单例数据类的最 Pythonic 方法是使用 @quamrana 评论的 answer 中建议的单例元类,并添加默认值以防止类型检查器在您调用时发出警告以后不带参数的数据类。

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

此代码的唯一问题是您不必填写数据类的所有参数。如果您想使用类型检查器强制执行此操作,请删除默认值,但请注意,如果您稍后不提供它们,它将引发警告。或者,您可以检查 dunder 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

另外:你的数据类用于配置,也许你应该考虑库Hydra

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2014-12-03
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多