py/objtype: Refactor object's handling of __new__ to not create 2 objs.
Before this patch, if a user defined the __new__() function for a class then two instances of that class would be created: once before __new__ is called and once during the __new__ call (assuming the user creates some instance, eg using super().__new__, which is most of the time). The first one was then discarded. This refactor makes it so that a new instance is only created if the user __new__ function doesn't exist.
This commit is contained in:
parent
3c28df1658
commit
c78ef92d78
@ -52,9 +52,12 @@ STATIC mp_obj_t object___new__(mp_obj_t cls) {
|
|||||||
if (!MP_OBJ_IS_TYPE(cls, &mp_type_type) || !mp_obj_is_instance_type((mp_obj_type_t*)MP_OBJ_TO_PTR(cls))) {
|
if (!MP_OBJ_IS_TYPE(cls, &mp_type_type) || !mp_obj_is_instance_type((mp_obj_type_t*)MP_OBJ_TO_PTR(cls))) {
|
||||||
mp_raise_TypeError("__new__ arg must be a user-type");
|
mp_raise_TypeError("__new__ arg must be a user-type");
|
||||||
}
|
}
|
||||||
mp_obj_t o = MP_OBJ_SENTINEL;
|
// This executes only "__new__" part of instance creation.
|
||||||
mp_obj_t res = mp_obj_instance_make_new(MP_OBJ_TO_PTR(cls), 1, 0, &o);
|
// TODO: This won't work well for classes with native bases.
|
||||||
return res;
|
// TODO: This is a hack, should be resolved along the lines of
|
||||||
|
// https://github.com/micropython/micropython/issues/606#issuecomment-43685883
|
||||||
|
const mp_obj_type_t *native_base;
|
||||||
|
return MP_OBJ_FROM_PTR(mp_obj_new_instance(MP_OBJ_TO_PTR(cls), &native_base));
|
||||||
}
|
}
|
||||||
STATIC MP_DEFINE_CONST_FUN_OBJ_1(object___new___fun_obj, object___new__);
|
STATIC MP_DEFINE_CONST_FUN_OBJ_1(object___new___fun_obj, object___new__);
|
||||||
STATIC MP_DEFINE_CONST_STATICMETHOD_OBJ(object___new___obj, MP_ROM_PTR(&object___new___fun_obj));
|
STATIC MP_DEFINE_CONST_STATICMETHOD_OBJ(object___new___obj, MP_ROM_PTR(&object___new___fun_obj));
|
||||||
|
57
py/objtype.c
57
py/objtype.c
@ -90,17 +90,22 @@ STATIC mp_obj_t native_base_init_wrapper(size_t n_args, const mp_obj_t *args) {
|
|||||||
}
|
}
|
||||||
STATIC MP_DEFINE_CONST_FUN_OBJ_VAR_BETWEEN(native_base_init_wrapper_obj, 1, MP_OBJ_FUN_ARGS_MAX, native_base_init_wrapper);
|
STATIC MP_DEFINE_CONST_FUN_OBJ_VAR_BETWEEN(native_base_init_wrapper_obj, 1, MP_OBJ_FUN_ARGS_MAX, native_base_init_wrapper);
|
||||||
|
|
||||||
STATIC mp_obj_t mp_obj_new_instance(const mp_obj_type_t *class, size_t subobjs) {
|
#if !MICROPY_CPYTHON_COMPAT
|
||||||
mp_obj_instance_t *o = m_new_obj_var(mp_obj_instance_t, mp_obj_t, subobjs);
|
STATIC
|
||||||
|
#endif
|
||||||
|
mp_obj_instance_t *mp_obj_new_instance(const mp_obj_type_t *class, const mp_obj_type_t **native_base) {
|
||||||
|
size_t num_native_bases = instance_count_native_bases(class, native_base);
|
||||||
|
assert(num_native_bases < 2);
|
||||||
|
mp_obj_instance_t *o = m_new_obj_var(mp_obj_instance_t, mp_obj_t, num_native_bases);
|
||||||
o->base.type = class;
|
o->base.type = class;
|
||||||
mp_map_init(&o->members, 0);
|
mp_map_init(&o->members, 0);
|
||||||
// Initialise the native base-class slot (should be 1 at most) with a valid
|
// Initialise the native base-class slot (should be 1 at most) with a valid
|
||||||
// object. It doesn't matter which object, so long as it can be uniquely
|
// object. It doesn't matter which object, so long as it can be uniquely
|
||||||
// distinguished from a native class that is initialised.
|
// distinguished from a native class that is initialised.
|
||||||
if (subobjs != 0) {
|
if (num_native_bases != 0) {
|
||||||
o->subobj[0] = MP_OBJ_FROM_PTR(&native_base_init_wrapper_obj);
|
o->subobj[0] = MP_OBJ_FROM_PTR(&native_base_init_wrapper_obj);
|
||||||
}
|
}
|
||||||
return MP_OBJ_FROM_PTR(o);
|
return o;
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO
|
// TODO
|
||||||
@ -267,20 +272,6 @@ STATIC void instance_print(const mp_print_t *print, mp_obj_t self_in, mp_print_k
|
|||||||
mp_obj_t mp_obj_instance_make_new(const mp_obj_type_t *self, size_t n_args, size_t n_kw, const mp_obj_t *args) {
|
mp_obj_t mp_obj_instance_make_new(const mp_obj_type_t *self, size_t n_args, size_t n_kw, const mp_obj_t *args) {
|
||||||
assert(mp_obj_is_instance_type(self));
|
assert(mp_obj_is_instance_type(self));
|
||||||
|
|
||||||
const mp_obj_type_t *native_base = NULL;
|
|
||||||
size_t num_native_bases = instance_count_native_bases(self, &native_base);
|
|
||||||
assert(num_native_bases < 2);
|
|
||||||
|
|
||||||
mp_obj_instance_t *o = MP_OBJ_TO_PTR(mp_obj_new_instance(self, num_native_bases));
|
|
||||||
|
|
||||||
// This executes only "__new__" part of instance creation.
|
|
||||||
// TODO: This won't work well for classes with native bases.
|
|
||||||
// TODO: This is a hack, should be resolved along the lines of
|
|
||||||
// https://github.com/micropython/micropython/issues/606#issuecomment-43685883
|
|
||||||
if (n_args == 1 && *args == MP_OBJ_SENTINEL) {
|
|
||||||
return MP_OBJ_FROM_PTR(o);
|
|
||||||
}
|
|
||||||
|
|
||||||
// look for __new__ function
|
// look for __new__ function
|
||||||
mp_obj_t init_fn[2] = {MP_OBJ_NULL};
|
mp_obj_t init_fn[2] = {MP_OBJ_NULL};
|
||||||
struct class_lookup_data lookup = {
|
struct class_lookup_data lookup = {
|
||||||
@ -292,10 +283,12 @@ mp_obj_t mp_obj_instance_make_new(const mp_obj_type_t *self, size_t n_args, size
|
|||||||
};
|
};
|
||||||
mp_obj_class_lookup(&lookup, self);
|
mp_obj_class_lookup(&lookup, self);
|
||||||
|
|
||||||
mp_obj_t new_ret = MP_OBJ_FROM_PTR(o);
|
const mp_obj_type_t *native_base = NULL;
|
||||||
if (init_fn[0] == MP_OBJ_SENTINEL) {
|
mp_obj_instance_t *o;
|
||||||
// Native type's constructor is what wins - it gets all our arguments,
|
if (init_fn[0] == MP_OBJ_NULL || init_fn[0] == MP_OBJ_SENTINEL) {
|
||||||
// and none Python classes are initialized at all.
|
// Either there is no __new__() method defined or there is a native
|
||||||
|
// constructor. In both cases create a blank instance.
|
||||||
|
o = mp_obj_new_instance(self, &native_base);
|
||||||
|
|
||||||
// Since type->make_new() implements both __new__() and __init__() in
|
// Since type->make_new() implements both __new__() and __init__() in
|
||||||
// one go, of which the latter may be overridden by the Python subclass,
|
// one go, of which the latter may be overridden by the Python subclass,
|
||||||
@ -303,8 +296,9 @@ mp_obj_t mp_obj_instance_make_new(const mp_obj_type_t *self, size_t n_args, size
|
|||||||
// constructor to give a chance for the Python __init__() method to call
|
// constructor to give a chance for the Python __init__() method to call
|
||||||
// said native constructor.
|
// said native constructor.
|
||||||
|
|
||||||
} else if (init_fn[0] != MP_OBJ_NULL) {
|
} else {
|
||||||
// now call Python class __new__ function with all args
|
// Call Python class __new__ function with all args to create an instance
|
||||||
|
mp_obj_t new_ret;
|
||||||
if (n_args == 0 && n_kw == 0) {
|
if (n_args == 0 && n_kw == 0) {
|
||||||
mp_obj_t args2[1] = {MP_OBJ_FROM_PTR(self)};
|
mp_obj_t args2[1] = {MP_OBJ_FROM_PTR(self)};
|
||||||
new_ret = mp_call_function_n_kw(init_fn[0], 1, 0, args2);
|
new_ret = mp_call_function_n_kw(init_fn[0], 1, 0, args2);
|
||||||
@ -316,16 +310,17 @@ mp_obj_t mp_obj_instance_make_new(const mp_obj_type_t *self, size_t n_args, size
|
|||||||
m_del(mp_obj_t, args2, 1 + n_args + 2 * n_kw);
|
m_del(mp_obj_t, args2, 1 + n_args + 2 * n_kw);
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
// https://docs.python.org/3.4/reference/datamodel.html#object.__new__
|
||||||
|
// "If __new__() does not return an instance of cls, then the new
|
||||||
|
// instance's __init__() method will not be invoked."
|
||||||
|
if (mp_obj_get_type(new_ret) != self) {
|
||||||
|
return new_ret;
|
||||||
|
}
|
||||||
|
|
||||||
// https://docs.python.org/3.4/reference/datamodel.html#object.__new__
|
// The instance returned by __new__() becomes the new object
|
||||||
// "If __new__() does not return an instance of cls, then the new instance's __init__() method will not be invoked."
|
o = MP_OBJ_TO_PTR(new_ret);
|
||||||
if (mp_obj_get_type(new_ret) != self) {
|
|
||||||
return new_ret;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
o = MP_OBJ_TO_PTR(new_ret);
|
|
||||||
|
|
||||||
// now call Python class __init__ function with all args
|
// now call Python class __init__ function with all args
|
||||||
// This method has a chance to call super().__init__() to construct a
|
// This method has a chance to call super().__init__() to construct a
|
||||||
// possible native base class.
|
// possible native base class.
|
||||||
|
@ -37,6 +37,11 @@ typedef struct _mp_obj_instance_t {
|
|||||||
// TODO maybe cache __getattr__ and __setattr__ for efficient lookup of them
|
// TODO maybe cache __getattr__ and __setattr__ for efficient lookup of them
|
||||||
} mp_obj_instance_t;
|
} mp_obj_instance_t;
|
||||||
|
|
||||||
|
#if MICROPY_CPYTHON_COMPAT
|
||||||
|
// this is needed for object.__new__
|
||||||
|
mp_obj_instance_t *mp_obj_new_instance(const mp_obj_type_t *cls, const mp_obj_type_t **native_base);
|
||||||
|
#endif
|
||||||
|
|
||||||
// this needs to be exposed for MICROPY_OPT_CACHE_MAP_LOOKUP_IN_BYTECODE to work
|
// this needs to be exposed for MICROPY_OPT_CACHE_MAP_LOOKUP_IN_BYTECODE to work
|
||||||
void mp_obj_instance_attr(mp_obj_t self_in, qstr attr, mp_obj_t *dest);
|
void mp_obj_instance_attr(mp_obj_t self_in, qstr attr, mp_obj_t *dest);
|
||||||
|
|
||||||
|
Loading…
Reference in New Issue
Block a user