[BangPypers] Class decorator to capture the creation and deletion of objects

Sangeeth Saravanaraj sangeeth.saravanaraj at gmail.com
Tue Feb 25 01:49:51 CET 2014


This question was initially asked in tutor at python.org; Now I am widening
the audience to gain attention.

I want to create a decorator which should do the following things:
 => When an object of the decorated class is created, the objects name (say
the value of the incoming "id" argument) should be stored as a record in a
table in a database.
 => When an object of the decorated class is deleted, the record with this
deleted objects name (i.e. object.id) should be removed from the table.

Now, for example - consider the following snippet:

@saveme
class A(object):
    def __init__(self, id):
        self.id = id

@saveme
class B(object):
    def __init__(self, id):
        self.id = id

"saveme" should do what I have explained earlier.

a1 = A("A1")
a2 = A("A2")
a3 = A("A3")
b1 = B("B1")
b2 = B("B2")

At this point if I query and print all the records in a table, I should get
the following output:
["A1", "A2", "A3", "B1", "B2"]

del a1
del a2
del a3
del b1
del b2

At this point, all entries in the table should be deleted; query should
return an empty list!

And, I want to highlight that the classes that are being decorated with
"saveme" can de derived classes too [which initialises its base classes
using super() method]!

Now the following is what I have tried:

class saveme(object):
    def __init__(self, klass):
        print "saveme::__init__()"
        self._klass = klass

    def __call__(self, *args, **kwargs):
        print "saveme::__call__()"
        obj = self._klass(*args, **kwargs)
        # creation of DB record will happen here!
        # i.e. something like add_to_db(kwargs.get("id"))
        return obj

    def __del__(self):
        # deletion of DB record will happen here!
        # i.e. something like remove_from_db(id)
        # TODO: how to retrieve the "id" here?!
        print "saveme::__del__()"


class Parent1(object):
    def __init__(self):
        print "Parent1:: __init__()"
        super(Parent1, self).__init__()


class Parent2(object):
    def __init__(self):
        print "Parent2:: __init__()"
        super(Parent2, self).__init__()


@saveme
class A(Parent1, Parent2):
    def __init__(self, id):
        print "A::__init__()"
        self.id = id
        #super(A, self).__init__()


#@saveme
#class B(object):
#    def __init__(self, id):
#        print "B::__init__()"
#        self.id = id


def main():
    a1 = A(id="A1")
#    b1 = B(id="B1")

if __name__ == "__main__":
    main()


When executed the above, I ran in to the following:

saveme::__init__()
saveme::__call__()
A::__init__()
Traceback (most recent call last):
  File "1.py", line 54, in <module>
    main()
  File "1.py", line 50, in main
    a1 = A(id="A1")
  File "1.py", line 10, in __call__
    obj = self._klass(*args, **kwargs)
  File "1.py", line 39, in __init__
    super(A, self).__init__()
TypeError: must be type, not saveme
saveme::__del__()


When I commented "super(A, self).__init__()" in the class A :: __init__()
method, it returned an object of type A and I was able to see the prints in
the __call__ and __del__ methods but the __init__() methods of the base
classes (Parent1 & Parent2) were not called!

>From the error message, what I could understand is - the object returned by
saveme::__call__() is not of type A but of type saveme. But when I put a
print in the saveme::__call__() I could see it returns an object of type A
and not saveme.

Now the question is - with this approach to capture the initiation and
deletion events of an object, how do I initialise the base classes using
super()?

Or, is there any other better way to capture the __call__ and __del__
 events for an object of a certain class - if so, how?!

Thank you,

Sangeeth


PS:
http://stackoverflow.com/questions/21826854/typeerror-when-using-super-method-with-class-decorator-for-a-derived-class


More information about the BangPypers mailing list