Can __new__ prevent __init__ from being called?

Collapse
This topic is closed.
X
X
 
  • Time
  • Show
Clear All
new posts
  • Felix Wiemann

    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):
    [color=blue][color=green][color=darkred]
    >>> class C(object):[/color][/color][/color]
    .... 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.'
    ....[color=blue][color=green][color=darkred]
    >>> C()[/color][/color][/color]
    Creating instance.
    Created.
    In init.
    <__main__.C object at 0x4062526c>[color=blue][color=green][color=darkred]
    >>> C()[/color][/color][/color]
    In init. <---------- Here I want __init__ not to be executed.
    <__main__.C object at 0x4062526c>[color=blue][color=green][color=darkred]
    >>>[/color][/color][/color]

    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.initialize d: 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/
  • Steven Bethard

    #2
    Re: Can __new__ prevent __init__ from being called?

    Felix Wiemann wrote:[color=blue]
    > 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.
    >[/color]
    [snip][color=blue]
    >
    > How can I prevent __init__ from being called on the already-initialized
    > object?[/color]

    The short answer: you can't:

    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__(t ype):
    .... 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__(t ype):
    .... 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 SingletonMetacl ass(type):
    .... def __call__(cls, *args, **kwargs):
    .... try:
    .... return cls.__instance_ _
    .... except AttributeError:
    .... cls.__instance_ _ = cls.__new__(cls , *args, **kwargs)
    .... cls.__instance_ _.__init__(*arg s, **kwargs)
    .... return cls.__instance_ _
    ....
    py> class C(object):
    .... __metaclass__ = SingletonMetacl ass
    .... 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

    Comment

    • Peter Hansen

      #3
      Re: Can __new__ prevent __init__ from being called?

      Felix Wiemann wrote:[color=blue]
      > 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):
      >[/color]
      [snip][color=blue]
      > How can I prevent __init__ from being called on the already-initialized
      > object?[/color]

      Is this an acceptable kludge?
      [color=blue][color=green][color=darkred]
      >>> class C(object):[/color][/color][/color]
      .... 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'
      ....[color=blue][color=green][color=darkred]
      >>> a = C()[/color][/color][/color]
      creating
      in init[color=blue][color=green][color=darkred]
      >>> b = C()
      >>>[/color][/color][/color]

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

      -Peter

      Comment

      • Michael Spencer

        #4
        Re: Can __new__ prevent __init__ from being called?

        Peter Hansen wrote:[color=blue]
        > Felix Wiemann wrote:
        >[color=green]
        >> 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):
        >>[/color]
        > [snip]
        >[color=green]
        >> How can I prevent __init__ from being called on the already-initialized
        >> object?[/color]
        >
        >
        > Is this an acceptable kludge?
        >[color=green][color=darkred]
        > >>> class C(object):[/color][/color]
        > ... 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'
        > ...[color=green][color=darkred]
        > >>> a = C()[/color][/color]
        > creating
        > in init[color=green][color=darkred]
        > >>> b = C()
        > >>>[/color][/color]
        >
        > (Translation: dynamically override now-useless __init__ method.
        > But if that works, why do you need __init__ in the first place?)
        >
        > -Peter[/color]
        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.'
        [color=blue][color=green][color=darkred]
        >>> c = C.new()[/color][/color][/color]
        Creating instance.
        Created.
        In init.[color=blue][color=green][color=darkred]
        >>> c = C.new()
        >>>[/color][/color][/color]

        Michael

        Comment

        • Felix Wiemann

          #5
          Re: Can __new__ prevent __init__ from being called?

          Steven Bethard wrote:
          [color=blue]
          > Felix Wiemann wrote:
          >[color=green]
          >> How can I prevent __init__ from being called on the
          >> already-initialized object?[/color]
          >
          > The short answer: you can't:
          > http://www.python.org/2.2.3/descrintro.html#__new__[/color]

          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?
          [color=blue][color=green][color=darkred]
          >>> class A(object):[/color][/color][/color]
          .... def __init__(self):
          .... print 'Init of A.'
          ....[color=blue][color=green][color=darkred]
          >>> instance = A()[/color][/color][/color]
          Init of A.[color=blue][color=green][color=darkred]
          >>> class B(object):[/color][/color][/color]
          .... def __new__(self):
          .... return instance
          .... def __init__(self):
          .... print 'Init of B.'
          ....[color=blue][color=green][color=darkred]
          >>> B() # <--------- A's __init__ is *not* called.[/color][/color][/color]
          <__main__.A object at 0x4062424c>[color=blue][color=green][color=darkred]
          >>> instance = object.__new__( B)
          >>> B() # <--------- B's __init__ is called[/color][/color][/color]
          Init of B.
          <__main__.B object at 0x406243ec>

          So there seems to be some type-checking in type.__call__.
          [color=blue]
          > Note that in the Singleton example there, subclasses are told to
          > override init, not __init__ for exactly this reason.[/color]

          I see.
          [color=blue]
          > py> class C(object):
          > ... class __metaclass__(t ype):
          > ... 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[/color]

          I didn't think of inlining the metaclass; that's really nice.
          [color=blue]
          > [...] where all the work is done in the metaclass and you don't even
          > define __new__.[/color]

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

          Thanks a lot!

          --
          Felix Wiemann -- http://www.ososo.de/

          Comment

          • Jack Diederich

            #6
            Re: Can __new__ prevent __init__ from being called?

            On Tue, Feb 15, 2005 at 10:30:21PM +0100, Felix Wiemann wrote:[color=blue]
            > 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):
            >[color=green][color=darkred]
            > >>> class C(object):[/color][/color]
            > ... 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.'
            > ...[/color]
            <snip>[color=blue]
            > How can I prevent __init__ from being called on the already-initialized
            > object?[/color]
            <snip>[color=blue]
            > Is there an easier way than using a metaclass and writing a custom
            > __call__ method?[/color]

            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.Ap pContext):
            """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.in it(*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.AppCont ext.__init__(se lf, req)
            # lots of initialization done here
            return

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

            Hope that helps,

            -Jack

            Comment

            • Colin J. Williams

              #7
              Re: Can __new__ prevent __init__ from being called?

              Felix Wiemann wrote:[color=blue]
              > Steven Bethard wrote:
              >
              >[color=green]
              >>Felix Wiemann wrote:
              >>
              >>[color=darkred]
              >>>How can I prevent __init__ from being called on the
              >>>already-initialized object?[/color]
              >>
              >>The short answer: you can't:
              >> http://www.python.org/2.2.3/descrintro.html#__new__[/color]
              >[/color]
              [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.[color=blue]
              >[/color]

              Comment

              • Steven Bethard

                #8
                Re: Can __new__ prevent __init__ from being called?

                Colin J. Williams wrote:[color=blue]
                > 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.[/color]

                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 PartlyMutableTu ple(tuple):
                .... def __new__(cls, *args, **kwargs):
                .... return super(PartlyMut ableTuple, cls).__new__(cl s, args)
                .... def __init__(self, *args, **kwargs):
                .... self.__dict__.u pdate(kwargs)
                ....
                py> t = PartlyMutableTu ple(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 "<interacti ve 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

                Comment

                • Martin

                  #9
                  Re: Can __new__ prevent __init__ from being called?

                  Felix Wiemann wrote:[color=blue]
                  > 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.[/color]

                  [snip]
                  [color=blue]
                  > How can I prevent __init__ from being called on the[/color]
                  already-initialized[color=blue]
                  > object?
                  >
                  > I do not want to have any code in the __init__ method which checks if
                  > the instance is already initialized (like "if self.initialize d:[/color]
                  return"[color=blue]
                  > 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?[/color]

                  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__.ge t("__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__.'
                  [color=blue][color=green][color=darkred]
                  >>> C()[/color][/color][/color]
                  In C init.
                  In C __init__.
                  [color=blue][color=green][color=darkred]
                  >>> C()[/color][/color][/color]
                  In C __init__.
                  [color=blue][color=green][color=darkred]
                  >>> D()[/color][/color][/color]
                  In D init.
                  In D __init__.
                  [color=blue][color=green][color=darkred]
                  >>> D()[/color][/color][/color]
                  In D __init__.

                  -Martin

                  Comment

                  • Martin

                    #10
                    Re: Can __new__ prevent __init__ from being called?

                    I meant to say:

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

                    not:
                    [color=blue]
                    > Although the base class __init__ does have to check to see if the
                    > instance is initialized, ...[/color]

                    Comment

                    Working...