Commit bcb6ca4d authored by Damien George's avatar Damien George
Browse files

py: Implement full behaviour of dict.update(), and dict().

Add keyword args to dict.update(), and ability to take a dictionary as
argument.

dict() class constructor can now use dict.update() directly.

This patch loses fast path for dict(other_dict), but is that really
needed?  Any anyway, this idiom will now re-hash the dictionary, so is
arguably more memory efficient.

Addresses issue #647.
parent 07995e94
...@@ -40,7 +40,7 @@ ...@@ -40,7 +40,7 @@
STATIC mp_obj_t mp_obj_new_dict_iterator(mp_obj_dict_t *dict, int cur); STATIC mp_obj_t mp_obj_new_dict_iterator(mp_obj_dict_t *dict, int cur);
STATIC mp_map_elem_t *dict_it_iternext_elem(mp_obj_t self_in); STATIC mp_map_elem_t *dict_it_iternext_elem(mp_obj_t self_in);
STATIC mp_obj_t dict_copy(mp_obj_t self_in); STATIC mp_obj_t dict_update(uint n_args, const mp_obj_t *args, mp_map_t *kwargs);
STATIC void dict_print(void (*print)(void *env, const char *fmt, ...), void *env, mp_obj_t self_in, mp_print_kind_t kind) { STATIC void dict_print(void (*print)(void *env, const char *fmt, ...), void *env, mp_obj_t self_in, mp_print_kind_t kind) {
mp_obj_dict_t *self = self_in; mp_obj_dict_t *self = self_in;
...@@ -61,40 +61,13 @@ STATIC void dict_print(void (*print)(void *env, const char *fmt, ...), void *env ...@@ -61,40 +61,13 @@ STATIC void dict_print(void (*print)(void *env, const char *fmt, ...), void *env
} }
STATIC mp_obj_t dict_make_new(mp_obj_t type_in, uint n_args, uint n_kw, const mp_obj_t *args) { STATIC mp_obj_t dict_make_new(mp_obj_t type_in, uint n_args, uint n_kw, const mp_obj_t *args) {
mp_obj_t dict; mp_obj_t dict = mp_obj_new_dict(0);
switch (n_args) { if (n_args > 0 || n_kw > 0) {
case 0: mp_obj_t args2[2] = {dict, args[0]}; // args[0] is always valid, even if it's not a positional arg
dict = mp_obj_new_dict(0); mp_map_t kwargs;
break; mp_map_init_fixed_table(&kwargs, n_kw, args + n_args);
dict_update(n_args + 1, args2, &kwargs); // dict_update will check that n_args + 1 == 1 or 2
case 1: {
if (MP_OBJ_IS_TYPE(args[0], &mp_type_dict)) {
return dict_copy(args[0]);
}
// TODO create dict from an arbitrary mapping!
// Make dict from iterable of pairs
mp_obj_t iterable = mp_getiter(args[0]);
mp_obj_t dict = mp_obj_new_dict(0);
// TODO: support arbitrary seq as a pair
mp_obj_t item;
while ((item = mp_iternext(iterable)) != MP_OBJ_STOP_ITERATION) {
mp_obj_t *sub_items;
mp_obj_get_array_fixed_n(item, 2, &sub_items);
mp_obj_dict_store(dict, sub_items[0], sub_items[1]);
}
return dict;
}
default:
nlr_raise(mp_obj_new_exception_msg_varg(&mp_type_TypeError, "dict takes at most 1 argument"));
}
// add to the new dict any keyword args
for (const mp_obj_t *a = args + n_args; n_kw > 0; n_kw--, a += 2) {
mp_obj_dict_store(dict, a[0], a[1]);
} }
return dict; return dict;
} }
...@@ -348,31 +321,57 @@ STATIC mp_obj_t dict_popitem(mp_obj_t self_in) { ...@@ -348,31 +321,57 @@ STATIC mp_obj_t dict_popitem(mp_obj_t self_in) {
} }
STATIC MP_DEFINE_CONST_FUN_OBJ_1(dict_popitem_obj, dict_popitem); STATIC MP_DEFINE_CONST_FUN_OBJ_1(dict_popitem_obj, dict_popitem);
STATIC mp_obj_t dict_update(mp_obj_t self_in, mp_obj_t iterable) { STATIC mp_obj_t dict_update(uint n_args, const mp_obj_t *args, mp_map_t *kwargs) {
assert(MP_OBJ_IS_TYPE(self_in, &mp_type_dict)); assert(MP_OBJ_IS_TYPE(args[0], &mp_type_dict));
mp_obj_dict_t *self = self_in; mp_obj_dict_t *self = args[0];
/* TODO: check for the "keys" method */
mp_obj_t iter = mp_getiter(iterable); mp_arg_check_num(n_args, kwargs->used, 1, 2, true);
mp_obj_t next = NULL;
while ((next = mp_iternext(iter)) != MP_OBJ_STOP_ITERATION) { if (n_args == 2) {
mp_obj_t inneriter = mp_getiter(next); // given a positional argument
mp_obj_t key = mp_iternext(inneriter);
mp_obj_t value = mp_iternext(inneriter); if (MP_OBJ_IS_TYPE(args[1], &mp_type_dict)) {
mp_obj_t stop = mp_iternext(inneriter); // update from other dictionary (make sure other is not self)
if (key == MP_OBJ_STOP_ITERATION if (args[1] != self) {
|| value == MP_OBJ_STOP_ITERATION // TODO don't allocate heap object for this iterator
|| stop != MP_OBJ_STOP_ITERATION) { mp_obj_t *dict_iter = mp_obj_new_dict_iterator(args[1], 0);
nlr_raise(mp_obj_new_exception_msg( mp_map_elem_t *elem = NULL;
&mp_type_ValueError, while ((elem = dict_it_iternext_elem(dict_iter)) != MP_OBJ_STOP_ITERATION) {
"dictionary update sequence has the wrong length")); mp_map_lookup(&self->map, elem->key, MP_MAP_LOOKUP_ADD_IF_NOT_FOUND)->value = elem->value;
}
}
} else { } else {
mp_map_lookup(&self->map, key, MP_MAP_LOOKUP_ADD_IF_NOT_FOUND)->value = value; // update from a generic iterable of pairs
mp_obj_t iter = mp_getiter(args[1]);
mp_obj_t next = NULL;
while ((next = mp_iternext(iter)) != MP_OBJ_STOP_ITERATION) {
mp_obj_t inneriter = mp_getiter(next);
mp_obj_t key = mp_iternext(inneriter);
mp_obj_t value = mp_iternext(inneriter);
mp_obj_t stop = mp_iternext(inneriter);
if (key == MP_OBJ_STOP_ITERATION
|| value == MP_OBJ_STOP_ITERATION
|| stop != MP_OBJ_STOP_ITERATION) {
nlr_raise(mp_obj_new_exception_msg(
&mp_type_ValueError,
"dictionary update sequence has the wrong length"));
} else {
mp_map_lookup(&self->map, key, MP_MAP_LOOKUP_ADD_IF_NOT_FOUND)->value = value;
}
}
}
}
// update the dict with any keyword args
for (machine_uint_t i = 0; i < kwargs->alloc; i++) {
if (MP_MAP_SLOT_IS_FILLED(kwargs, i)) {
mp_map_lookup(&self->map, kwargs->table[i].key, MP_MAP_LOOKUP_ADD_IF_NOT_FOUND)->value = kwargs->table[i].value;
} }
} }
return mp_const_none; return mp_const_none;
} }
STATIC MP_DEFINE_CONST_FUN_OBJ_2(dict_update_obj, dict_update); STATIC MP_DEFINE_CONST_FUN_OBJ_KW(dict_update_obj, 1, dict_update);
/******************************************************************************/ /******************************************************************************/
......
# dict constructor
d = dict()
print(d)
d = dict({1:2})
print(d)
d = dict(a=1)
print(d)
d = dict({1:2}, a=3)
print(d[1], d['a'])
d = dict([(1, 2)], a=3, b=4)
print(d[1], d['a'], d['b'])
...@@ -8,3 +8,9 @@ print(len(d)) ...@@ -8,3 +8,9 @@ print(len(d))
d.update([(1,4)]) d.update([(1,4)])
print(d[1]) print(d[1])
print(len(d)) print(len(d))
# using keywords
d.update(a=5)
print(d['a'])
d.update([(1,5)], b=6)
print(d[1], d['b'])
Supports Markdown
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment