472,143 Members | 1,178 Online
Bytes | Software Development & Data Engineering Community
Post +

Home Posts Topics Members FAQ

Join Bytes to post your question to a community of 472,143 software developers and data experts.

Can __new__ prevent __init__ from being called?

Sometimes (but not always) the __new__ method of one of my classes
returns an *existing* instance of the class. However, when it does
that, the __init__ method of the existing instance is called
nonetheless, so that the instance is initialized a second time. For
example, please consider the following class (a singleton in this case):
class C(object): .... instance = None
.... def __new__(cls):
.... if C.instance is None:
.... print 'Creating instance.'
.... C.instance = object.__new__(cls)
.... print 'Created.'
.... return cls.instance
.... def __init__(self):
.... print 'In init.'
.... C() Creating instance.
Created.
In init.
<__main__.C object at 0x4062526c> C() In init. <---------- Here I want __init__ not to be executed.
<__main__.C object at 0x4062526c>


How can I prevent __init__ from being called on the already-initialized
object?

I do not want to have any code in the __init__ method which checks if
the instance is already initialized (like "if self.initialized: return"
at the beginning) because that would mean I'd have to insert this
checking code in the __init__ method of every subclass.

Is there an easier way than using a metaclass and writing a custom
__call__ method?

--
Felix Wiemann -- http://www.ososo.de/
Jul 18 '05 #1
9 5723
Felix Wiemann wrote:
Sometimes (but not always) the __new__ method of one of my classes
returns an *existing* instance of the class. However, when it does
that, the __init__ method of the existing instance is called
nonetheless, so that the instance is initialized a second time.
[snip]
How can I prevent __init__ from being called on the already-initialized
object?


The short answer: you can't:
http://www.python.org/2.2.3/descrintro.html#__new__
Note that in the Singleton example there, subclasses are told to
override init, not __init__ for exactly this reason.

If it's okay that __init__ never be called, you could do something like:

py> class C(object):
.... class __metaclass__(type):
.... def __call__(cls, *args, **kwargs):
.... return cls.__new__(cls, *args, **kwargs)
.... instance = None
.... def __new__(cls):
.... if cls.instance is None:
.... print 'Creating instance'
.... cls.instance = object.__new__(cls)
.... print 'Created'
.... return cls.instance
.... def __init__(self):
.... print 'In init'
....
py> C()
Creating instance
Created
<__main__.C object at 0x011F2E30>
py> C()
<__main__.C object at 0x011F2E30>

If __init__ needs to be called, I might go with something like:

py> class C(object):
.... class __metaclass__(type):
.... def __call__(cls, *args, **kwargs):
.... if cls.instance is None:
.... print 'Creating instance'
.... cls.instance = cls.__new__(cls, *args, **kwargs)
.... print 'Created'
.... cls.instance.__init__(*args, **kwargs)
.... return cls.instance
.... instance = None
.... def __init__(self):
.... print 'In init'
....
py> C()
Creating instance
Created
In init
<__main__.C object at 0x011F2E50>
py> C()
<__main__.C object at 0x011F2E50>

where all the work is done in the metaclass and you don't even define
__new__. I would probably also create the 'instance' attribute as part
of the metaclass work, like:

py> class SingletonMetaclass(type):
.... def __call__(cls, *args, **kwargs):
.... try:
.... return cls.__instance__
.... except AttributeError:
.... cls.__instance__ = cls.__new__(cls, *args, **kwargs)
.... cls.__instance__.__init__(*args, **kwargs)
.... return cls.__instance__
....
py> class C(object):
.... __metaclass__ = SingletonMetaclass
.... def __init__(self):
.... print '__init__'
....
py> C()
__init__
<__main__.C object at 0x011F3210>
py> C()
<__main__.C object at 0x011F3210>
py> C() is C()
True

But none of these solutions is particularly simple... Sorry!

STeVe
Jul 18 '05 #2
Felix Wiemann wrote:
Sometimes (but not always) the __new__ method of one of my classes
returns an *existing* instance of the class. However, when it does
that, the __init__ method of the existing instance is called
nonetheless, so that the instance is initialized a second time. For
example, please consider the following class (a singleton in this case):
[snip] How can I prevent __init__ from being called on the already-initialized
object?


Is this an acceptable kludge?
class C(object): .... instance=None
.... def __new__(cls):
.... if C.instance is None:
.... print 'creating'
.... C.instance = object.__new__(cls)
.... else:
.... cls.__init__ = lambda self: None
.... return cls.instance
.... def __init__(self):
.... print 'in init'
.... a = C() creating
in init b = C()


(Translation: dynamically override now-useless __init__ method.
But if that works, why do you need __init__ in the first place?)

-Peter
Jul 18 '05 #3
Peter Hansen wrote:
Felix Wiemann wrote:
Sometimes (but not always) the __new__ method of one of my classes
returns an *existing* instance of the class. However, when it does
that, the __init__ method of the existing instance is called
nonetheless, so that the instance is initialized a second time. For
example, please consider the following class (a singleton in this case):

[snip]
How can I prevent __init__ from being called on the already-initialized
object?

Is this an acceptable kludge?
>>> class C(object): ... instance=None
... def __new__(cls):
... if C.instance is None:
... print 'creating'
... C.instance = object.__new__(cls)
... else:
... cls.__init__ = lambda self: None
... return cls.instance
... def __init__(self):
... print 'in init'
... >>> a = C() creating
in init >>> b = C()
>>>
(Translation: dynamically override now-useless __init__ method.
But if that works, why do you need __init__ in the first place?)

-Peter

Or this one: use an alternative constructor:

class C(object):
instance = None
@classmethod
def new(cls, *args, **kw):
if cls.instance is None:
print 'Creating instance.'
cls.instance = object.__new__(cls)
print 'Created.'
cls.instance.__init__(*args,**kw)
return cls.instance
def __init__(self):
print 'In init.'
c = C.new() Creating instance.
Created.
In init. c = C.new()


Michael

Jul 18 '05 #4
Steven Bethard wrote:
Felix Wiemann wrote:
How can I prevent __init__ from being called on the
already-initialized object?
The short answer: you can't:
http://www.python.org/2.2.3/descrintro.html#__new__


What a pity. By the way, I'm just seeing that the web page says:

| If you return an existing object, the constructor call will still call
| its __init__ method. If you return an object of a different class, its
| __init__ method will be called.

However, the latter doesn't seem to be true, or am I missing something?
class A(object): .... def __init__(self):
.... print 'Init of A.'
.... instance = A() Init of A. class B(object): .... def __new__(self):
.... return instance
.... def __init__(self):
.... print 'Init of B.'
.... B() # <--------- A's __init__ is *not* called. <__main__.A object at 0x4062424c> instance = object.__new__(B)
B() # <--------- B's __init__ is called

Init of B.
<__main__.B object at 0x406243ec>

So there seems to be some type-checking in type.__call__.
Note that in the Singleton example there, subclasses are told to
override init, not __init__ for exactly this reason.
I see.
py> class C(object):
... class __metaclass__(type):
... def __call__(cls, *args, **kwargs):
... if cls.instance is None:
... print 'Creating instance'
... cls.instance = cls.__new__(cls, *args, **kwargs)
... print 'Created'
... cls.instance.__init__(*args, **kwargs)
... return cls.instance
I didn't think of inlining the metaclass; that's really nice.
[...] where all the work is done in the metaclass and you don't even
define __new__.


Yeah, that's good. I think I'll go that way.

Thanks a lot!

--
Felix Wiemann -- http://www.ososo.de/
Jul 18 '05 #5
On Tue, Feb 15, 2005 at 10:30:21PM +0100, Felix Wiemann wrote:
Sometimes (but not always) the __new__ method of one of my classes
returns an *existing* instance of the class. However, when it does
that, the __init__ method of the existing instance is called
nonetheless, so that the instance is initialized a second time. For
example, please consider the following class (a singleton in this case):
class C(object):
... instance = None
... def __new__(cls):
... if C.instance is None:
... print 'Creating instance.'
... C.instance = object.__new__(cls)
... print 'Created.'
... return cls.instance
... def __init__(self):
... print 'In init.'
...

<snip> How can I prevent __init__ from being called on the already-initialized
object? <snip> Is there an easier way than using a metaclass and writing a custom
__call__ method?


The standard recipe is to define an alternate init method and call it
once when you instantiate the object (I couldn't find it on ASPN though).

Here is a cut-n-paste from production code. The work normally done in
the magic __init__() is done in init() instead.

class Page(context.AppContext):
"""the One True Singleton """
_single = None # our singleton reference
def __new__(cls, *args, **opts):
if (Page._single is None):
Page._single = object.__new__(cls)
Page._single.init(*args, **opts)
return Page._single

def __init__(self, *args, **opts):
"""We are a singleton, so setup is done just once in init() because
__init__() will be called every time the singleton is re-issued
This __init__ just prevents our parent's __init__'s from running other
than when told to by our init()
"""
return

def init(self, req = None):
"""setup Db objects, cgi params etc
Here is also where we decide if we are being run from the command
line or in mod_python"""
context.AppContext.__init__(self, req)
# lots of initialization done here
return

Page is a singleton but it inherits from the class context.AppContext
which is just a regular class. The empty Page.__init__ doesn't call
the context.AppContext.__init__ but the once-only Page.init does.

Hope that helps,

-Jack
Jul 18 '05 #6
Felix Wiemann wrote:
Steven Bethard wrote:

Felix Wiemann wrote:

How can I prevent __init__ from being called on the
already-initialized object?
The short answer: you can't:
http://www.python.org/2.2.3/descrintro.html#__new__

[snip]
This prompts a similar query. __new__ appears to be intended for
immutable objects but it seems to be called as part of constructor
process for all instances.

Regarding the original question. It is not possible to prevent the use
of __init__ but is it possible to prevent __init__ having any effect by
setting a flag when __init__ is called for the first creation of the
instance.

Colin W.

Jul 18 '05 #7
Colin J. Williams wrote:
This prompts a similar query. __new__ appears to be intended for
immutable objects but it seems to be called as part of constructor
process for all instances.


That's because Python has no builtin way of determining whether or not a
given type is immutable. If you wanted to, you could define both
__new__ and __init__, the first to set immutable parts and the second to
set mutable parts, e.g.:

py> class PartlyMutableTuple(tuple):
.... def __new__(cls, *args, **kwargs):
.... return super(PartlyMutableTuple, cls).__new__(cls, args)
.... def __init__(self, *args, **kwargs):
.... self.__dict__.update(kwargs)
....
py> t = PartlyMutableTuple(1, 2, 3, a=4, b=5)
py> t
(1, 2, 3)
py> t.a, t.b
(4, 5)
py> t.a, t.b = t.b, t.a
py> t.a, t.b
(5, 4)
py> t[0] = 2
Traceback (most recent call last):
File "<interactive input>", line 1, in ?
TypeError: object does not support item assignment

I don't think I'd advise this strategy, but by always calling both
__new__ and __init__, Python makes it possible...

Steve
Jul 18 '05 #8
Felix Wiemann wrote:
Sometimes (but not always) the __new__ method of one of my classes
returns an *existing* instance of the class. However, when it does
that, the __init__ method of the existing instance is called
nonetheless, so that the instance is initialized a second time.
[snip]
How can I prevent __init__ from being called on the already-initialized object?

I do not want to have any code in the __init__ method which checks if
the instance is already initialized (like "if self.initialized: return" at the beginning) because that would mean I'd have to insert this
checking code in the __init__ method of every subclass.

Is there an easier way than using a metaclass and writing a custom
__call__ method?


Yes. You could move all your initalization logic from __init__ to a
separate init method and use the following simple recipe that does not
involve metaclasses and overriding __call__.

Although the base class __init__ does have to check to see if the
instance is initialized, you don't have to repeat the code in derived
classes:

class C(object):
def __new__(cls, *args, **kwds):
it = cls.__dict__.get("__it__")
if it is not None:
return it
cls.__it__ = it = object.__new__(cls)
it.init(*args, **kwds)
return it

def init(self, *args, **kwds): # only called once
print 'In C init.'
pass

def __init__(self): # called each time
print 'In C __init__.'

class D(C):
def init(self, *args, **kwds): # only called once
print 'In D init.'
pass

def __init__(self): # called each time
print 'In D __init__.'
C() In C init.
In C __init__.
C() In C __init__.
D() In D init.
In D __init__.
D()

In D __init__.

-Martin

Jul 18 '05 #9
I meant to say:

Although the base class __new__ does have to check to see if the
^^^^^^^
instance is initialized, ...

not:
Although the base class __init__ does have to check to see if the
instance is initialized, ...


Jul 18 '05 #10

This discussion thread is closed

Replies have been disabled for this discussion.

Similar topics

2 posts views Thread by Uwe Mayer | last post: by
3 posts views Thread by Christoph Groth | last post: by
3 posts views Thread by James Stroud | last post: by
5 posts views Thread by Ken Schutte | last post: by
1 post views Thread by Frank Benkstein | last post: by
5 posts views Thread by Sandra-24 | last post: by
4 posts views Thread by Steven D'Aprano | last post: by
3 posts views Thread by Torsten Mohr | last post: by
reply views Thread by leo001 | last post: by

By using Bytes.com and it's services, you agree to our Privacy Policy and Terms of Use.

To disable or enable advertisements and analytics tracking please visit the manage ads & tracking page.