mirror of
https://github.com/python/cpython.git
synced 2025-08-04 08:59:19 +00:00
bpo-32544: Speed up hasattr() and getattr() (GH-5173)
AttributeError was raised always when attribute is not found. This commit skip raising AttributeError when `tp_getattro` is `PyObject_GenericGetAttr`. It makes hasattr() and getattr() about 4x faster when attribute is not found.
This commit is contained in:
parent
b44c5169f6
commit
378edee0a3
5 changed files with 71 additions and 19 deletions
|
@ -887,10 +887,41 @@ PyObject_GetAttr(PyObject *v, PyObject *name)
|
|||
return NULL;
|
||||
}
|
||||
|
||||
PyObject *
|
||||
_PyObject_GetAttrWithoutError(PyObject *v, PyObject *name)
|
||||
{
|
||||
PyTypeObject *tp = Py_TYPE(v);
|
||||
PyObject *ret = NULL;
|
||||
|
||||
if (!PyUnicode_Check(name)) {
|
||||
PyErr_Format(PyExc_TypeError,
|
||||
"attribute name must be string, not '%.200s'",
|
||||
name->ob_type->tp_name);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
if (tp->tp_getattro == PyObject_GenericGetAttr) {
|
||||
return _PyObject_GenericGetAttrWithDict(v, name, NULL, 1);
|
||||
}
|
||||
if (tp->tp_getattro != NULL) {
|
||||
ret = (*tp->tp_getattro)(v, name);
|
||||
}
|
||||
else if (tp->tp_getattr != NULL) {
|
||||
const char *name_str = PyUnicode_AsUTF8(name);
|
||||
if (name_str == NULL)
|
||||
return NULL;
|
||||
ret = (*tp->tp_getattr)(v, (char *)name_str);
|
||||
}
|
||||
if (ret == NULL && PyErr_ExceptionMatches(PyExc_AttributeError)) {
|
||||
PyErr_Clear();
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
int
|
||||
PyObject_HasAttr(PyObject *v, PyObject *name)
|
||||
{
|
||||
PyObject *res = PyObject_GetAttr(v, name);
|
||||
PyObject *res = _PyObject_GetAttrWithoutError(v, name);
|
||||
if (res != NULL) {
|
||||
Py_DECREF(res);
|
||||
return 1;
|
||||
|
@ -1098,10 +1129,13 @@ _PyObject_GetMethod(PyObject *obj, PyObject *name, PyObject **method)
|
|||
/* Generic GetAttr functions - put these in your tp_[gs]etattro slot. */
|
||||
|
||||
PyObject *
|
||||
_PyObject_GenericGetAttrWithDict(PyObject *obj, PyObject *name, PyObject *dict)
|
||||
_PyObject_GenericGetAttrWithDict(PyObject *obj, PyObject *name,
|
||||
PyObject *dict, int suppress)
|
||||
{
|
||||
/* Make sure the logic of _PyObject_GetMethod is in sync with
|
||||
this method.
|
||||
|
||||
When suppress=1, this function suppress AttributeError.
|
||||
*/
|
||||
|
||||
PyTypeObject *tp = Py_TYPE(obj);
|
||||
|
@ -1132,6 +1166,10 @@ _PyObject_GenericGetAttrWithDict(PyObject *obj, PyObject *name, PyObject *dict)
|
|||
f = descr->ob_type->tp_descr_get;
|
||||
if (f != NULL && PyDescr_IsData(descr)) {
|
||||
res = f(descr, obj, (PyObject *)obj->ob_type);
|
||||
if (res == NULL && suppress &&
|
||||
PyErr_ExceptionMatches(PyExc_AttributeError)) {
|
||||
PyErr_Clear();
|
||||
}
|
||||
goto done;
|
||||
}
|
||||
}
|
||||
|
@ -1171,6 +1209,10 @@ _PyObject_GenericGetAttrWithDict(PyObject *obj, PyObject *name, PyObject *dict)
|
|||
|
||||
if (f != NULL) {
|
||||
res = f(descr, obj, (PyObject *)Py_TYPE(obj));
|
||||
if (res == NULL && suppress &&
|
||||
PyErr_ExceptionMatches(PyExc_AttributeError)) {
|
||||
PyErr_Clear();
|
||||
}
|
||||
goto done;
|
||||
}
|
||||
|
||||
|
@ -1180,9 +1222,11 @@ _PyObject_GenericGetAttrWithDict(PyObject *obj, PyObject *name, PyObject *dict)
|
|||
goto done;
|
||||
}
|
||||
|
||||
PyErr_Format(PyExc_AttributeError,
|
||||
"'%.50s' object has no attribute '%U'",
|
||||
tp->tp_name, name);
|
||||
if (!suppress) {
|
||||
PyErr_Format(PyExc_AttributeError,
|
||||
"'%.50s' object has no attribute '%U'",
|
||||
tp->tp_name, name);
|
||||
}
|
||||
done:
|
||||
Py_XDECREF(descr);
|
||||
Py_DECREF(name);
|
||||
|
@ -1192,7 +1236,7 @@ _PyObject_GenericGetAttrWithDict(PyObject *obj, PyObject *name, PyObject *dict)
|
|||
PyObject *
|
||||
PyObject_GenericGetAttr(PyObject *obj, PyObject *name)
|
||||
{
|
||||
return _PyObject_GenericGetAttrWithDict(obj, name, NULL);
|
||||
return _PyObject_GenericGetAttrWithDict(obj, name, NULL, 0);
|
||||
}
|
||||
|
||||
int
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue