Search code examples
pythonpython-decoratorspython-descriptors

Decorate class to run a piece of code when __get__() is called


Please have a look at the below code

class MyStudent:
    def __init__(self, student_id, student_name):
        self._student_id = student_id
        self._student_name = student_name

    def get_student_id(self):
        return self._student_id

    def get_student_name(self):
        return self._student_name


student1 = MyStudent(student_id=123, student_name="ABC")
student1.get_student_id()
student1.get_student_name()

I would like to run some code like adding the student to the DB whenever student1.get_student_id() or student1.get_student_name() is invoked (or when get() is accessed. Please correct me if i used the wrong descriptor). And I have to do this via Decorators only for multiple classes like below

@save_student_to_db
class MyStudent:......

How can this be achieved using Decorators? I need to use a single decorator for multiple classes which can have any method. Whenever any method (apart for the ones starting with _ or __) of any class is called the decorator should save the data to DB


Solution

  • If all classes implement the same methods, say get_student_id and get_student_name, and has the same attributes, say _student_id and _student_name, then you can do a class decorator like so:

    from functools import wraps
    from somewhere import Database
    
    @wraps(fn)
    def _method_decorator(fn):
        def getter_wrapper(self):
            db = Database()
            db.save(self._student_id, self._student_name)
            return fn(self)
        return getter_wrapper
    
    def save_student_to_db(cls):
        cls.get_student_id = _method_decorator(cls.get_student_id)
        cls.get_student_name = _method_decorator(cls.get_student_name)
        return cls
    

    About the database, you can instantiate it every time its needed like I proposed above or have a dependency injection framework do it for you. I've been using injectable for a while and it is quite simple and yet powerful, there is serum too.