Copy constructors

Roeland Rengelink r.b.rigilink at chello.nl
Mon Aug 13 02:44:51 EDT 2001


Guido van Rossum wrote:
> 
> Roeland Rengelink <r.b.rigilink at chello.nl> writes:
> 
> > One idiom where I use __class__ assignment is the following
> >
> > class State1:
> >     def do_something(self):
> >         ...do something...
> >     def change_state(self):
> >         self.__class__ = State2
> > class State2:
> >     def do_something(self):
> >         ...do something else...
> >     def change_state(self):
> >         self.__class__ = State1
> 
> But you can easily do this differently -- e.g. you could do a method
> assignment, or you could represent the state by an object.
> 

Sure, and if...else worked fine too ;)

I have only two arguments for using this solution, in favor of the
others. First, I think class assignment expresses the intent of the code
rather elegantly. Second, --and this is an even weaker argument-- it was
the most efficient solution in my particular application. (speed-wise
when compared with if..else or method-dispatch to a state-object,
memory-wise when compared to method assignments to all instances)

I am, of course, not qualified to make the trade-off between these
benefits, and the cost of making class-assignment available.

> > Speaking of __new__. Would it be an idea to give __new__() the
> > responsibility for calling __init__ on the new instance.
> 
> I have thought about this.  It would be less flexible, so I think not.
> For example, with the current set-up you can use __new__ to create an
> uninitialized instance, bypassing __init__.  If __new__ called
> __init__, you couldn't do that.
> 

I didn't mean to let object.__new__ call the init, which indeed looses
flexibility
What I did mean is the following change to type_call. I Have no idea
what side effects
I may have missed here, but it does exactly what I want.

staticforward  PyObject *
slot_tp_new(PyTypeObject *type, PyObject *args, PyObject *kwds);

static PyObject *
type_call(PyTypeObject *type, PyObject *args, PyObject *kwds)
{
        PyObject *obj;

        if (type->tp_new == NULL) {
                PyErr_Format(PyExc_TypeError,
                             "cannot create '%.100s' instances",
                             type->tp_name);
                return NULL;
        }

        obj = type->tp_new(type, args, NULL);
        if (obj != NULL) {
                type = obj->ob_type;
                /* if the user defines his own __new__, 
                   let him call __init_ explicitly */
                if (type->tp_new != slot_tp_new){
                        if (type->tp_init != NULL &&
                            type->tp_init(obj, args, kwds) < 0) {
                               Py_DECREF(obj);
                               obj = NULL;
                        }
                }
        }
        return obj;
}

Usage:

class A(object):
    def __new__(object_type, *args, **kwargs):
        new_inst = object.__new__(object_type)
        new_inst.__init__(*args, **kwargs)
        return new_inst
    def new(object_type):
	return object.__new__(object_type)
    new = classmethod(new)
    def __init__(self, *args, **kwargs):
        print 'Hi...'


>>> a = A()
Hi
>>> b = A.new()
>>> a
<A object at 0x81807b8>
>>> b
<A object at 0x817dd60>
>>> type(a)   
<type 'A'>
>>> type(b)
<type 'A'>


I.e. the programmer can define __new__, to determine what happens with
A(), and define A.new() to give bare instances. A gain in flexibility (I
think)

Cheers,

Roeland

-- 
r.b.rigilink at chello.nl

"Half of what I say is nonsense. Unfortunately I don't know which half"



More information about the Python-list mailing list