使用单例作为计数器

时间:2013-03-15 18:20:28

标签: python singleton python-2.6

我有一个自动化测试,它使用为文件夹创建屏幕截图的功能。此功能由多个屏幕截图实例调用。在每次测试运行时,都会创建一个新文件夹,所以我不关心计数器重置。为了反映这些屏幕截图的顺序,我必须提出可以按顺序排序的名称。这是我的解决方案:

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 个答案:

答案 0 :(得分:4)

你在这里要做的是模拟一个全局变量。

没有充分的理由这样做。如果你真的想要一个全局变量,那么明确地将它变成一个全局变量。

您可以创建一个简单的Counter类,每次访问时将count递增1,然后创建它的全局实例。但正如DSM在评论中解释的那样,标准库已经在itertools.count中免费提供了类似的内容。

所以:

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个单个对象都可能无关紧要你可能永远不会有一个以上,或者当你只做了几次时它是否需要3ns或3us才能访问它。

但如果您 担心,正如您从the source所看到的那样,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来解决这个问题。

值得指出的是,使用经典类而不是新式类(通过在parens中传递任何内容)是一个非常糟糕的主意,如果你想要一个经典类你应该让parens关闭,大多数Python程序员会将名称Counter与类collections.Counter相关联,并且没有理由count不能是@classmethod或{{ 1}} ......在这一点上,这正是Andrew T.的答案。正如他所指出的那样,这比你正在做的更简单,而且不会更多或更少的Pythonic。

但实际上,所有这些并不仅仅是让@staticmethod成为模块级全局,并添加一个模块级_count函数,该函数会递增并返回它。

答案 1 :(得分:2)

为什么不做呢

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))

答案 2 :(得分:1)

使用静态方法和变量。不是pythonic,但更简单。

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

答案 3 :(得分:0)

好吧,您可以使用此解决方案,只需确保您永远不会初始化订单kwarg!

函数中的可变Kwargs像类全局变量一样工作。正如您最初可能想到的那样,值不会在调用之间重置为默认值!

def make_screenshot_file(file_name , order=[0]):
    order[0] = order[0] + 1
    test_suites_path = _make_job_directory()
    return make_writable_file(os.path.join(test_suites_path,'screenshot',file_name % order[0]))