Thanks to visit codestin.com
Credit goes to github.com

Skip to content

Commit bcb6ca4

Browse files
committed
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 micropython#647.
1 parent 07995e9 commit bcb6ca4

File tree

3 files changed

+74
-53
lines changed

3 files changed

+74
-53
lines changed

py/objdict.c

+52-53
Original file line numberDiff line numberDiff line change
@@ -40,7 +40,7 @@
4040

4141
STATIC mp_obj_t mp_obj_new_dict_iterator(mp_obj_dict_t *dict, int cur);
4242
STATIC mp_map_elem_t *dict_it_iternext_elem(mp_obj_t self_in);
43-
STATIC mp_obj_t dict_copy(mp_obj_t self_in);
43+
STATIC mp_obj_t dict_update(uint n_args, const mp_obj_t *args, mp_map_t *kwargs);
4444

4545
STATIC void dict_print(void (*print)(void *env, const char *fmt, ...), void *env, mp_obj_t self_in, mp_print_kind_t kind) {
4646
mp_obj_dict_t *self = self_in;
@@ -61,40 +61,13 @@ STATIC void dict_print(void (*print)(void *env, const char *fmt, ...), void *env
6161
}
6262

6363
STATIC mp_obj_t dict_make_new(mp_obj_t type_in, uint n_args, uint n_kw, const mp_obj_t *args) {
64-
mp_obj_t dict;
65-
switch (n_args) {
66-
case 0:
67-
dict = mp_obj_new_dict(0);
68-
break;
69-
70-
case 1: {
71-
if (MP_OBJ_IS_TYPE(args[0], &mp_type_dict)) {
72-
return dict_copy(args[0]);
73-
}
74-
// TODO create dict from an arbitrary mapping!
75-
76-
// Make dict from iterable of pairs
77-
mp_obj_t iterable = mp_getiter(args[0]);
78-
mp_obj_t dict = mp_obj_new_dict(0);
79-
// TODO: support arbitrary seq as a pair
80-
mp_obj_t item;
81-
while ((item = mp_iternext(iterable)) != MP_OBJ_STOP_ITERATION) {
82-
mp_obj_t *sub_items;
83-
mp_obj_get_array_fixed_n(item, 2, &sub_items);
84-
mp_obj_dict_store(dict, sub_items[0], sub_items[1]);
85-
}
86-
return dict;
87-
}
88-
89-
default:
90-
nlr_raise(mp_obj_new_exception_msg_varg(&mp_type_TypeError, "dict takes at most 1 argument"));
91-
}
92-
93-
// add to the new dict any keyword args
94-
for (const mp_obj_t *a = args + n_args; n_kw > 0; n_kw--, a += 2) {
95-
mp_obj_dict_store(dict, a[0], a[1]);
64+
mp_obj_t dict = mp_obj_new_dict(0);
65+
if (n_args > 0 || n_kw > 0) {
66+
mp_obj_t args2[2] = {dict, args[0]}; // args[0] is always valid, even if it's not a positional arg
67+
mp_map_t kwargs;
68+
mp_map_init_fixed_table(&kwargs, n_kw, args + n_args);
69+
dict_update(n_args + 1, args2, &kwargs); // dict_update will check that n_args + 1 == 1 or 2
9670
}
97-
9871
return dict;
9972
}
10073

@@ -348,31 +321,57 @@ STATIC mp_obj_t dict_popitem(mp_obj_t self_in) {
348321
}
349322
STATIC MP_DEFINE_CONST_FUN_OBJ_1(dict_popitem_obj, dict_popitem);
350323

351-
STATIC mp_obj_t dict_update(mp_obj_t self_in, mp_obj_t iterable) {
352-
assert(MP_OBJ_IS_TYPE(self_in, &mp_type_dict));
353-
mp_obj_dict_t *self = self_in;
354-
/* TODO: check for the "keys" method */
355-
mp_obj_t iter = mp_getiter(iterable);
356-
mp_obj_t next = NULL;
357-
while ((next = mp_iternext(iter)) != MP_OBJ_STOP_ITERATION) {
358-
mp_obj_t inneriter = mp_getiter(next);
359-
mp_obj_t key = mp_iternext(inneriter);
360-
mp_obj_t value = mp_iternext(inneriter);
361-
mp_obj_t stop = mp_iternext(inneriter);
362-
if (key == MP_OBJ_STOP_ITERATION
363-
|| value == MP_OBJ_STOP_ITERATION
364-
|| stop != MP_OBJ_STOP_ITERATION) {
365-
nlr_raise(mp_obj_new_exception_msg(
366-
&mp_type_ValueError,
367-
"dictionary update sequence has the wrong length"));
324+
STATIC mp_obj_t dict_update(uint n_args, const mp_obj_t *args, mp_map_t *kwargs) {
325+
assert(MP_OBJ_IS_TYPE(args[0], &mp_type_dict));
326+
mp_obj_dict_t *self = args[0];
327+
328+
mp_arg_check_num(n_args, kwargs->used, 1, 2, true);
329+
330+
if (n_args == 2) {
331+
// given a positional argument
332+
333+
if (MP_OBJ_IS_TYPE(args[1], &mp_type_dict)) {
334+
// update from other dictionary (make sure other is not self)
335+
if (args[1] != self) {
336+
// TODO don't allocate heap object for this iterator
337+
mp_obj_t *dict_iter = mp_obj_new_dict_iterator(args[1], 0);
338+
mp_map_elem_t *elem = NULL;
339+
while ((elem = dict_it_iternext_elem(dict_iter)) != MP_OBJ_STOP_ITERATION) {
340+
mp_map_lookup(&self->map, elem->key, MP_MAP_LOOKUP_ADD_IF_NOT_FOUND)->value = elem->value;
341+
}
342+
}
368343
} else {
369-
mp_map_lookup(&self->map, key, MP_MAP_LOOKUP_ADD_IF_NOT_FOUND)->value = value;
344+
// update from a generic iterable of pairs
345+
mp_obj_t iter = mp_getiter(args[1]);
346+
mp_obj_t next = NULL;
347+
while ((next = mp_iternext(iter)) != MP_OBJ_STOP_ITERATION) {
348+
mp_obj_t inneriter = mp_getiter(next);
349+
mp_obj_t key = mp_iternext(inneriter);
350+
mp_obj_t value = mp_iternext(inneriter);
351+
mp_obj_t stop = mp_iternext(inneriter);
352+
if (key == MP_OBJ_STOP_ITERATION
353+
|| value == MP_OBJ_STOP_ITERATION
354+
|| stop != MP_OBJ_STOP_ITERATION) {
355+
nlr_raise(mp_obj_new_exception_msg(
356+
&mp_type_ValueError,
357+
"dictionary update sequence has the wrong length"));
358+
} else {
359+
mp_map_lookup(&self->map, key, MP_MAP_LOOKUP_ADD_IF_NOT_FOUND)->value = value;
360+
}
361+
}
362+
}
363+
}
364+
365+
// update the dict with any keyword args
366+
for (machine_uint_t i = 0; i < kwargs->alloc; i++) {
367+
if (MP_MAP_SLOT_IS_FILLED(kwargs, i)) {
368+
mp_map_lookup(&self->map, kwargs->table[i].key, MP_MAP_LOOKUP_ADD_IF_NOT_FOUND)->value = kwargs->table[i].value;
370369
}
371370
}
372371

373372
return mp_const_none;
374373
}
375-
STATIC MP_DEFINE_CONST_FUN_OBJ_2(dict_update_obj, dict_update);
374+
STATIC MP_DEFINE_CONST_FUN_OBJ_KW(dict_update_obj, 1, dict_update);
376375

377376

378377
/******************************************************************************/

tests/basics/dict_construct.py

+16
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
# dict constructor
2+
3+
d = dict()
4+
print(d)
5+
6+
d = dict({1:2})
7+
print(d)
8+
9+
d = dict(a=1)
10+
print(d)
11+
12+
d = dict({1:2}, a=3)
13+
print(d[1], d['a'])
14+
15+
d = dict([(1, 2)], a=3, b=4)
16+
print(d[1], d['a'], d['b'])

tests/basics/dict_update.py

+6
Original file line numberDiff line numberDiff line change
@@ -8,3 +8,9 @@
88
d.update([(1,4)])
99
print(d[1])
1010
print(len(d))
11+
12+
# using keywords
13+
d.update(a=5)
14+
print(d['a'])
15+
d.update([(1,5)], b=6)
16+
print(d[1], d['b'])

0 commit comments

Comments
 (0)