-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathbenchmarks.py
More file actions
68 lines (50 loc) · 2.34 KB
/
Copy pathbenchmarks.py
File metadata and controls
68 lines (50 loc) · 2.34 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
"""
Chapter 13: Benchmarks — Duck Typing vs Goose Typing (isinstance)
=================================================================
Comparing the runtime cost of checking for an attribute (Duck typing)
vs using isinstance() against an Abstract Base Class (Goose typing).
"""
import sys
import timeit
import collections.abc
sys.stdout.reconfigure(encoding="utf-8")
def section(title: str) -> None:
print(f"\n{'=' * 55}\n {title}\n{'=' * 55}")
# ── Setup ────────────────────────────────────────────────────────────────────
SETUP_CODE = """
import collections.abc
class DuckSequence:
def __len__(self): return 0
def __getitem__(self, i): raise IndexError
class GooseSequence(collections.abc.Sequence):
def __len__(self): return 0
def __getitem__(self, i): raise IndexError
duck = DuckSequence()
goose = GooseSequence()
"""
# Duck typing: "Does it have a __len__?"
TEST_DUCK = """
has_len = hasattr(duck, '__len__')
"""
# Goose typing: "Is it an instance of Sequence?"
TEST_GOOSE = """
is_seq = isinstance(goose, collections.abc.Sequence)
"""
# ── Benchmarks ───────────────────────────────────────────────────────────────
def run_benchmarks():
section("Benchmark: hasattr() vs isinstance(ABC)")
iters = 5_000_000
print(f" (Running {iters:,} iterations each)\n")
time_duck = timeit.timeit(TEST_DUCK, setup=SETUP_CODE, number=iters)
time_goose = timeit.timeit(TEST_GOOSE, setup=SETUP_CODE, number=iters)
print(f" {'Method':<35} {'Time (s)':>15}")
print(" " + "-" * 51)
print(f" {'Duck: hasattr(obj, __len__)':<35} {time_duck:>15.4f}")
print(f" {'Goose: isinstance(obj, Sequence)':<35} {time_goose:>15.4f}")
slower = time_goose / time_duck if time_duck > 0 else 0
print(f"\n Conclusion: isinstance() against an ABC is ~{slower:.1f}x slower.")
print(" Why? `hasattr` is a direct dictionary lookup on the C struct.")
print(" `isinstance` against an ABC must traverse the MRO, invoke")
print(" the ABCMeta metaclass, and often execute __subclasshook__.")
if __name__ == "__main__":
run_benchmarks()