1

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

Somesh Garje
  • 105
  • 11
  • This is what `property` (an application of decorators) is for. – chepner Apr 13 '20 at 13:08
  • You may also want to look into using an ORM (like SqlAlchemy) if you want to persist objects in memory to a database. – chepner Apr 13 '20 at 13:09
  • Thanks very much for your advice. I will have a look at the property function – Somesh Garje Apr 14 '20 at 04:25
  • @chepner seems `property` requires to be implemented within a class to create setters or getters or deleters of a specific function. Can i use it in a decorator to dynamically decorate multiple classes? – Somesh Garje Apr 15 '20 at 10:16

1 Answers1

1

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.

beatsme
  • 91
  • 5
  • Thanks very much for this suggestion. All my classes have different methods and attributes. I was looking to make something generic for all the classes – Somesh Garje Apr 14 '20 at 04:24
  • After getting list of all class method names and do `cls.{method_name} = _method_decorator(cls.{method_name})` ? I can get method names like this `methods_list = [prop for prop in cls.__dict__.keys() if not prop.startswith('_')]` – Somesh Garje Apr 15 '20 at 09:53
  • @SomeshGarje then maybe you want to refer to this answer: https://stackoverflow.com/q/6307761/13298288 – beatsme May 05 '20 at 19:27