2012-08-22 85 views
6

我有几个在我的Python应用程序中普遍存在并且应该只有一个全局实例(例如Logger,DbConnection)的类。 Python不支持类中的静态变量/方法,因此在这里创建单例的常用Java/C++方式不起作用。我已经寻找了在Python中实现单例的替代方案。我想要一个简单的(如果可能的话,不要编程)和干净的实现。这看起来很不错:Python中的全局变量和单例

class MyClass(object): 
    def a(): 
     pass 

singleton = MyClass() 

使用Singleton是简单

import myClass 
myClass.singleton.a() 

直接分配可以由创建功能所取代,如果对象的初始化并非如此简单。

我也可以在模块范围内创建一个getInstance(),并始终使用它来获取myObj。

问题1)这工作正常吗?模块代码(myObj赋值)仅在第一次将其导入其他模块时运行,并且每次将该模块导入某处时都不会创建myObj?

我看到的另一种方法是使用全局模块。喜欢的东西:

from myClass1 import MyClass1 
from myClass2 import MyClass2 

myObj1 = MyClass1() 
myObj2 = MyClass2() 

使用此:

import globals 
globals.myObj1.a() 

我倾向于选择第一种选择。

问题2)在两种解决方案之间,你有什么建议?

问题3)第三种解决方案是将诸如Logger等广泛的对象传递给几个类/函数,但这不是一个好的解决方案。有没有更好的解决方案在这里没有提到?

我知道使用全局变量和单例的缺点。但是,拥有一个全球性的国家在我的申请中并不是一个大问题。我更喜欢代码清晰且易于使用的解决方案。

+0

你问题的前提是错误的。首先,python支持静态方法和变量。其次,为什么不使用现有的标准库进行日志记录和数据库访问? –

+0

Python直接支持静态方法/变量(没有注释破解)?你的第二个问题偏离了这个问题(单例实现),用任何一个单例类代替logging/db访问类,并且它在应用程序中被广泛使用。 –

回答

6

如果你想有一个只有一个实例的logger类,可以将它作为一个独立的模块。

# In logging.py 
def log(msg): 
    print msg 

然后从任何脚本你想登录。

from logging import log 
log("A critical error occured.") 
+1

我的记录器类中有属性(例如日志文件名)。如果我在模块函数中转换我的类方法,那么以前的类属性将成为模块变量。这不是问题吗? (例如,用户使用“从日志导入*”(错误我知道,但不过),我的变量会混乱应用程序变量空间 –

+0

@ user1550682:模块实际上只是Python中的单例,它是一个对象,只有一个实例,你可以像任何其他值一样传递它,所以没问题 –

+5

@ user1550682 - 用一个下划线作为你的私有变量的前缀,它们不会用'import *语句,例如'_filename =“bla.bla”' – Aesthete

-1

如何制作单身人士的正确答案?别。你应该明确地传递一切需要使用它的引用。您可以减少工厂功能,包装类等的工作量。

即使有一些东西,如本质上是全局性的屏幕或记录器,您仍然应该明确地通过它以允许进行单元测试。请注意,这仅适用于旨在成为最终设计一部分的事物。如果您的记录器只是一个快速入门的调试工具,请随时将其设置为全局。

+0

正如我原来的文章中提到的,我意识到使用单例的缺点。自从我第一次听说他们到今天,他们的使用是有争议的。将一个记录器变量传递给应用程序中的所有类和函数(如果它们不在类中)似乎是乌托邦式的,我从来没有见过一个真正的应用程序。 –

+0

我已经做了几次,虽然承认相对简单的脚本。而且我计划在我目前的项目中做得更完整。但你说得对,这有点乌托邦。 – Antimony

+1

Alan:许多应用程序使用全局记录器实例,或者将其传递到应用程序其他部分导入的设置模块中。此外,许多应用程序还依赖于应用程序或设置模块,该模块存储全局变量,单例实例或要在整个应用程序中使用的线程。 Django和Celery都是这样做的应用程序的例子。通过使用代理和状态设计模式,它简化了依赖单例的需要,尽管最终这是发生的事情,只是以不同的形式。 –

0

考虑创建如下:

  1. State类
  2. 代理类

状态类将创建在INIT初始化一个线程。 py模块。状态类将具有将类实例传递到线程并将弱参数保存到其中的方法。

稍后,可以使用代理类访问线程中的类实例。如果未创建类对象,则代理将创建所需类的实例并将其保存到线程中。

使用此模式,可以将每个类的实例数限制为1,并确保无论何时应用程序的一部分正在运行,都不必重新创建需要全局重复使用的任何实例。