Search code examples
pythonpython-3.xdecoratorpython-decorators

Using decorators in classes python3


Please help to understand how to correctly use decorators in classes. For example, I want to check variables before executing the class, and if it's exist run class with this variable. How to do this?

My not working example :

import os,sys
import requests


def checkCredentials(function):
  KEY = os.environ['KEY']
  if KEY:
    print("exist")
    return KEY
  else:
    print("nope")
    sys.exit(0)


@checkCredentials
class StartUp(object):

  def __init__(self, argument1):
    self.argument1 = argument1

  def printstr(self):
    print(self.argument1)


if __name__ == '__main__':
  start = StartUp()

My error :

python3.6 ./main.py 
exist
Traceback (most recent call last):
  File "./main.py", line 26, in <module>
    start = StartUp()
TypeError: 'str' object is not callable

Solution

  • You should read up some more on decorators, maybe check PEP 3129 or PEP 318. The following example from the former PEP illustrates the semantics of class decorators:

    # Solution 1: Without class decorators
    class A:
      pass
    A = foo(bar(A))
    
    # Solution 2: With class decorators
    @foo
    @bar
    class A:
      pass
    

    The two solutions produce identical results. In Solution 1 the functions foo and bar are called explicitly to modify the initial class A and produce a modified variant. By using decorators, the same effect can be achieved much more clearly: this is Solution 2.

    Let's apply this to your code example. Your final line of code is

    start = StartUp()
    

    which translates to

    start = checkCredentials(StartUp)()
    

    However, checkCredentials either returns nothing or a str, hence the TypeError. I am not sure what exactly you want to achieve with the decorator. If all you want is a test for the presence of the specific environment variable, the following decorator might produce the desired effect:

    def checkCredentials(cls):
      KEY = os.environ['KEY']
      if KEY:
        print("exist")
        return cls
      else:
        print("nope")
        sys.exit(0)
    

    Note that this returns the original class from the decorator iff the environment variable is set (happy code-path) and terminates execution otherwise (hence there's no need to return anything form the decorator).