Python 它是';原罪';要在_;新_;中执行初始工作?如果是这样,如何最好地处理单例运行一次初始化?

Python 它是';原罪';要在_;新_;中执行初始工作?如果是这样,如何最好地处理单例运行一次初始化?,python,Python,我有一个类,它具有我使用的标准python单例形式。它也初始化一个值,但不是在每个实例中初始化它,而是在执行单例处理时在\uuu new\uuu()中初始化它。这是蟒蛇式的方法吗?如果没有,我还能做什么 class Foo(object): _inst = None def __new__(cls, val): if Foo._inst is None: Foo._inst = super(Foo, self).__new__(cls)

我有一个类,它具有我使用的标准python单例形式。它也初始化一个值,但不是在每个实例中初始化它,而是在执行单例处理时在
\uuu new\uuu()
中初始化它。这是蟒蛇式的方法吗?如果没有,我还能做什么

class Foo(object):
    _inst = None
    def __new__(cls, val):
        if Foo._inst is None:
            Foo._inst = super(Foo, self).__new__(cls)
            Foo._inst.val = val
        return Foo._inst
这显然是简化的形式。我使用了waiting till
\uuuu init\uuuu()
的构造来检查并设置
Foo.\u inst
来处理单实例的run once init,但这对于多线程情况下的同步错误来说似乎已经成熟。如果再次运行
\uuuu init\uuuu()
时,现有实例中没有可能损坏的值,那么将init放入
\uuuu init\uuu()
中并没有什么害处,除非您运行的代码在不需要时实际上什么都不做,但是,我不想覆盖的状态值经常会损坏。我知道单例也是有害的,但有时,它们是最好的解决方案(比如定义我希望能够检查的特殊使用类型
is


这似乎是我能想到的最好的解决方案,但我觉得在
\uuuu new\uuuu()
中初始化实例值有点脏。有人对如何更好地处理这个问题有想法吗?

回答第一个问题: 在Python中不经常使用单例,因为它们实际上不是必需的

回答第二个问题: 如果你认为你真的需要它们,有一种更干净、更相似的图案,叫做“博格”,有时也会用到,参见: 如果您只需要在实例之间共享一些属性,而不需要完整的单例(可以实例化一次),您可以简单地使用类属性,如下所示:

class Foo( object ):
    val = ...

    def __init__( self ):
        ...

然后所有实例将共享相同的“val”属性值。

没有更好的方法来创建单例。如果你想这样做(三值逻辑是我能想到的唯一一个有意义的例子),那么你就可以按自己的方式来做。

你总是可以使用全局变量而不是单例。具有三个不同实例的类的示例:

class _Ternary(object):
    """Internal class.  Don't instantiate!"""
    def __init__(self, value):
        self.value = value

ternary_0 = _Ternary(0)
ternary_1 = _Ternary(1)
ternary_2 = _Ternary(2)

def ternary(value):
    """Factory function to request a "ternary" instance."""
    return {0: ternary_0, 1: ternary_1: 2: ternary_2}[value]

这是直截了当的,可以避免单身人士的痛苦。

使用单身人士是一种原罪。它们是一种给globals添加额外缺点和奇怪限制的方式。如果您需要全局状态,请使用全局状态,但最好尽量避免使用全局状态。永远不要使用单例。@Sven Marnach我当然会尽我所能避免单例和全局,但在某些情况下,它们是最好的解决方案。例如,我刚刚实现了一个三元类,但我不需要每种类型(True、False、Unknown)都有一个以上的实例,事实上,每种类型只有一个实例,通过允许
is
操作符工作,我可以更像bool一样使用它。所以,虽然单身是要避免的,但当他们是最好的选择时,应该有好的方法来使用他们。你不是说“大罪”吗?使用博格。(它看起来像一个普通的类,但实例字典存储在该类上,因此每个实例的行为实际上都是相同的。)此外,您不应该对布尔值使用
is
。例如,
[]不是False
,但空列表是boolean False。我可能过于简化了我的示例。。。在更常见的示例中,我实际上在类中存储了一个实例字典,我的目标是限制特殊类型实例的数量。所以你最终得到了
Foo.\u insts={1:Foo(1),2:Foo(2),3:Foo(3)}
。然后你会得到3个值的唯一类型,但每个值只有一个实例。Python不是一种允许如此严格的操作的语言,也就是说,你可能无法100%地保护自己。但是,如果您只是想为它创建一个合适的接口,为什么不创建两个类,其中一个是寄存器,如果没有超出限制,那么允许创建其他类的实例?i、 例如Foo()和FooRegistry(),并且有一个FooRegistry.create()方法,如果未超过限制,该方法将返回Foo实例,或者引发异常,或者执行其他操作来处理这种情况?为什么要添加另一层类来做同样的事情?这将FooRegistry和Foo合并为一个类,其中FooRegistry.create()将转换为Foo.\uuuu new\uuuuu()。因为我们认为它不太优雅和pythonic,请参阅@Sven Marnach对您的问题的评论。这些类还应该映射到问题域中的某种逻辑对象。根据你所说的,这里显然有两个对象——Foo,以及某种跟踪Foo的注册表,那么为什么不简化(KISS原则)并使其干净?@sr2222:如果你认为你的示例被过度使用,你可以修改你的问题并更新它/改进它/添加更多有用的信息。在这里陈述您的需求和实际代码,这样我们就可以真正帮助您/告诉您是否有问题。是的,这是我对数据类型的使用案例。但是使用这种模式,如何使运算符重载很好地工作?例如,您将如何使
三元\u 1-三元\u 1
返回
三元\u 0
?@GrantJ:我不理解这个问题。在
三元
类上定义
\uuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuu?