Modify arguments between __new__ and __init__

Steven Bethard steven.bethard at gmail.com
Sun Dec 23 01:01:50 EST 2007


Steven D'Aprano wrote:
> When you call a new-style class, the __new__ method is called with the 
> user-supplied arguments, followed by the __init__ method with the same 
> arguments.
> 
> I would like to modify the arguments after the __new__ method is called 
> but before the __init__ method, somewhat like this:
> 
> 
>>>> class Spam(object):
> ...     def __new__(cls, *args):
> ...             print "__new__", args
> ...             x = object.__new__(cls)
> ...             args = ['spam spam spam']
> ...             return x
> ...     def __init__(self, *args):
> ...             print "__init__", args  # hope to get 'spam spam spam'
> ...             return None
> 
> but naturally it doesn't work:
> 
>>>> s = Spam('spam and eggs', 'tomato', 'beans are off')
> __new__ ('spam and eggs', 'tomato', 'beans are off')
> __init__ ('spam and eggs', 'tomato', 'beans are off')
> 
> Is there any way to do this, or am I all outta luck?

You can really only achieve this by writing a metaclass.  When a new 
object is created, what's first called is the __call__ method of the 
type object.  This basically looks like::

     def __call__(cls, *args, **kwargs):
         obj = cls.__new__(cls, *args, **kwargs)
         obj.__init__(*args, **kwargs)
         return obj

Hopefully that explains the behavior you're seeing.  If you want 
different behavior than this, you can change __call__ by defining your 
own metaclass with a different __call__ method, for example::

     >>> class SpamMeta(type):
     ...     def __call__(cls, *args, **kwargs):
     ...         obj = cls.__new__(cls)
     ...         obj.__init__('spam spam spam')
     ...         return obj
     ...
     >>> class Spam(object):
     ...     __metaclass__ = SpamMeta
     ...     def __new__(cls, *args):
     ...         print '__new__', args
     ...         return object.__new__(cls)
     ...     def __init__(self, *args):
     ...         print '__init__', args
     ...
     >>> Spam()
     __new__ ()
     __init__ ('spam spam spam',)
     <__main__.Spam object at 0x00E756F0>

Hope that helps,

STeVe



More information about the Python-list mailing list