在python中使用单例模式非常的“容易”。 你在一个模块中初始化一个对象,然后去引用这个对象就行了。因为python中万物皆对象,哪怕是一个包(package),也可以当作一个对象。但是这种方式并不好,因为你的代码会看上去非常耦合,所以我从来不建议把包当成一个对象,你仅仅当成一个包就可以了。我再解释一下我的意思,就是你不应该直接从一个包中import一个对象,虽然你可以这么做。你应该import进来一个类,然后通过这个类,来创建对象,这才是单例模式正确的使用方式。而且对于IDE来说,那种从包中导入的对象,因为是动态的,所以你goto define也是找不到的。 而且如果你明白了我的意思,这些来来回回传递的对象,当你想要把一个函数封装出来单独写测试时,你会发现要把这些对象当做上下文当做传入的时候,简直头疼的要死。 下面将通过三个例子,来说明我的观点,第一个例子是我认为python日志的正确使用方式,第二个是tornado中是如何使用ioloop这个全局对象的,第三个例子是我认为的一个项目的配置文件应该使用的方式。 日志的使用方式我现在接手的项目,这个web服务的日志就是我说的那样,他们在一个包中初始化了日志对象,比如这个叫main_logger, 那个叫biz_logger,然后到处引用这些对象。 实际上我自己的代码中,获取日志的方式简单有粗暴: logger = logging.getLogger(__name__) 然后你在一个统一的地方为这个 name 配置handler就行了。而且实际上的工作量要比你想象的少很多,因为像”business.handler”,这样的name是可以继承”business”的日志配置的。你设计好包的格式,然后配置他们的日志打印方式,对于单测,只要以另一种方式初始化这些logger就行了,甚至可以不管,让他们使用root默认。 因为你的代码并没有“动态耦合”。 tornado中的ioloop还记得你怎么写tornado的代码吗? tornado.ioloop.instance().start() 我们可以去看tornado的ioloop源码 @staticmethod def instance(): """Returns a global `IOLoop` instance. Most applications have a single, global `IOLoop` running on the main thread. Use this method to get this instance from another thread. In most other cases, it is better to use `current()` to get the current thread's `IOLoop`. """ if not hasattr(IOLoop, "_instance"): with IOLoop._instance_lock: if not hasattr(IOLoop, "_instance"): # New instance after double check IOLoop._instance = IOLoop() return IOLoop._instance 注意这里加了一个线程锁,相当于在类中维护了这个单例对象,你可以把这个例子用在你的单例对象中。 配置文件的使用方式我的配置也是使用单例的方式,载入到服务中,然后instance去引用它。 当然我的配置类,可以有各种方式初始化其中的值,比如用json,用yaml,用conf文件,等等。。 需要注意的是,不要给你的单例对象写构造函数( init ),因为即使是单例模式的对象,也会每次调用你的构造函数,防止你每次获取单个例都刷新了所有东西。 还要注意的是,我现在维护的代码,有之前的同事,自己写了一个装饰器来装饰类,代码和上面的装饰器思路一样,使它变成一个单例类。这个问题是啥?被装饰器包裹的类,变成了一个函数。 (责任编辑:好模板) |