Skip to content

Commit e0d1c5d

Browse files
author
Guido van Rossum
committed
Improve speed of Generic.__new__. Both PY2 and PY3. Fixes #196.
1 parent 6a6119c commit e0d1c5d

File tree

2 files changed

+54
-21
lines changed

2 files changed

+54
-21
lines changed

python2/typing.py

Lines changed: 27 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -891,6 +891,20 @@ def _geqv(a, b):
891891
return _gorg(a) is _gorg(b)
892892

893893

894+
def _next_in_mro(cls):
895+
"""Helper for Generic.__new__.
896+
897+
Returns the class after the last occurrence of Generic or
898+
Generic[...] in cls.__mro__.
899+
"""
900+
next_in_mro = object
901+
# Look for the last occurrence of Generic or Generic[...].
902+
for i, c in enumerate(cls.__mro__[:-1]):
903+
if isinstance(c, GenericMeta) and _gorg(c) is Generic:
904+
next_in_mro = cls.__mro__[i+1]
905+
return next_in_mro
906+
907+
894908
class GenericMeta(TypingMeta, abc.ABCMeta):
895909
"""Metaclass for generic types."""
896910

@@ -919,9 +933,6 @@ def __new__(cls, name, bases, namespace,
919933
# and reject multiple Generic[...].
920934
gvars = None
921935
for base in bases:
922-
if base is object:
923-
# Avoid checking for Generic in its own definition.
924-
continue
925936
if base is Generic:
926937
raise TypeError("Cannot inherit from plain Generic")
927938
if (isinstance(base, GenericMeta) and
@@ -950,6 +961,8 @@ def __new__(cls, name, bases, namespace,
950961
self.__extra__ = extra
951962
# Else __extra__ is inherited, eventually from the
952963
# (meta-)class default above.
964+
# Speed hack (https://github.com/python/typing/issues/196).
965+
self.__next_in_mro__ = _next_in_mro(self)
953966
return self
954967

955968
def _get_type_vars(self, tvars):
@@ -1077,6 +1090,10 @@ def __subclasscheck__(self, cls):
10771090
return issubclass(cls, self.__extra__)
10781091

10791092

1093+
# Prevent checks for Generic to crash when defining Generic.
1094+
Generic = None
1095+
1096+
10801097
class Generic(object):
10811098
"""Abstract base class for generic types.
10821099
@@ -1102,16 +1119,13 @@ def lookup_name(mapping: Mapping[KT, VT], key: KT, default: VT) -> VT:
11021119
__slots__ = ()
11031120

11041121
def __new__(cls, *args, **kwds):
1105-
next_in_mro = object
1106-
# Look for the last occurrence of Generic or Generic[...].
1107-
for i, c in enumerate(cls.__mro__[:-1]):
1108-
if isinstance(c, GenericMeta) and _gorg(c) is Generic:
1109-
next_in_mro = cls.__mro__[i+1]
1110-
origin = _gorg(cls)
1111-
obj = next_in_mro.__new__(origin)
1112-
if origin is not cls:
1122+
if cls.__origin__ is None:
1123+
return cls.__next_in_mro__.__new__(cls)
1124+
else:
1125+
origin = _gorg(cls)
1126+
obj = cls.__next_in_mro__.__new__(origin)
11131127
obj.__init__(*args, **kwds)
1114-
return obj
1128+
return obj
11151129

11161130

11171131
def cast(typ, val):
@@ -1305,6 +1319,7 @@ def _get_protocol_attrs(self):
13051319
attr != '__args__' and
13061320
attr != '__slots__' and
13071321
attr != '_get_protocol_attrs' and
1322+
attr != '__next_in_mro__' and
13081323
attr != '__parameters__' and
13091324
attr != '__origin__' and
13101325
attr != '__module__'):

src/typing.py

Lines changed: 27 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -873,6 +873,20 @@ def _geqv(a, b):
873873
return _gorg(a) is _gorg(b)
874874

875875

876+
def _next_in_mro(cls):
877+
"""Helper for Generic.__new__.
878+
879+
Returns the class after the last occurrence of Generic or
880+
Generic[...] in cls.__mro__.
881+
"""
882+
next_in_mro = object
883+
# Look for the last occurrence of Generic or Generic[...].
884+
for i, c in enumerate(cls.__mro__[:-1]):
885+
if isinstance(c, GenericMeta) and _gorg(c) is Generic:
886+
next_in_mro = cls.__mro__[i+1]
887+
return next_in_mro
888+
889+
876890
class GenericMeta(TypingMeta, abc.ABCMeta):
877891
"""Metaclass for generic types."""
878892

@@ -929,6 +943,8 @@ def __new__(cls, name, bases, namespace,
929943
self.__extra__ = extra
930944
# Else __extra__ is inherited, eventually from the
931945
# (meta-)class default above.
946+
# Speed hack (https://github.com/python/typing/issues/196).
947+
self.__next_in_mro__ = _next_in_mro(self)
932948
return self
933949

934950
def _get_type_vars(self, tvars):
@@ -1056,6 +1072,10 @@ def __subclasscheck__(self, cls):
10561072
return issubclass(cls, self.__extra__)
10571073

10581074

1075+
# Prevent checks for Generic to crash when defining Generic.
1076+
Generic = None
1077+
1078+
10591079
class Generic(metaclass=GenericMeta):
10601080
"""Abstract base class for generic types.
10611081
@@ -1080,16 +1100,13 @@ def lookup_name(mapping: Mapping[KT, VT], key: KT, default: VT) -> VT:
10801100
__slots__ = ()
10811101

10821102
def __new__(cls, *args, **kwds):
1083-
next_in_mro = object
1084-
# Look for the last occurrence of Generic or Generic[...].
1085-
for i, c in enumerate(cls.__mro__[:-1]):
1086-
if isinstance(c, GenericMeta) and _gorg(c) is Generic:
1087-
next_in_mro = cls.__mro__[i+1]
1088-
origin = _gorg(cls)
1089-
obj = next_in_mro.__new__(origin)
1090-
if origin is not cls:
1103+
if cls.__origin__ is None:
1104+
return cls.__next_in_mro__.__new__(cls)
1105+
else:
1106+
origin = _gorg(cls)
1107+
obj = cls.__next_in_mro__.__new__(origin)
10911108
obj.__init__(*args, **kwds)
1092-
return obj
1109+
return obj
10931110

10941111

10951112
def cast(typ, val):
@@ -1283,6 +1300,7 @@ def _get_protocol_attrs(self):
12831300
attr != '__args__' and
12841301
attr != '__slots__' and
12851302
attr != '_get_protocol_attrs' and
1303+
attr != '__next_in_mro__' and
12861304
attr != '__parameters__' and
12871305
attr != '__origin__' and
12881306
attr != '__module__'):

0 commit comments

Comments
 (0)