Doc: match statement in style guide (#5187)

* Test: add micro benchmark for match

* Doc: add 'match' to python style guide
This commit is contained in:
black-sliver
2025-07-14 07:22:10 +00:00
committed by GitHub
parent a9b35de7ee
commit ec3f168a09
2 changed files with 70 additions and 0 deletions

View File

@@ -29,6 +29,10 @@
* New classes, attributes, and methods in core code should have docstrings that follow
[reST style](https://peps.python.org/pep-0287/).
* Worlds that do not follow PEP8 should still have a consistent style across its files to make reading easier.
* [Match statements](https://docs.python.org/3/tutorial/controlflow.html#tut-match)
may be used instead of `if`-`elif` if they result in nicer code, or they actually use pattern matching.
Beware of the performance: they are not `goto`s, but `if`-`elif` under the hood, and you may have less control. When
in doubt, just don't use it.
## Markdown

66
test/benchmark/match.py Normal file
View File

@@ -0,0 +1,66 @@
"""Micro benchmark comparing match as "switch" with if-elif and dict access"""
from timeit import timeit
def make_match(count: int) -> str:
code = f"for val in range({count}):\n match val:\n"
for n in range(count):
m = n + 1
code += f" case {n}:\n"
code += f" res = {m}\n"
return code
def make_elif(count: int) -> str:
code = f"for val in range({count}):\n"
for n in range(count):
m = n + 1
code += f" {'' if n == 0 else 'el'}if val == {n}:\n"
code += f" res = {m}\n"
return code
def make_dict(count: int, mode: str) -> str:
if mode == "value":
code = "dct = {\n"
for n in range(count):
m = n + 1
code += f" {n}: {m},\n"
code += "}\n"
code += f"for val in range({count}):\n res = dct[val]"
return code
elif mode == "call":
code = ""
for n in range(count):
m = n + 1
code += f"def func{n}():\n val = {m}\n\n"
code += "dct = {\n"
for n in range(count):
code += f" {n}: func{n},\n"
code += "}\n"
code += f"for val in range({count}):\n dct[val]()"
return code
return ""
def timeit_best_of_5(stmt: str, setup: str = "pass") -> float:
"""
Benchmark some code, returning the best of 5 runs.
:param stmt: Code to benchmark
:param setup: Optional code to set up environment
:return: Time taken in microseconds
"""
return min(timeit(stmt, setup, number=10000, globals={}) for _ in range(5)) * 100
def main() -> None:
for count in (3, 5, 8, 10, 20, 30):
print(f"value of {count:-2} with match: {timeit_best_of_5(make_match(count)) / count:.3f} us")
print(f"value of {count:-2} with elif: {timeit_best_of_5(make_elif(count)) / count:.3f} us")
print(f"value of {count:-2} with dict: {timeit_best_of_5(make_dict(count, 'value')) / count:.3f} us")
print(f"call of {count:-2} with dict: {timeit_best_of_5(make_dict(count, 'call')) / count:.3f} us")
if __name__ == "__main__":
main()