views:

53

answers:

2

I am trying to decorate an actual class, using this code:

def my_decorator(cls):
    def wrap(*args, **kw):
        return object.__new__(cls)
    return wrap

@my_decorator
class TestClass(object):
    def __init__(self):
        print "__init__ should run if object.__new__ correctly returns an instance of cls"


test = TestClass() # shouldn't TestClass.__init__() be run here?

I get no errors, but I also don't see the message from TestClass.__init__().

According to the docs for new-style classes:

Typical implementations create a new instance of the class by invoking the superclass’s __new__() method using super(currentclass, cls).__new__(cls[, ...]) with appropriate arguments and then modifying the newly-created instance as necessary before returning it.

If __new__() returns an instance of cls, then the new instance’s __init__() method will be invoked like __init__(self[, ...]), where self is the new instance and the remaining arguments are the same as were passed to __new__().

Any ideas why __init__ isn't running?

Also, I have tried to call __new__ like this:

return super(cls.__bases__[0], cls).__new__(cls)

but it would return a TypeError:

TypeError: super.__new__(TestClass): TestClass is not a subtype of super
+5  A: 

__init__ isn't running because object.__new__ doesn't know to call it. If you change it to cls.__call__(*args, **kwargs), or better, cls(*args, **kwargs), it should work. Remember that a class is a callable: calling it produces a new instance. Just calling __new__ returns an instance but doesn't go through the initialization. An alternative would be to call __new__ and then manually call __init__ but this is just replacing the logic that is already embodied in __call__.

The documentation that you quote is referring to calling super from within the __new__ method of the class. Here, you are calling it from the outside and not in the usual way as I've already discussed.

aaronasterling
for some reason I thought the act of creating an instance would fire off the `__init__`. Thanks for the clarification!
W_P
A: 

Couldn't tell you the reason but this hack does run __init__

def my_decorator(cls):
    print "In my_decorator()"
    def wrap(*args, **kw):
        print "In wrap()"
        return cls.__init__(object.__new__(cls), *args, **kw)
    return wrap

@my_decorator
class TestClass(object):
    def __init__(self):
        print "__init__ should run if object.__new__ correctly returns an instance of cls"
Gregory