利用python import机制与类声明机制实现单例
import机制一:执行被导入代码
当在python中执行一条import 语句的时候python解释器会按照sys.path变量中的顺序遍历文件夹寻找import 语句指定的包.如果指定的包是package(文件夹)则尝试执行__init__.py。
在base.py中有如下语句
# base.py
print(f"base.py is been importing")
在importBase_1.py中有如下语句
# importBase_1.py
import base
以上语句将输出 base.py is been importing
import机制二:每个包只导入一次
接上文的例子, 在importBase_2.py中有如下语句
# importBase_2.py
import importBase_1
import base
结果只打印了一次, 显然只有在第一条import语句执行的时候才真正导入了base.py,并执行了base.py中的代码
类声明机制:任然是执行代码
java中声明一个类的语句
class Test:
print("declaring a class in python")
def method(self):
pass
# 输出: declaring a class in python
利用两种机制实现单例
现在来修改base.py使他看起来有意义(而不只是打印一句话)
# base.py
class Base:
some_count = 0
Base 类中定义了一个(静态)字段some_count用来记录(全局)程序中某件事情发生的次数。
现在importBase_1.py 中导入Base
# importBase_1.py
import base
# some code...
Base.some_count += 10
在importBase_1.py中,某件事情发生了10次,给Base.some_count 加上10
然后importBase_1.py执行完毕了回到了调用importBase_count的逻辑中,出于某种原因,调用importBase_1.py的程序import 了base.py
# importBase_2.py
import importBase_1
# for some reasons
from base import Base
print(Base.some_count) # 输出 10
print(importBase_1.Base.some_count) # 输出 10
此时,尽管importBase_1.Base与Base并不在同一个命名空间中但是由于importBase_2.py已经import了Base类,所以并不会再次导入base.py,也并不会再次执行base.py中的代码,所以不会将Base.some_count字段设置为0。
-
也就是说,现在加入要实现这样的逻辑:
- 在主程序中通过import 导入了一系列的包,这些在执行的时候共享某一个单例。(比如统计错误的error)就可以讲error在专门的py文件中用类定义的方式实现:
# error.py
class errors:# 类名小写以模拟对象
# 错误计数
error_count = 0
# 避免实例化这个类
def __init__(self):
raise Exception("Do not instantiate this class")
这样一来,在其他有可能产生错误的文件中就可以像是使用单例一样使用error类
# use_error.py
from error import errors as e
def some_func(arg1, arg2):
# 如果出现某种错误情况,给e.error_count++
if (arg1 == arg2 * 10):
e.error_count += 1
else:
# some code...
总结
利用python的两个机制,可以比较好的模拟单例。但是本文给出的例子没有考虑多线程甚至多进程对单例字段修改的同步问题,这一点需要注意。以后有时间可能会研究一下如何在多线程多进程中优雅的使用单例