1
votes

I am using a nested class for "book keeping" purposes in Python 3. The class structure (highly simplified) looks something like this:

class BookKeeping:

    class ItemA:
        count = 0
        item_list = []

        @classmethod
        def add(cls, item_a):
            cls.count += 1
            cls.item_list.append(item_a)

    class ItemB:
        count = 0
        item_dict = {}

        ...

This class makes it convenient to keep track and to access some "global" properties of a system I am modelling. Having nested classes allows for clear namespacing. For instance, in any module I can ... import BookKeeping as BK and check how many ItemB objects there are (BK.ItemB.count).

At some point in my program I would like to "reset" all class attributes, i.e. I would like to set BookKeeping.ItemA.count to 0 again and empty lists and dictionaries, etc.

Obviously, I could add a top-level class method like BookKeeping.reset() which resets all values "manually", like:

class BookKeeping:

    @classmethod
    def reset(cls):
        cls.ItemA.count = 0
        cls.ItemA.item_list = []
        cls.ItemB.count = 0

        ...

However, this seems to be an error-prone approach to me and it involves "maintenance" of this class (I might forget to reset some attributes). Please note, my real class is more complicated than the example I have provided.

What is the easiest way to reset all class attributes to their original state at runtime?

1
Can you just have a separate class (e.g. BookKeepingSession or something) that holds those values as instance properties rather than class properties? Then you could just create a fresh BookKeepingSession each time you want to do some unrelated BookKeeping.Daniel Pryden

1 Answers

2
votes

So upfront: Should you actually be doing this? Probably not.

I'd advise you to try to rethink your projects structure, actually, since global state is something which should usually be avoided.

If you still want to do it that way though...

Beware, some untested (!) arcane magic ahead:

import copy

class BookKeeping(object):
    __backup = {}

    class ItemA(object):
        ...

    class ItemB(object):
        ...

    @classmethod
    def reset(cls):
        for name, backup in cls.__backup.items():
            inner_cls = getattr(cls, name)
            for k, v in backup.items():
                setattr(inner_cls, k, copy.deepcopy(v))

for i in dir(BookKeeping):
    if not i.startswith("_"):
        obj = getattr(BookKeeping, i)
        BookKeeping.__backup[i] = {k: copy.deepcopy(getattr(obj, k)) for k in dir(obj)}

Additional notes: This is generally a tricky problem. But you could use the same technique for other nesting structures too, and handle types differently instead of handling the first level differently.

This is a problem because you don't want to deepcopy the types themselves, since that would duplicate the state across all live objects at that time and all new objects together with the current new global state.