mirror of
https://github.com/python/cpython.git
synced 2025-10-03 05:35:59 +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
|
@ -536,6 +536,8 @@ PyAPI_FUNC(int) PyObject_SetAttr(PyObject *, PyObject *, PyObject *);
|
||||||
PyAPI_FUNC(int) PyObject_HasAttr(PyObject *, PyObject *);
|
PyAPI_FUNC(int) PyObject_HasAttr(PyObject *, PyObject *);
|
||||||
#ifndef Py_LIMITED_API
|
#ifndef Py_LIMITED_API
|
||||||
PyAPI_FUNC(int) _PyObject_IsAbstract(PyObject *);
|
PyAPI_FUNC(int) _PyObject_IsAbstract(PyObject *);
|
||||||
|
/* Same as PyObject_GetAttr(), but don't raise AttributeError. */
|
||||||
|
PyAPI_FUNC(PyObject *) _PyObject_GetAttrWithoutError(PyObject *, PyObject *);
|
||||||
PyAPI_FUNC(PyObject *) _PyObject_GetAttrId(PyObject *, struct _Py_Identifier *);
|
PyAPI_FUNC(PyObject *) _PyObject_GetAttrId(PyObject *, struct _Py_Identifier *);
|
||||||
PyAPI_FUNC(int) _PyObject_SetAttrId(PyObject *, struct _Py_Identifier *, PyObject *);
|
PyAPI_FUNC(int) _PyObject_SetAttrId(PyObject *, struct _Py_Identifier *, PyObject *);
|
||||||
PyAPI_FUNC(int) _PyObject_HasAttrId(PyObject *, struct _Py_Identifier *);
|
PyAPI_FUNC(int) _PyObject_HasAttrId(PyObject *, struct _Py_Identifier *);
|
||||||
|
@ -567,7 +569,7 @@ PyAPI_FUNC(int) PyObject_CallFinalizerFromDealloc(PyObject *);
|
||||||
/* Same as PyObject_Generic{Get,Set}Attr, but passing the attributes
|
/* Same as PyObject_Generic{Get,Set}Attr, but passing the attributes
|
||||||
dict as the last parameter. */
|
dict as the last parameter. */
|
||||||
PyAPI_FUNC(PyObject *)
|
PyAPI_FUNC(PyObject *)
|
||||||
_PyObject_GenericGetAttrWithDict(PyObject *, PyObject *, PyObject *);
|
_PyObject_GenericGetAttrWithDict(PyObject *, PyObject *, PyObject *, int);
|
||||||
PyAPI_FUNC(int)
|
PyAPI_FUNC(int)
|
||||||
_PyObject_GenericSetAttrWithDict(PyObject *, PyObject *,
|
_PyObject_GenericSetAttrWithDict(PyObject *, PyObject *,
|
||||||
PyObject *, PyObject *);
|
PyObject *, PyObject *);
|
||||||
|
|
|
@ -0,0 +1,3 @@
|
||||||
|
``hasattr(obj, name)`` and ``getattr(obj, name, default)`` are about 4 times
|
||||||
|
faster than before when ``name`` is not found and ``obj`` doesn't override
|
||||||
|
``__getattr__`` or ``__getattribute__``.
|
|
@ -925,13 +925,15 @@ local_getattro(localobject *self, PyObject *name)
|
||||||
|
|
||||||
if (Py_TYPE(self) != &localtype)
|
if (Py_TYPE(self) != &localtype)
|
||||||
/* use generic lookup for subtypes */
|
/* use generic lookup for subtypes */
|
||||||
return _PyObject_GenericGetAttrWithDict((PyObject *)self, name, ldict);
|
return _PyObject_GenericGetAttrWithDict(
|
||||||
|
(PyObject *)self, name, ldict, 0);
|
||||||
|
|
||||||
/* Optimization: just look in dict ourselves */
|
/* Optimization: just look in dict ourselves */
|
||||||
value = PyDict_GetItem(ldict, name);
|
value = PyDict_GetItem(ldict, name);
|
||||||
if (value == NULL)
|
if (value == NULL)
|
||||||
/* Fall back on generic to get __class__ and __dict__ */
|
/* Fall back on generic to get __class__ and __dict__ */
|
||||||
return _PyObject_GenericGetAttrWithDict((PyObject *)self, name, ldict);
|
return _PyObject_GenericGetAttrWithDict(
|
||||||
|
(PyObject *)self, name, ldict, 0);
|
||||||
|
|
||||||
Py_INCREF(value);
|
Py_INCREF(value);
|
||||||
return value;
|
return value;
|
||||||
|
|
|
@ -887,10 +887,41 @@ PyObject_GetAttr(PyObject *v, PyObject *name)
|
||||||
return NULL;
|
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
|
int
|
||||||
PyObject_HasAttr(PyObject *v, PyObject *name)
|
PyObject_HasAttr(PyObject *v, PyObject *name)
|
||||||
{
|
{
|
||||||
PyObject *res = PyObject_GetAttr(v, name);
|
PyObject *res = _PyObject_GetAttrWithoutError(v, name);
|
||||||
if (res != NULL) {
|
if (res != NULL) {
|
||||||
Py_DECREF(res);
|
Py_DECREF(res);
|
||||||
return 1;
|
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. */
|
/* Generic GetAttr functions - put these in your tp_[gs]etattro slot. */
|
||||||
|
|
||||||
PyObject *
|
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
|
/* Make sure the logic of _PyObject_GetMethod is in sync with
|
||||||
this method.
|
this method.
|
||||||
|
|
||||||
|
When suppress=1, this function suppress AttributeError.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
PyTypeObject *tp = Py_TYPE(obj);
|
PyTypeObject *tp = Py_TYPE(obj);
|
||||||
|
@ -1132,6 +1166,10 @@ _PyObject_GenericGetAttrWithDict(PyObject *obj, PyObject *name, PyObject *dict)
|
||||||
f = descr->ob_type->tp_descr_get;
|
f = descr->ob_type->tp_descr_get;
|
||||||
if (f != NULL && PyDescr_IsData(descr)) {
|
if (f != NULL && PyDescr_IsData(descr)) {
|
||||||
res = f(descr, obj, (PyObject *)obj->ob_type);
|
res = f(descr, obj, (PyObject *)obj->ob_type);
|
||||||
|
if (res == NULL && suppress &&
|
||||||
|
PyErr_ExceptionMatches(PyExc_AttributeError)) {
|
||||||
|
PyErr_Clear();
|
||||||
|
}
|
||||||
goto done;
|
goto done;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1171,6 +1209,10 @@ _PyObject_GenericGetAttrWithDict(PyObject *obj, PyObject *name, PyObject *dict)
|
||||||
|
|
||||||
if (f != NULL) {
|
if (f != NULL) {
|
||||||
res = f(descr, obj, (PyObject *)Py_TYPE(obj));
|
res = f(descr, obj, (PyObject *)Py_TYPE(obj));
|
||||||
|
if (res == NULL && suppress &&
|
||||||
|
PyErr_ExceptionMatches(PyExc_AttributeError)) {
|
||||||
|
PyErr_Clear();
|
||||||
|
}
|
||||||
goto done;
|
goto done;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1180,9 +1222,11 @@ _PyObject_GenericGetAttrWithDict(PyObject *obj, PyObject *name, PyObject *dict)
|
||||||
goto done;
|
goto done;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (!suppress) {
|
||||||
PyErr_Format(PyExc_AttributeError,
|
PyErr_Format(PyExc_AttributeError,
|
||||||
"'%.50s' object has no attribute '%U'",
|
"'%.50s' object has no attribute '%U'",
|
||||||
tp->tp_name, name);
|
tp->tp_name, name);
|
||||||
|
}
|
||||||
done:
|
done:
|
||||||
Py_XDECREF(descr);
|
Py_XDECREF(descr);
|
||||||
Py_DECREF(name);
|
Py_DECREF(name);
|
||||||
|
@ -1192,7 +1236,7 @@ _PyObject_GenericGetAttrWithDict(PyObject *obj, PyObject *name, PyObject *dict)
|
||||||
PyObject *
|
PyObject *
|
||||||
PyObject_GenericGetAttr(PyObject *obj, PyObject *name)
|
PyObject_GenericGetAttr(PyObject *obj, PyObject *name)
|
||||||
{
|
{
|
||||||
return _PyObject_GenericGetAttrWithDict(obj, name, NULL);
|
return _PyObject_GenericGetAttrWithDict(obj, name, NULL, 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
int
|
int
|
||||||
|
|
|
@ -1126,13 +1126,15 @@ builtin_getattr(PyObject *self, PyObject *const *args, Py_ssize_t nargs)
|
||||||
"getattr(): attribute name must be string");
|
"getattr(): attribute name must be string");
|
||||||
return NULL;
|
return NULL;
|
||||||
}
|
}
|
||||||
result = PyObject_GetAttr(v, name);
|
if (dflt != NULL) {
|
||||||
if (result == NULL && dflt != NULL &&
|
result = _PyObject_GetAttrWithoutError(v, name);
|
||||||
PyErr_ExceptionMatches(PyExc_AttributeError))
|
if (result == NULL && !PyErr_Occurred()) {
|
||||||
{
|
|
||||||
PyErr_Clear();
|
|
||||||
Py_INCREF(dflt);
|
Py_INCREF(dflt);
|
||||||
result = dflt;
|
return dflt;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
result = PyObject_GetAttr(v, name);
|
||||||
}
|
}
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
@ -1189,10 +1191,9 @@ builtin_hasattr_impl(PyObject *module, PyObject *obj, PyObject *name)
|
||||||
"hasattr(): attribute name must be string");
|
"hasattr(): attribute name must be string");
|
||||||
return NULL;
|
return NULL;
|
||||||
}
|
}
|
||||||
v = PyObject_GetAttr(obj, name);
|
v = _PyObject_GetAttrWithoutError(obj, name);
|
||||||
if (v == NULL) {
|
if (v == NULL) {
|
||||||
if (PyErr_ExceptionMatches(PyExc_AttributeError)) {
|
if (!PyErr_Occurred()) {
|
||||||
PyErr_Clear();
|
|
||||||
Py_RETURN_FALSE;
|
Py_RETURN_FALSE;
|
||||||
}
|
}
|
||||||
return NULL;
|
return NULL;
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue