6
\$\begingroup\$

I am sub-classing an object in python and I need to overload a bunch of the methods with very similar logic, essentially a call to super() and then some additional operations. I want to avoid explicitly writing out each of those overloaded methods in that way, so I implemented a decorator.

Is there anything wrong with doing the following:

def decorator(function):
    def wrapper(*args, **kwargs):
        # Logic + operations here
        function()
        # Logic and operations here

    return wrapper

class SubClass(SuperClass):
    method1 = decorator(SuperClass.method1)
    method2 = decorator(SuperClass.method2)

    def __init__(self, *args, **kwargs):
        super().__init__(*args, **kwargs)
        # Extra logic here

Here is my actual implementation:

def updates_file(function):
    """
    Decorator to call update file method after super call.
    """
    def wraper(*args, **kargs):
        function(*args, **kargs)
        args[0]._update_file()

    return wraper

class FileMirroredDeque(deque):

    __delitem__ = updates_file(deque.__delitem__)
    __iadd__    = updates_file(deque.__iadd__)
    __imul__    = updates_file(deque.__imul__)
    __setitem__ = updates_file(deque.__setitem__)
    append      = updates_file(deque.append)
    appendleft  = updates_file(deque.appendleft)
    extend      = updates_file(deque.extend)
    extendleft  = updates_file(deque.extendleft)
    insert      = updates_file(deque.insert)
    pop         = updates_file(deque.pop)
    popleft     = updates_file(deque.popleft)
    remove      = updates_file(deque.remove)
    reverse     = updates_file(deque.reverse)
    rotate      = updates_file(deque.rotate)

    def __init__(self, cache_path, maxlen=None, clean=False, file_indent=None):
        super().__init__((), maxlen)  # Initializes the deque as well TODO: Check the implication of this calling __setitem__

        self.path = cache_path
        self._indent = file_indent
        self._bak_file = None

        if clean:
            self._update_file()  # Overwrite contents of file.
            return  # Don't import data from file.

        with open(self.path, 'a+') as f:  # Creates file if it doesn't exist
            f.seek(0, 0)  # Seek back to beginning of file for json decode.
            contents = f.read()

        try:
            self.extend(json.loads(contents or "[]"))  # initializes internal list with persistent data or empty list.

        except json.decoder.JSONDecodeError:
            new_file = os.path.basename(self.path) \
                       + datetime.datetime.utcnow().strftime("_%Y_%m_%d_%H_%M_%S") + ".bak"

            new_path = os.path.join(os.path.dirname(self.path), new_file)

            logger.warning(f"File: {self.path} was not valid JSON. It will be copied to {new_path} and a new "
                           f"file will be created.")

            shutil.copyfile(self.path, new_path)
            self._bak_file = new_path

    def _update_file(self):
        with open(self.path, 'w') as f:
            json.dump(list(self), f, indent=self._indent)
\$\endgroup\$
  • \$\begingroup\$ Your decorator is not equivalent to calling super. In your case you always refer to the parent class, which is not the only behaviour of super. \$\endgroup\$ – Mathias Ettinger Aug 14 '18 at 13:04
  • \$\begingroup\$ Perhaps I am missing something about the nature of super(). All I need, however, is to inherit all the functionality of the parent method and perform some extra operations before and after which do not modify the instance of the object at all. \$\endgroup\$ – Alex Eshoo Aug 14 '18 at 13:10
  • \$\begingroup\$ What I mean is: this may work fine in single inheritance schemes, but as soon as you have sibling classes in your inheritance tree, your approach and super() would give different results. \$\endgroup\$ – Mathias Ettinger Aug 14 '18 at 13:12
  • 1
    \$\begingroup\$ I have now included my actual listing. \$\endgroup\$ – Alex Eshoo Aug 14 '18 at 14:08
  • 2
    \$\begingroup\$ Much better than the original post. I congratulate you on managing to reverse an 'on-hold' question. \$\endgroup\$ – FreezePhoenix Aug 14 '18 at 14:46

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Browse other questions tagged or ask your own question.