@@ -1332,14 +1332,44 @@ impl ExecutingFrame<'_> {
13321332 let mut values = Vec :: new ( ) ;
13331333 let mut all_match = true ;
13341334
1335- for key in keys {
1336- match subject. get_item ( key. as_object ( ) , vm) {
1337- Ok ( value) => values. push ( value) ,
1338- Err ( e) if e. fast_isinstance ( vm. ctx . exceptions . key_error ) => {
1339- all_match = false ;
1340- break ;
1335+ // We use the two argument form of map.get(key, default) for two reasons:
1336+ // - Atomically check for a key and get its value without error handling.
1337+ // - Don't cause key creation or resizing in dict subclasses like
1338+ // collections.defaultdict that define __missing__ (or similar).
1339+ // See CPython's _PyEval_MatchKeys
1340+
1341+ if let Ok ( get_method) = subject. get_attr ( "get" , vm) {
1342+ // dummy = object()
1343+ // CPython: dummy = _PyObject_CallNoArgs((PyObject *)&PyBaseObject_Type);
1344+ let dummy = vm
1345+ . ctx
1346+ . new_base_object ( vm. ctx . types . object_type . to_owned ( ) , None ) ;
1347+
1348+ for key in keys {
1349+ // value = map.get(key, dummy)
1350+ match get_method. call ( ( key. as_object ( ) , dummy. clone ( ) ) , vm) {
1351+ Ok ( value) => {
1352+ // if value == dummy: key not in map!
1353+ if value. is ( & dummy) {
1354+ all_match = false ;
1355+ break ;
1356+ }
1357+ values. push ( value) ;
1358+ }
1359+ Err ( e) => return Err ( e) ,
1360+ }
1361+ }
1362+ } else {
1363+ // Fallback if .get() method is not available (shouldn't happen for mappings)
1364+ for key in keys {
1365+ match subject. get_item ( key. as_object ( ) , vm) {
1366+ Ok ( value) => values. push ( value) ,
1367+ Err ( e) if e. fast_isinstance ( vm. ctx . exceptions . key_error ) => {
1368+ all_match = false ;
1369+ break ;
1370+ }
1371+ Err ( e) => return Err ( e) ,
13411372 }
1342- Err ( e) => return Err ( e) ,
13431373 }
13441374 }
13451375
0 commit comments