简体   繁体   中英

Python Singleton design pattern tracking application stats

I have a Python application that executes a work flow of tasks. Each of these tasks might be in its own module. When the list of all tasks is completed, the application closes. Before it shuts down, I want to collect relevant statistics from each task.

I was thinking about using the singleton pattern to provide one place to store all this data so I can retrieve it at the end. Each task would import the singleton stats-tracking class, create an instance (the common, single instance of the class) and use that to store any data.

In my case I want a single bag to store data from each task. I keep hearing that singletons are bad. I wanted to get input about using the singleton design pattern or any other recommendations.

Whether singletons are "bad" or not seems to be a matter of taste. Certainly they have their place, and any of the variations on the singleton theme should work for you.

The "Borg pattern" (less colorfully & much less commonly, "StatelessProxy" or "Monostate") has been a popular Python alternative to Singleton probably ever since Alex Martelli's clever ActiveState recipe Singleton? We don't need no stinkin' singleton: the Borg design pattern . It differs from Singleton in allowing multiple distinct objects of a class, all sharing common data. The Singleton pattern, by contrast, ensures that only one instance of a class is ever created.

A discussion of the Borg vs Singleton issue can be found in this stackoverflow post: Why is the Borg pattern better than the Singleton pattern in Python . The implementations at the top of the post might be puzzling due to the missing _init_default_register method, whose purpose is to create and initialize the common data attributes, once only. For reference and comparison, here are complete implementations (in Python 3), both of which create a single data attribute (a dict named data ):

The standard way to implement Singleton in Python is with a metaclass;

class Singleton(type):
    def __init__(cls, *args, **kwargs):
        cls.__instance = None
        super().__init__(*args, **kwargs)

    def __call__(cls, *args, **kwargs):
        if cls.__instance is None:
            cls.__instance = super().__call__(*args, **kwargs)
        return cls.__instance

You would make your statistics-tracking class a singleton by giving it this metaclass:

class StatsTrackerSingleton(metaclass=Singleton):
    def __init__(self):
        self.data = {}

    # ... methods to update data, summarize it, etc. ...

The Borg pattern is simpler to implement and, because it doesn't use a metaclass, potentially more flexible:

class StatsTrackerBorg():
    __shared_data = {'data':{}}
    # RHS guarantees a common dict named `data`

    def __init__(self):
        """Make every instance use StatsTrackerBorg.__shared_data
        for its attribute dict."""
        self.__dict__ = self.__shared_data

    # ... methods to update data, summarize it, etc. ...

With both patterns, as implemented above, you can use the common dict data , and you can simply get and set shared attributes using the dot operator. For example, using the Borg class:

>>> a = StatsTrackerBorg()
>>> b = StatsTrackerBorg()
>>> a is b          # would be True for StatsTrackerSingleton
False
>>> vars(a) is vars(b)
True

>>> vars(a)
{'data': {}}
>>> a.data['running_time'] = 10000
>>> b.bar = 10
>>> vars(a)
{'data': {'running_time': 10000}, 'bar': 10}

>>> b.foo = 'y'
>>> a.foo
'y'

A noteworthy difference between the two patterns: a subclass of a "Borg'ed" class shares the same common state with the superclass, and can still add more shared state accessible to its instances, whereas each subclass of a Singleton class gets its own unique instance and thus its own common state, disjoint from that of the superclass. For some intended applications, one of these behaviors may be clearly more appropriate than the other.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM