Allow assignments to instance.__dict__ and instance.__class__.  The
former lets you give an instance a set of new instance vars.  The
latter lets you give it a new class.  Both are typechecked and
disallowed in restricted mode.

For classes, the check for read-only special attributes is tightened
so that only assignments to __dict__, __bases__, __name__,
__getattr__, __setattr__, and __delattr__ (these could be made to work
as well, but I don't know if that's useful -- let's see first whether
mucking with instances will help).
diff --git a/Objects/classobject.c b/Objects/classobject.c
index f0f023b..91307f8 100644
--- a/Objects/classobject.c
+++ b/Objects/classobject.c
@@ -175,20 +175,31 @@
 	PyObject *name;
 	PyObject *v;
 {
-	char *sname = PyString_AsString(name);
-	if (sname[0] == '_' && sname[1] == '_') {
-		int n = PyString_Size(name);
-		if (sname[n-1] == '_' && sname[n-2] == '_') {
-			PyErr_SetString(PyExc_TypeError,
-					"read-only special attribute");
-			return -1;
-		}
-	}
+	char *sname;
 	if (PyEval_GetRestricted()) {
 		PyErr_SetString(PyExc_RuntimeError,
 			   "classes are read-only in restricted mode");
 		return -1;
 	}
+	sname = PyString_AsString(name);
+	if (sname[0] == '_' && sname[1] == '_') {
+		int n = PyString_Size(name);
+		if (sname[n-1] == '_' && sname[n-2] == '_') {
+			if (strcmp(sname, "__dict__") == 0 ||
+			    strcmp(sname, "__bases__") == 0 ||
+			    strcmp(sname, "__name__") == 0 ||
+			    strcmp(sname, "__getattr__") == 0 ||
+			    strcmp(sname, "__setattr__") == 0 ||
+			    strcmp(sname, "__delattr__") == 0)
+			{
+				/* XXX In unrestricted mode, we should
+				   XXX allow this -- with a type check */
+				PyErr_SetString(PyExc_TypeError,
+						"read-only special attribute");
+				return -1;
+			}
+		}
+	}
 	if (v == NULL) {
 		int rv = PyDict_DelItem(op->cl_dict, name);
 		if (rv < 0)
@@ -489,16 +500,45 @@
 	PyObject *name;
 	PyObject *v;
 {
-	PyObject *func, *args, *res;
+	PyObject *func, *args, *res, *tmp;
 	char *sname = PyString_AsString(name);
-	if (sname[0] == '_' && sname[1] == '_'
-	    && (strcmp(sname, "__dict__") == 0 ||
-		strcmp(sname, "__class__") == 0)) {
-	        int n = PyString_Size(name);
+	if (sname[0] == '_' && sname[1] == '_') {
+		int n = PyString_Size(name);
 		if (sname[n-1] == '_' && sname[n-2] == '_') {
-			PyErr_SetString(PyExc_TypeError,
-					"read-only special attribute");
-			return -1;
+			if (strcmp(sname, "__dict__") == 0) {
+				if (PyEval_GetRestricted()) {
+					PyErr_SetString(PyExc_RuntimeError,
+				 "__dict__ not accessible in restricted mode");
+					return -1;
+				}
+				if (v == NULL || !PyDict_Check(v)) {
+				    PyErr_SetString(PyExc_TypeError,
+				       "__dict__ must be set to a dictionary");
+				    return -1;
+				}
+				tmp = inst->in_dict;
+				Py_INCREF(v);
+				inst->in_dict = v;
+				Py_DECREF(tmp);
+				return 0;
+			}
+			if (strcmp(sname, "__class__") == 0) {
+				if (PyEval_GetRestricted()) {
+					PyErr_SetString(PyExc_RuntimeError,
+				"__class__ not accessible in restricted mode");
+					return -1;
+				}
+				if (v == NULL || !PyClass_Check(v)) {
+					PyErr_SetString(PyExc_TypeError,
+					   "__class__ must be set to a class");
+					return -1;
+				}
+				tmp = (PyObject *)(inst->in_class);
+				Py_INCREF(v);
+				inst->in_class = (PyClassObject *)v;
+				Py_DECREF(tmp);
+				return 0;
+			}
 		}
 	}
 	if (v == NULL)