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

Alex Martelli aleax at google.com
Tue Feb 25 18:44:08 CET 2014


The best way to avoid relying on __del__ in this particular case may be to
hold, rather than a plain list of the IDs, a list of weak references to
instances.  When the "list of IDs" is asked for, it goes through a property
that cleans up those weak refs that correspond to now-disappeared
instances, before returning the requested list of IDs.  Now __del__ can
usefully be removed (as its existence may interfere with GC of cyclical
garbage), yet the list of IDs is guaranteed to return the list of
still-existing instances' IDs, no matter what constructs or idioms have
been used to create and remove instances.

This avoid relying on creators/destroyers of instances properly calling
release and/or using the appropriate context manager; such reliance is
sometimes inevitable, but for this particular spec it does not appear to
be, thus, it might best be avoided.


Alex



On Mon, Feb 24, 2014 at 7:46 PM, David Lawrence <david at bitcasa.com> wrote:

> If as according to the docs, there is no guarantee of __del__ being
> called, anything that relies on that seems unsafe (depending on how robust
> one needs the solutions to be).  Solutions I've seen elsewhere suggest
> creating something like a *release()* method on your objects and calling
> it explicitly before calling *del my_obj *(if you even call del, rather
> than just relying on GC).
>
> Alternatively, if it suits the scenario, use a context manager.
>
> All suggestions lifted from (a SO that informed me in the past):
> http://stackoverflow.com/questions/6104535/i-dont-understand-this-python-del-behaviour
>
>
> On Mon, Feb 24, 2014 at 6:24 PM, Alex Martelli <aleax at google.com> wrote:
>
>> Off the cuff, I'd make saveme into a function, not a class;
>> the saveme function would alter the class passed in as its only argument
>> (saving __init__ and/or __del__ methods it may have and replacing them with
>> other -- nested -- functions that call them and do the rest of the job) and
>> return the same class object it received.
>>
>> No time to actually write the code but this seems a much sounder
>> architecture.
>>
>>
>> Alex
>>
>>
>>
>>  On Mon, Feb 24, 2014 at 4:49 PM, Sangeeth Saravanaraj <
>> sangeeth.saravanaraj at gmail.com> wrote:
>>
>>>  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
>>>
>>>
>>> _______________________________________________
>>> Baypiggies mailing list
>>> Baypiggies at python.org
>>> To change your subscription options or unsubscribe:
>>> https://mail.python.org/mailman/listinfo/baypiggies
>>>
>>
>>
>> _______________________________________________
>> Baypiggies mailing list
>> Baypiggies at python.org
>> To change your subscription options or unsubscribe:
>> https://mail.python.org/mailman/listinfo/baypiggies
>>
>
>
-------------- next part --------------
An HTML attachment was scrubbed...
URL: <http://mail.python.org/pipermail/python-list/attachments/20140225/7bc9e060/attachment.html>


More information about the Python-list mailing list