[Tutor] Class decorator on a derived class not initialising the base classes using super - TypeError
Peter Otten
__peter__ at web.de
Mon Feb 24 21:09:06 CET 2014
Sangeeth Saravanaraj wrote:
> On Mon, Feb 24, 2014 at 10:53 PM, Peter Otten <__peter__ at web.de> wrote:
>
>> Sangeeth Saravanaraj wrote:
>>
>> > I am trying to capture an object initiation and deletion events using
>> > the __call__() and __del__() methods with the following approach.
>>
>> Note that there is no guarantee that __dell__ will ever be called.
>> Usually it is better to introduce a weakref with callback.
>>
>> > class A(object):
>> > def __init__(self, klass):
>> > print "A::__init__()"
>> > self._klass = klass
>> >
>> > def __call__(self):
>> > print "A::__call__()"
>> > return self._klass()
>> >
>> > def __del__(self):
>> > print "A::__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__()
>> >
>> > @A
>> > class B(Parent1, Parent2):
>> > def __init__(self):
>> > print "B::__init__()"
>> > super(B, self).__init__()
>> >
>> > def main():
>> > b = B()
>> >
>> > if __name__ == "__main__":
>> > main()
>> >
>> >
>> > I decorate a class, say class B (whose object initiation and deletion I
>> > wanted to capture) with a decorator class A. Please note that the class
>> > B is derived from two classes - Parent1 & Parent2 and I want to use
>> > super() method to initialise the parent classes.
>> >
>> > When I executed the above code snippet, I ran into the following issue:
>> >
>> >
>> > A::__init__()
>> > A::__call__()
>> > B::__init__()
>> > Traceback (most recent call last):
>> > File "so.py", line 40, in <module>
>> > main()
>> > File "so.py", line 36, in main
>> > b = B()
>> > File "so.py", line 10, in __call__
>> > return self._klass()
>> > File "so.py", line 32, in __init__
>> > super(B, self).__init__()
>> > TypeError: must be type, not A
>> > A::__del__()
>> >
>> >
>> > When I commented "super(B, self).__init__()" in the class B ::
>> > __init__() method, it returned an object of type B and I was able to
>> > see the prints in the __call__ and __del__ methods but the __init__()
>> > methods of the
>> base
>> > classes (Parent1 & Parent2) are not called!
>> >
>> > From the error message, what I could understand is - the object
>> > returned by A::__call__() is not of type B but of type A. But when I
>> > put a print
>> in
>> > the A::__call__() I could see it returns an object of type B and not A.
>> >
>> > 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()?
>>
>> You'd have to introduce a naming convention or rewrite your class to be
>> aware of the wrapping in some way:
>>
>> @A
>> class B(Parent1, Parent2):
>> def __init__(self):
>> print "B::__init__()"
>> super(B._klass, self).__init__()
>>
>> Not pretty.
>>
>> > Or, is there any other better way to capture the __call__ and __del__
>> > events for an object of a certain class - if so, how?!
>>
>> Most certainly, but you have to give some details about what you are up
>> to first.
>>
>
> Sorry, I should have described what I was trying!
>
> 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.
>
> You can safely assume that all the database operations are working fine!
>
> 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!
>
> What is the best way to do this?!
I'm sorry, after a bit of try-and-error I could not come up with a good way
to write such a decorator. My best effort so far uses inheritance:
import itertools
import weakref
_registry = weakref.WeakValueDictionary()
_next_id = lambda count=itertools.count(): next(count)
def show():
print(list(_registry.values()))
class Registered(object):
def __init__(self, id=None):
if id is None:
id = _next_id()
self.id = id
_registry[id] = self
def __repr__(self):
return "{}(id={!r})".format(self.__class__.__name__, self.id)
class A(Registered):
pass
class B(Registered):
pass
class C(B):
pass
a1 = A()
b1 = B()
c1 = C()
show()
del a1
show()
b2 = B()
show()
del b1
del c1
del b2
show()
More information about the Tutor
mailing list