Skip to content

Commit 51ca233

Browse files
committed
pythongh-74690: typing: Don't unnecessarily call _get_protocol_attrs twice in _ProtocolMeta.__instancecheck__
1 parent 01a49d1 commit 51ca233

File tree

2 files changed

+13
-6
lines changed

2 files changed

+13
-6
lines changed

Lib/typing.py

Lines changed: 11 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1931,9 +1931,9 @@ def _get_protocol_attrs(cls):
19311931
return attrs
19321932

19331933

1934-
def _is_callable_members_only(cls):
1934+
def _is_callable_members_only(cls, protocol_attrs):
19351935
# PEP 544 prohibits using issubclass() with protocols that have non-method members.
1936-
return all(callable(getattr(cls, attr, None)) for attr in _get_protocol_attrs(cls))
1936+
return all(callable(getattr(cls, attr, None)) for attr in protocol_attrs)
19371937

19381938

19391939
def _no_init_or_replace_init(self, *args, **kwargs):
@@ -2008,16 +2008,18 @@ def __instancecheck__(cls, instance):
20082008
raise TypeError("Instance and class checks can only be used with"
20092009
" @runtime_checkable protocols")
20102010

2011+
protocol_attrs = _get_protocol_attrs(cls)
2012+
20112013
if ((not getattr(cls, '_is_protocol', False) or
2012-
_is_callable_members_only(cls)) and
2014+
_is_callable_members_only(cls, protocol_attrs)) and
20132015
issubclass(instance.__class__, cls)):
20142016
return True
20152017
if cls._is_protocol:
20162018
if all(hasattr(instance, attr) and
20172019
# All *methods* can be blocked by setting them to None.
20182020
(not callable(getattr(cls, attr, None)) or
20192021
getattr(instance, attr) is not None)
2020-
for attr in _get_protocol_attrs(cls)):
2022+
for attr in protocol_attrs):
20212023
return True
20222024
return super().__instancecheck__(instance)
20232025

@@ -2074,7 +2076,10 @@ def _proto_hook(other):
20742076
return NotImplemented
20752077
raise TypeError("Instance and class checks can only be used with"
20762078
" @runtime_checkable protocols")
2077-
if not _is_callable_members_only(cls):
2079+
2080+
protocol_attrs = _get_protocol_attrs(cls)
2081+
2082+
if not _is_callable_members_only(cls, protocol_attrs):
20782083
if _allow_reckless_class_checks():
20792084
return NotImplemented
20802085
raise TypeError("Protocols with non-method members"
@@ -2084,7 +2089,7 @@ def _proto_hook(other):
20842089
raise TypeError('issubclass() arg 1 must be a class')
20852090

20862091
# Second, perform the actual structural compatibility check.
2087-
for attr in _get_protocol_attrs(cls):
2092+
for attr in protocol_attrs:
20882093
for base in other.__mro__:
20892094
# Check if the members appears in the class dictionary...
20902095
if attr in base.__dict__:
Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
Speed up :func:`isinstance` checks against :func:`runtime-checkable
2+
protocols <typing.runtime_checkable>` by up to 6x. Patch by Alex Waygood

0 commit comments

Comments
 (0)