// See mp_load_attr() if making any changes STATIC inline mp_obj_t mp_load_attr_default(mp_obj_t base, qstr attr, mp_obj_t defval) { mp_obj_t dest[2]; // use load_method, raising or not raising exception ((defval == MP_OBJ_NULL) ? mp_load_method : mp_load_method_maybe)(base, attr, dest); if (dest[0] == MP_OBJ_NULL) { return defval; } else if (dest[1] == MP_OBJ_NULL) { // load_method returned just a normal attribute return dest[0]; } else { // load_method returned a method, so build a bound method object return mp_obj_new_bound_meth(dest[0], dest[1]); } }
mp_obj_t mp_obj_instance_load_attr(mp_obj_t self_in, qstr attr) { // logic: look in obj members then class locals (TODO check this against CPython) mp_obj_instance_t *self = self_in; mp_map_elem_t *elem = mp_qstr_map_lookup(self->members, attr, false); if (elem != NULL) { // object member, always treated as a value return elem->value; } elem = mp_qstr_map_lookup(mp_obj_class_get_locals(self->class), attr, false); if (elem != NULL) { if (mp_obj_is_callable(elem->value)) { // class member is callable so build a bound method return mp_obj_new_bound_meth(self_in, elem->value); } else { // class member is a value, so just return that value return elem->value; } } nlr_jump(mp_obj_new_exception_msg_2_args(rt_q_AttributeError, "'%s' object has no attribute '%s'", mp_obj_get_type_str(self_in), qstr_str(attr))); }