Repository: open-mmlab/mmcv

Issue:
Type inconsistency when using `_delete_` flag in dict to merge config
**Describe the Issue**

When `<class 'mmcv.utils.config.Config'>` merged with a dict of `_delete_` flag, the result loses type consistency of `ConfigDict`. 

**Reproduction**

```python
cfg = mmcv.Config(dict(model=dict(backbone=dict(type='ResNet'))))
# dict with delete
d1 = dict(model=dict(_delete_=True,backbone=dict(type='MobileNetV2')))
cfg.merge_from_dict(d1)
print(f'Type of cfg.model with _delete_ {type(cfg.model)}')

########################
cfg = mmcv.Config(dict(model=dict(backbone=dict(type='ResNet'))))
# dict without delete
d2 = dict(model=dict(backbone=dict(type='MobileNetV2')))
cfg.merge_from_dict(d2)
print(f'Type of cfg.model without _delete_ {type(cfg)}')
```

Outputs:
```
Type of cfg.model with _delete_ <class 'dict'>
Type of cfg.model without _delete_ <class 'mmcv.utils.config.Config'>
```
**Environment**

mmcv master might still remain this problem.



Discussion:

Resolving patch:
diff --git a/mmcv/utils/config.py b/mmcv/utils/config.py
--- a/mmcv/utils/config.py
+++ b/mmcv/utils/config.py
@@ -310,16 +310,19 @@ def _merge_a_into_b(a, b, allow_list_keys=False):
                 if len(b) <= k:
                     raise KeyError(f'Index {k} exceeds the length of list {b}')
                 b[k] = Config._merge_a_into_b(v, b[k], allow_list_keys)
-            elif isinstance(v,
-                            dict) and k in b and not v.pop(DELETE_KEY, False):
-                allowed_types = (dict, list) if allow_list_keys else dict
-                if not isinstance(b[k], allowed_types):
-                    raise TypeError(
-                        f'{k}={v} in child config cannot inherit from base '
-                        f'because {k} is a dict in the child config but is of '
-                        f'type {type(b[k])} in base config. You may set '
-                        f'`{DELETE_KEY}=True` to ignore the base config')
-                b[k] = Config._merge_a_into_b(v, b[k], allow_list_keys)
+            elif isinstance(v, dict):
+                if k in b and not v.pop(DELETE_KEY, False):
+                    allowed_types = (dict, list) if allow_list_keys else dict
+                    if not isinstance(b[k], allowed_types):
+                        raise TypeError(
+                            f'{k}={v} in child config cannot inherit from '
+                            f'base because {k} is a dict in the child config '
+                            f'but is of type {type(b[k])} in base config. '
+                            f'You may set `{DELETE_KEY}=True` to ignore the '
+                            f'base config.')
+                    b[k] = Config._merge_a_into_b(v, b[k], allow_list_keys)
+                else:
+                    b[k] = ConfigDict(v)
             else:
                 b[k] = v
         return b


Test patch:
diff --git a/tests/data/config/delete.py b/tests/data/config/delete.py
--- a/tests/data/config/delete.py
+++ b/tests/data/config/delete.py
@@ -1,2 +1,3 @@
 _base_ = './base.py'
-item2 = {'b': 0, '_delete_': True}
+item1 = {'a': 0, '_delete_': True}
+item2 = {'b': 0}
diff --git a/tests/test_utils/test_config.py b/tests/test_utils/test_config.py
--- a/tests/test_utils/test_config.py
+++ b/tests/test_utils/test_config.py
@@ -10,7 +10,7 @@
 import pytest
 import yaml
 
-from mmcv import Config, DictAction, dump, load
+from mmcv import Config, ConfigDict, DictAction, dump, load
 
 data_path = osp.join(osp.dirname(osp.dirname(__file__)), 'data')
 
@@ -347,12 +347,16 @@ def test_merge_delete():
     cfg_file = osp.join(data_path, 'config/delete.py')
     cfg = Config.fromfile(cfg_file)
     # cfg.field
-    assert cfg.item1 == [1, 2]
-    assert cfg.item2 == dict(b=0)
+    assert cfg.item1 == dict(a=0)
+    assert cfg.item2 == dict(a=0, b=0)
     assert cfg.item3 is True
     assert cfg.item4 == 'test'
     assert '_delete_' not in cfg.item2
 
+    # related issue: https://github.com/open-mmlab/mmcv/issues/1570
+    assert type(cfg.item1) == ConfigDict
+    assert type(cfg.item2) == ConfigDict
+
 
 def test_merge_intermediate_variable():