diff -r 34fb29694c76 Lib/pickle.py --- a/Lib/pickle.py Wed Jan 25 23:35:46 2017 -0800 +++ b/Lib/pickle.py Thu Jan 26 18:01:03 2017 +0200 @@ -1464,12 +1464,16 @@ class _Unpickler: def load_appends(self): items = self.pop_mark() list_obj = self.stack[-1] - if isinstance(list_obj, list): - list_obj.extend(items) + try: + extend = list_obj.extend + except AttributeError: + pass else: - append = list_obj.append - for item in items: - append(item) + extend(items) + return + append = list_obj.append + for item in items: + append(item) dispatch[APPENDS[0]] = load_appends def load_setitem(self): diff -r 34fb29694c76 Misc/NEWS --- a/Misc/NEWS Wed Jan 25 23:35:46 2017 -0800 +++ b/Misc/NEWS Thu Jan 26 18:01:03 2017 +0200 @@ -218,6 +218,10 @@ Core and Builtins Library ------- +- Issue #29368: The extend() method is now called instead of the append() + method when unpickle list-like objects . This can speed up unpickling + to 2 times. + - Issue #29338: The help of a builtin or extension class now includes the constructor signature if __text_signature__ is provided for the class. diff -r 34fb29694c76 Modules/_pickle.c --- a/Modules/_pickle.c Wed Jan 25 23:35:46 2017 -0800 +++ b/Modules/_pickle.c Thu Jan 26 18:01:03 2017 +0200 @@ -5807,7 +5807,9 @@ static int do_append(UnpicklerObject *self, Py_ssize_t x) { PyObject *value; + PyObject *slice; PyObject *list; + PyObject *result; Py_ssize_t len, i; len = Py_SIZE(self->stack); @@ -5818,8 +5820,7 @@ do_append(UnpicklerObject *self, Py_ssiz list = self->stack->data[x - 1]; - if (PyList_Check(list)) { - PyObject *slice; + if (PyList_CheckExact(list)) { Py_ssize_t list_len; int ret; @@ -5832,27 +5833,49 @@ do_append(UnpicklerObject *self, Py_ssiz return ret; } else { - PyObject *append_func; - _Py_IDENTIFIER(append); - - append_func = _PyObject_GetAttrId(list, &PyId_append); - if (append_func == NULL) - return -1; - for (i = x; i < len; i++) { - PyObject *result; - - value = self->stack->data[i]; - result = _Pickle_FastCall(append_func, value); - if (result == NULL) { - Pdata_clear(self->stack, i + 1); - Py_SIZE(self->stack) = x; - Py_DECREF(append_func); + PyObject *extend_func; + _Py_IDENTIFIER(extend); + + extend_func = _PyObject_GetAttrId(list, &PyId_extend); + if (extend_func != NULL) { + slice = Pdata_poplist(self->stack, x); + if (!slice) { + Py_DECREF(extend_func); return -1; } + result = _Pickle_FastCall(extend_func, slice); + Py_DECREF(slice); + Py_DECREF(extend_func); + if (result == NULL) + return -1; Py_DECREF(result); } - Py_SIZE(self->stack) = x; - Py_DECREF(append_func); + else { + PyObject *append_func; + _Py_IDENTIFIER(append); + + /* The support of both extend() and append() is required + by PEP 307. But since Python 3.6 and lower used only + append(), it should fall back to append() to avoid breaking + existing code when extend() is not implemented. */ + PyErr_Clear(); + append_func = _PyObject_GetAttrId(list, &PyId_append); + if (append_func == NULL) + return -1; + for (i = x; i < len; i++) { + value = self->stack->data[i]; + result = _Pickle_FastCall(append_func, value); + if (result == NULL) { + Pdata_clear(self->stack, i + 1); + Py_SIZE(self->stack) = x; + Py_DECREF(append_func); + return -1; + } + Py_DECREF(result); + } + Py_SIZE(self->stack) = x; + Py_DECREF(append_func); + } } return 0;