diff --git a/bson/__init__.py b/bson/__init__.py
index 5c7145c..acd8758 100644
--- a/bson/__init__.py
+++ b/bson/__init__.py
@@ -434,16 +434,29 @@ def _element_to_bson(key, value, check_keys, uuid_subtype):
                           type(value))
 
 
-def _dict_to_bson(dict, check_keys, uuid_subtype, top_level=True):
+# Contrary to the C implementation, the Python
+# implementation of :func:`_dict_to_bson` does not check
+# `document` for an explicit dict type and accepts any
+# object that implements key access and :meth:`iteritems`.
+#
+# Although this is really the better behavior, this check makes
+# sure, that both implementations act identically.
+dict_to_bson_ensure_c_compat = True
+
+def _dict_to_bson(dct, check_keys, uuid_subtype, top_level=True):
+    if dict_to_bson_ensure_c_compat and not isinstance(dct, dict):
+        # ensure that test_arbitrary_mapping_encode passes
+        raise TypeError("encoder expected a mapping type but got: %r" % dct)
+
     try:
         elements = []
-        if top_level and "_id" in dict:
-            elements.append(_element_to_bson("_id", dict["_id"], False, uuid_subtype))
-        for (key, value) in dict.iteritems():
+        if top_level and "_id" in dct:
+            elements.append(_element_to_bson("_id", dct["_id"], False, uuid_subtype))
+        for (key, value) in dct.iteritems():
             if not top_level or key != "_id":
                 elements.append(_element_to_bson(key, value, check_keys, uuid_subtype))
     except AttributeError:
-        raise TypeError("encoder expected a mapping type but got: %r" % dict)
+        raise TypeError("encoder expected a mapping type but got: %r" % dct)
 
     encoded = EMPTY.join(elements)
     length = len(encoded) + 5
diff --git a/test/test_bson.py b/test/test_bson.py
index e8d6355..b4e311f 100644
--- a/test/test_bson.py
+++ b/test/test_bson.py
@@ -440,5 +440,35 @@ class TestBSON(unittest.TestCase):
         d = OrderedDict([("one", 1), ("two", 2), ("three", 3), ("four", 4)])
         self.assertEqual(d, BSON.encode(d).decode(as_class=OrderedDict))
 
+    def test_arbitrary_mapping_type(self):
+        class ArbitrayMapping(object):
+            def __init__(self):
+                self._arbitrary_mapping_dict_ = dict()
+            def __getattr__(self, attr):
+                try:
+                    return getattr(self._arbitrary_mapping_dict_, attr)
+                except AttributeError:
+                    return super(ArbitrayMapping, self).__getattribute__(attr)
+            def __iter__(self):
+                return self._arbitrary_mapping_dict_.__iter__()
+            def __getitem__(self, key):
+                return self._arbitrary_mapping_dict_.__getitem__(key)
+            def __setitem__(self, key, val):
+                return self._arbitrary_mapping_dict_.__setitem__(key, val)
+
+        if not bson.has_c():
+            sys.stderr.write('test_arbitrary_mapping_type: Python test')
+            bson.dict_to_bson_ensure_c_compat = False
+            am = ArbitrayMapping()
+            self.assertTrue(isinstance(BSON.encode(am).decode(), dict))
+            am['_id'] = 'FFFFFFFFFFFFFFFFFFFFFFFF'
+            self.assertTrue(isinstance(BSON.encode(am).decode(), dict))
+
+        bson.dict_to_bson_ensure_c_compat = True
+        am = ArbitrayMapping()
+        self.assertRaises(TypeError, BSON.encode, am)
+        am['_id'] = 'FFFFFFFFFFFFFFFFFFFFFFFF'
+        self.assertRaises(TypeError, BSON.encode, am)
+
 if __name__ == "__main__":
     unittest.main()
