使用单例模式作为计数器
我有一个自动化测试,它使用一个函数来把截图保存到一个文件夹里。这个函数会被多个截图实例调用。在每次测试运行时,都会创建一个新的文件夹,所以我不在乎计数器重置的问题。为了能反映出这些截图的拍摄顺序,我需要想出一些可以按顺序排序的名字。这是我的解决方案:
def make_screenshot_file(file_name):
order = Counter().count
test_suites_path = _make_job_directory()
return make_writable_file(os.path.join(test_suites_path,'screenshot',file_name % order))
class Counter():
__counter_instance = None
def __init__(self):
if Counter.__counter_instance is None:
self.count = 1
Counter.__counter_instance = self
else:
Counter.__counter_instance.count += 1
self.count = Counter.__counter_instance.count
这个方法对我来说运行得很好。但我一直在想,应该有更简单的方法来解决这个问题。真的有吗?如果单例模式是唯一的解决办法,我的代码有没有什么可以优化的地方?
4 个回答
使用静态方法和变量。这种做法虽然不太符合 Python 的风格,但相对简单。
def make_screenshot_file(file_name):
order = Counter.count() #Note the move of the parens
test_suites_path = _make_job_directory()
return make_writable_file(os.path.join(test_suites_path,'screenshot',file_name % order))
class Counter():
count_n = 0
@staticmethod
def count():
Counter.count_n += 1
return Counter.count_n
print Counter.count()
print Counter.count()
print Counter.count()
print Counter.count()
print Counter.count()
atarzwell@freeman:~/src$ python so.py
1
2
3
4
5
为什么不直接这样做呢
order = time.time()
或者可以试试这样做
import glob #glob is used for unix like path expansion
order = len(glob.glob(os.path.join(test_suites_path,"screenshot","%s*"%filename))
你想做的其实是在模拟一个全局变量。
这样做没有什么好理由。如果你真的想要一个全局变量,那就直接定义一个全局变量吧。
你可以创建一个简单的 Counter
类,每次访问时让 count
增加 1,然后创建这个类的全局实例。不过,标准库里已经有类似的功能可以直接使用,像 itertools.count
,正如 DSM 在评论中提到的。
所以:
import itertools
_counter = itertools.count()
def make_screenshot_file(file_name):
order = next(_counter)
test_suites_path = _make_job_directory()
return make_writable_file(os.path.join(test_suites_path,'screenshot',file_name % order))
我不太明白你为什么这么担心存储或时间的消耗,因为我想不出有什么程序会在乎你是用 8 字节还是 800 字节来存一个对象,反正你也不可能有多个这样的对象,或者说访问它需要 3 纳秒还是 3 微秒,当你只会访问几次的时候。
不过如果你真的担心,正如你可以从 源代码看到的,count
是用 C 实现的,内存使用非常高效,如果你不做什么复杂的操作,基本上生成每个数字只需要一次 PyNumber_Add
,这比解释几行代码要简单得多。
既然你问了,这里有个方法可以通过使用 _count
类属性来大幅简化你现有的代码,而不是使用 __counter_instance
类属性:
class Counter():
_count = 0
def count(self):
Counter._count += 1
return Counter.count
当然,现在你需要用 Counter().count()
而不是 Counter().count
——但如果这很重要,你可以用 @property
来轻松解决这个问题。
值得注意的是,使用经典类而不是新式类(在括号里什么都不传)是个很糟糕的主意,如果你确实想要经典类,应该去掉括号。而且大多数 Python 程序员会把 Counter
这个名字和 collections.Counter
类联系在一起,count
也可以是 @classmethod
或 @staticmethod
……到那时,这就和 Andrew T. 的回答完全一致。正如他所指出的,这比你正在做的要简单得多,而且同样符合 Python 的风格。
但实际上,所有这些都不如直接把 _count
定义为模块级的全局变量,并添加一个模块级的 count()
函数来增加并返回它要好。