Python 它是';原罪';要在_;新_;中执行初始工作?如果是这样,如何最好地处理单例运行一次初始化?
我有一个类,它具有我使用的标准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)
\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?