Move abc._Abstract into object by adding a new flag Py_TPFLAGS_IS_ABSTRACT,
which forbids constructing types that have it set. The effect is to speed ./python.exe -m timeit -s 'import abc' -s 'class Foo(object): __metaclass__ = abc.ABCMeta' 'Foo()' up from 2.5us to 0.201us. This fixes issue 1762.
This commit is contained in:
parent
c105289ec4
commit
960b9b7a2f
|
@ -537,6 +537,9 @@ given type object has a specified feature.
|
|||
#define Py_TPFLAGS_HAVE_VERSION_TAG (1L<<18)
|
||||
#define Py_TPFLAGS_VALID_VERSION_TAG (1L<<19)
|
||||
|
||||
/* Type is abstract and cannot be instantiated */
|
||||
#define Py_TPFLAGS_IS_ABSTRACT (1L<<20)
|
||||
|
||||
/* These flags are used to determine if a type is a subclass. */
|
||||
#define Py_TPFLAGS_INT_SUBCLASS (1L<<23)
|
||||
#define Py_TPFLAGS_LONG_SUBCLASS (1L<<24)
|
||||
|
|
49
Lib/abc.py
49
Lib/abc.py
|
@ -51,52 +51,6 @@ class abstractproperty(property):
|
|||
__isabstractmethod__ = True
|
||||
|
||||
|
||||
class _Abstract(object):
|
||||
|
||||
"""Helper class inserted into the bases by ABCMeta (using _fix_bases()).
|
||||
|
||||
You should never need to explicitly subclass this class.
|
||||
|
||||
There should never be a base class between _Abstract and object.
|
||||
"""
|
||||
|
||||
def __new__(cls, *args, **kwds):
|
||||
am = cls.__dict__.get("__abstractmethods__")
|
||||
if am:
|
||||
raise TypeError("Can't instantiate abstract class %s "
|
||||
"with abstract methods %s" %
|
||||
(cls.__name__, ", ".join(sorted(am))))
|
||||
if (args or kwds) and cls.__init__ is object.__init__:
|
||||
raise TypeError("Can't pass arguments to __new__ "
|
||||
"without overriding __init__")
|
||||
return super(_Abstract, cls).__new__(cls)
|
||||
|
||||
@classmethod
|
||||
def __subclasshook__(cls, subclass):
|
||||
"""Abstract classes can override this to customize issubclass().
|
||||
|
||||
This is invoked early on by __subclasscheck__() below. It
|
||||
should return True, False or NotImplemented. If it returns
|
||||
NotImplemented, the normal algorithm is used. Otherwise, it
|
||||
overrides the normal algorithm (and the outcome is cached).
|
||||
"""
|
||||
return NotImplemented
|
||||
|
||||
|
||||
def _fix_bases(bases):
|
||||
"""Helper method that inserts _Abstract in the bases if needed."""
|
||||
for base in bases:
|
||||
if issubclass(base, _Abstract):
|
||||
# _Abstract is already a base (maybe indirectly)
|
||||
return bases
|
||||
if object in bases:
|
||||
# Replace object with _Abstract
|
||||
return tuple([_Abstract if base is object else base
|
||||
for base in bases])
|
||||
# Append _Abstract to the end
|
||||
return bases + (_Abstract,)
|
||||
|
||||
|
||||
class ABCMeta(type):
|
||||
|
||||
"""Metaclass for defining Abstract Base Classes (ABCs).
|
||||
|
@ -119,7 +73,6 @@ class ABCMeta(type):
|
|||
_abc_invalidation_counter = 0
|
||||
|
||||
def __new__(mcls, name, bases, namespace):
|
||||
bases = _fix_bases(bases)
|
||||
cls = super(ABCMeta, mcls).__new__(mcls, name, bases, namespace)
|
||||
# Compute set of abstract method names
|
||||
abstracts = set(name
|
||||
|
@ -130,7 +83,7 @@ class ABCMeta(type):
|
|||
value = getattr(cls, name, None)
|
||||
if getattr(value, "__isabstractmethod__", False):
|
||||
abstracts.add(name)
|
||||
cls.__abstractmethods__ = abstracts
|
||||
cls.__abstractmethods__ = frozenset(abstracts)
|
||||
# Set up inheritance registry
|
||||
cls._abc_registry = set()
|
||||
cls._abc_cache = set()
|
||||
|
|
|
@ -209,6 +209,7 @@ Instead, you can get the same information from the list type:
|
|||
'__setitem__',
|
||||
'__setslice__',
|
||||
'__str__',
|
||||
'__subclasshook__',
|
||||
'append',
|
||||
'count',
|
||||
'extend',
|
||||
|
|
|
@ -305,6 +305,40 @@ type_set_module(PyTypeObject *type, PyObject *value, void *context)
|
|||
return PyDict_SetItemString(type->tp_dict, "__module__", value);
|
||||
}
|
||||
|
||||
static PyObject *
|
||||
type_abstractmethods(PyTypeObject *type, void *context)
|
||||
{
|
||||
PyObject *mod = PyDict_GetItemString(type->tp_dict,
|
||||
"__abstractmethods__");
|
||||
if (!mod) {
|
||||
PyErr_Format(PyExc_AttributeError, "__abstractmethods__");
|
||||
return NULL;
|
||||
}
|
||||
Py_XINCREF(mod);
|
||||
return mod;
|
||||
}
|
||||
|
||||
static int
|
||||
type_set_abstractmethods(PyTypeObject *type, PyObject *value, void *context)
|
||||
{
|
||||
/* __abstractmethods__ should only be set once on a type, in
|
||||
abc.ABCMeta.__new__, so this function doesn't do anything
|
||||
special to update subclasses.
|
||||
*/
|
||||
int res = PyDict_SetItemString(type->tp_dict,
|
||||
"__abstractmethods__", value);
|
||||
if (res == 0) {
|
||||
type_modified(type);
|
||||
if (value && PyObject_IsTrue(value)) {
|
||||
type->tp_flags |= Py_TPFLAGS_IS_ABSTRACT;
|
||||
}
|
||||
else {
|
||||
type->tp_flags &= ~Py_TPFLAGS_IS_ABSTRACT;
|
||||
}
|
||||
}
|
||||
return res;
|
||||
}
|
||||
|
||||
static PyObject *
|
||||
type_get_bases(PyTypeObject *type, void *context)
|
||||
{
|
||||
|
@ -542,6 +576,8 @@ static PyGetSetDef type_getsets[] = {
|
|||
{"__name__", (getter)type_name, (setter)type_set_name, NULL},
|
||||
{"__bases__", (getter)type_get_bases, (setter)type_set_bases, NULL},
|
||||
{"__module__", (getter)type_module, (setter)type_set_module, NULL},
|
||||
{"__abstractmethods__", (getter)type_abstractmethods,
|
||||
(setter)type_set_abstractmethods, NULL},
|
||||
{"__dict__", (getter)type_dict, NULL, NULL},
|
||||
{"__doc__", (getter)type_get_doc, NULL, NULL},
|
||||
{0}
|
||||
|
@ -2749,6 +2785,56 @@ object_new(PyTypeObject *type, PyObject *args, PyObject *kwds)
|
|||
}
|
||||
if (err < 0)
|
||||
return NULL;
|
||||
|
||||
if (type->tp_flags & Py_TPFLAGS_IS_ABSTRACT) {
|
||||
static PyObject *comma = NULL;
|
||||
PyObject *abstract_methods = NULL;
|
||||
PyObject *builtins;
|
||||
PyObject *sorted;
|
||||
PyObject *sorted_methods = NULL;
|
||||
PyObject *joined = NULL;
|
||||
const char *joined_str;
|
||||
|
||||
/* Compute ", ".join(sorted(type.__abstractmethods__))
|
||||
into joined. */
|
||||
abstract_methods = type_abstractmethods(type, NULL);
|
||||
if (abstract_methods == NULL)
|
||||
goto error;
|
||||
builtins = PyEval_GetBuiltins();
|
||||
if (builtins == NULL)
|
||||
goto error;
|
||||
sorted = PyDict_GetItemString(builtins, "sorted");
|
||||
if (sorted == NULL)
|
||||
goto error;
|
||||
sorted_methods = PyObject_CallFunctionObjArgs(sorted,
|
||||
abstract_methods,
|
||||
NULL);
|
||||
if (sorted_methods == NULL)
|
||||
goto error;
|
||||
if (comma == NULL) {
|
||||
comma = PyString_InternFromString(", ");
|
||||
if (comma == NULL)
|
||||
goto error;
|
||||
}
|
||||
joined = PyObject_CallMethod(comma, "join",
|
||||
"O", sorted_methods);
|
||||
if (joined == NULL)
|
||||
goto error;
|
||||
joined_str = PyString_AsString(joined);
|
||||
if (joined_str == NULL)
|
||||
goto error;
|
||||
|
||||
PyErr_Format(PyExc_TypeError,
|
||||
"Can't instantiate abstract class %s "
|
||||
"with abstract methods %s",
|
||||
type->tp_name,
|
||||
joined_str);
|
||||
error:
|
||||
Py_XDECREF(joined);
|
||||
Py_XDECREF(sorted_methods);
|
||||
Py_XDECREF(abstract_methods);
|
||||
return NULL;
|
||||
}
|
||||
return type->tp_alloc(type, 0);
|
||||
}
|
||||
|
||||
|
@ -3210,6 +3296,21 @@ object_reduce_ex(PyObject *self, PyObject *args)
|
|||
return _common_reduce(self, proto);
|
||||
}
|
||||
|
||||
static PyObject *
|
||||
object_subclasshook(PyObject *cls, PyObject *args)
|
||||
{
|
||||
Py_INCREF(Py_NotImplemented);
|
||||
return Py_NotImplemented;
|
||||
}
|
||||
|
||||
PyDoc_STRVAR(object_subclasshook_doc,
|
||||
"Abstract classes can override this to customize issubclass().\n"
|
||||
"\n"
|
||||
"This is invoked early on by abc.ABCMeta.__subclasscheck__().\n"
|
||||
"It should return True, False or NotImplemented. If it returns\n"
|
||||
"NotImplemented, the normal algorithm is used. Otherwise, it\n"
|
||||
"overrides the normal algorithm (and the outcome is cached).\n");
|
||||
|
||||
/*
|
||||
from PEP 3101, this code implements:
|
||||
|
||||
|
@ -3259,6 +3360,8 @@ static PyMethodDef object_methods[] = {
|
|||
PyDoc_STR("helper for pickle")},
|
||||
{"__reduce__", object_reduce, METH_VARARGS,
|
||||
PyDoc_STR("helper for pickle")},
|
||||
{"__subclasshook__", object_subclasshook, METH_CLASS | METH_VARARGS,
|
||||
object_subclasshook_doc},
|
||||
{"__format__", object_format, METH_VARARGS,
|
||||
PyDoc_STR("default object formatter")},
|
||||
{0}
|
||||
|
|
Loading…
Reference in New Issue