Search code examples
pythonpython-3.xstackpython-decorators

How to pass instance of a class as a parameter to a decorator decorating an instance method defined in the class?


I want to implement a Stack in Python3 and for some methods which need to check Stack Empty or Stack Full, I want to write decorators which would take care of checking the same and be usable for various methods which need those checks.

This is what I have tried to do (Please check the implementation of push and pop methods):

repl.it link : https://repl.it/@Ishitva/Stack

class StackFullException(Exception):
    pass

class StackEmptyException(Exception):
    pass

def checkStackFull(instance):
    def check(func):
        def execute(*args, **kwargs):
            if len(instance.items) <= instance.limit:
                return func(*args, **kwargs)
            raise StackFullException

        return execute
    return check

def checkStackEmpty(instance):
    def check(func):
        def execute(*args, **kwargs):
            if len(instance.items) > -1:
                return func(*args, **kwargs)
            raise StackEmptyException

        return execute
    return check


class Stack():

    def __init__(self, limit=10):
        self.items = []
        self.limit = limit

    @checkStackFull(self)
    def push(item):
        self.items.append(item)
        return item

    @checkStackEmpty(self)
    def pop():
        return self.items.pop()

    def getSize():
        return len(self.items)

This gives me the following exception:

Traceback (most recent call last):
  File "main.py", line 28, in <module>
    class Stack():
  File "main.py", line 34, in Stack
    @checkStackFull(self)
NameError: name 'self' is not defined

Solution

  • But if you really need to do that, then code:

    class StackFullException(Exception):
        pass
    
    class StackEmptyException(Exception):
        pass
    
    
    def checkStackFull(func):
        def execute(self, *args, **kwargs):
            if len(self.items) <= self.limit:
                return func(self, *args, **kwargs)
            raise StackFullException()
    
        return execute
    
    
    def checkStackEmpty(func):
        def execute(self, *args, **kwargs):
            if len(self.items):
                return func(self, *args, **kwargs)
            raise StackEmptyException()
        return execute
    
    
    class Stack():
        def __init__(self, limit=10):
            self.items = []
            self.limit = limit
    
        @checkStackFull
        def push(self, item):
            self.items.append(item)
            return item
    
        @checkStackEmpty
        def pop(self):
            return self.items.pop()
    
        def getSize(self):
            return len(self.items)
    

    And by the way pop from empty list will raise IndexError anyway so you could just use that.