Skip to content

Commit 635ca87

Browse files
committed
fix: pep8 refactor
1 parent 1e27175 commit 635ca87

1 file changed

Lines changed: 98 additions & 63 deletions

File tree

src/bad_style.py

Lines changed: 98 additions & 63 deletions
Original file line numberDiff line numberDiff line change
@@ -1,86 +1,121 @@
1-
# NOTE: This file is intentionally written with poor style for the exam.
2-
# Your task: refactor to be PEP 8-friendly while keeping behavior unchanged.
1+
"""CLI tool for simple statistics (exam task).
32
4-
import math,os,sys
3+
This module is intentionally included in the exam with poor style in the
4+
starter version. The candidate should refactor it to be PEP 8 friendly
5+
without changing its observable behavior.
6+
"""
57

8+
from __future__ import annotations
69

7-
def calc(Arr,mode="mean",round_to=2, allow_negative=False):
8-
"""
9-
do calculation for mean/median/std of list.
10-
mode can be: mean,median,std. round_to means rounding digits.
11-
allow_negative False means raise if negative number in Arr
10+
import math
11+
import os
12+
import sys
13+
from collections.abc import Iterable
14+
15+
16+
def calc(
17+
arr: Iterable[float] | None,
18+
mode: str = "mean",
19+
round_to: int = 2,
20+
allow_negative: bool = False,
21+
) -> float | None:
22+
"""Calculate mean/median/std for a sequence.
23+
24+
Args:
25+
arr: Input numbers. If None or empty, return None.
26+
mode: One of "mean", "median", "std".
27+
round_to: Number of decimal digits to round to.
28+
allow_negative: If False, raise Exception when encountering negative value.
29+
30+
Returns:
31+
Rounded statistic value, or None if arr is None/empty.
32+
33+
Raises:
34+
Exception: When allow_negative is False and a negative value appears.
35+
ValueError: When mode is unknown.
1236
"""
13-
if Arr==None or len(Arr)==0: return None
14-
for x in Arr:
15-
if (not allow_negative) and x<0:
16-
raise Exception("negative not allowed: "+str(x))
17-
18-
if mode=="mean":
19-
s=0
20-
for x in Arr: s+=x
21-
v=s/len(Arr)
22-
elif mode=="median":
23-
B=sorted(Arr)
24-
n=len(B)
25-
mid=n//2
26-
if n%2==1: v=B[mid]
27-
else: v=(B[mid-1]+B[mid])/2
28-
elif mode=="std":
29-
m=calc(Arr,"mean",round_to,allow_negative)
30-
s=0
31-
for x in Arr:
32-
s+=(x-m)**2
33-
v=math.sqrt(s/len(Arr))
37+
if arr is None:
38+
return None
39+
40+
values = list(arr)
41+
if not values:
42+
return None
43+
44+
if not allow_negative:
45+
for x in values:
46+
if x < 0:
47+
# Keep behavior compatible with the original tests.
48+
raise Exception(f"negative not allowed: {x}")
49+
50+
if mode == "mean":
51+
value = sum(values) / len(values)
52+
elif mode == "median":
53+
sorted_values = sorted(values)
54+
n = len(sorted_values)
55+
mid = n // 2
56+
if n % 2 == 1:
57+
value = sorted_values[mid]
58+
else:
59+
value = (sorted_values[mid - 1] + sorted_values[mid]) / 2
60+
elif mode == "std":
61+
mean_value = calc(values, "mean", round_to, allow_negative)
62+
assert mean_value is not None
63+
value = math.sqrt(sum((x - mean_value) ** 2 for x in values) / len(values))
3464
else:
35-
raise ValueError("Unknown mode:"+mode)
65+
raise ValueError(f"Unknown mode:{mode}")
3666

37-
return round(v,round_to)
67+
return round(value, round_to)
3868

3969

40-
def parse_numbers(text):
41-
# expects "1,2,3" or "1 2 3" but does it badly
42-
if text is None: return []
43-
t=text.replace(","," ").replace("\t"," ")
44-
parts=t.split(" ")
45-
out=[]
46-
for p in parts:
47-
if p.strip()=="":
48-
continue
49-
out.append(float(p))
50-
return out
70+
def parse_numbers(text: str | None) -> list[float]:
71+
"""Parse numbers from a string.
72+
73+
Accept input like:
74+
- "1,2,3"
75+
- "1 2 3"
76+
- "1\\t2\\t3"
77+
"""
78+
if text is None:
79+
return []
80+
81+
normalized = text.replace(",", " ").replace("\t", " ")
82+
return [float(p) for p in normalized.split()]
5183

5284

53-
def read_file(path):
54-
# reads first line
55-
f=open(path,"r")
56-
line=f.readline()
57-
f.close()
58-
return line
85+
def read_file(path: str) -> str:
86+
"""Read the first line from the file."""
87+
with open(path, encoding="utf-8") as f:
88+
return f.readline()
5989

6090

61-
def main(argv=None):
62-
if argv is None: argv=sys.argv
63-
if len(argv)<3:
91+
def main(argv: list[str] | None = None) -> int:
92+
"""Entry point for CLI.
93+
94+
Usage:
95+
python -m src.bad_style <mode> <numbers_or_file_path> [--file]
96+
"""
97+
if argv is None:
98+
argv = sys.argv
99+
100+
if len(argv) < 3:
64101
print("Usage: python -m src.bad_style <mode> <numbers_or_file_path> [--file]")
65102
return 2
66103

67-
mode=argv[1]
68-
value=argv[2]
69-
is_file=False
70-
if len(argv)>3 and argv[3]=="--file":
71-
is_file=True
104+
mode = argv[1]
105+
value = argv[2]
106+
is_file = len(argv) > 3 and argv[3] == "--file"
72107

73108
if is_file:
74109
if not os.path.exists(value):
75-
print("file not found:"+value)
110+
print(f"file not found:{value}")
76111
return 2
77-
value=read_file(value)
112+
value = read_file(value)
78113

79-
nums=parse_numbers(value)
80-
r=calc(nums,mode=mode)
81-
print(r)
114+
nums = parse_numbers(value)
115+
result = calc(nums, mode=mode)
116+
print(result)
82117
return 0
83118

84119

85-
if __name__=="__main__":
86-
raise SystemExit(main())
120+
if __name__ == "__main__":
121+
raise SystemExit(main())

0 commit comments

Comments
 (0)