From ae46352bb0e2e6c8639ea8d1888e86378474c93c Mon Sep 17 00:00:00 2001 From: Sude Naz Ay Date: Wed, 28 Oct 2026 21:09:14 +0300 Subject: [PATCH 1/5] Create decorators_sude_naz_ay.py --- Week04/decorators_sude_naz_ay.py | 30 ++++++++++++++++++++++++++++++ 1 file changed, 30 insertions(+) create mode 100644 Week04/decorators_sude_naz_ay.py diff --git a/Week04/decorators_sude_naz_ay.py b/Week04/decorators_sude_naz_ay.py new file mode 100644 index 00000000..2660d467 --- /dev/null +++ b/Week04/decorators_sude_naz_ay.py @@ -0,0 +1,30 @@ +import functools +import time +import tracemalloc + + +def performance(fn): + @functools.wraps(fn) + def wrapper(*args, **kwargs): + performance.counter += 1 + + tracemalloc.start() + start_time = time.perf_counter() + + result = fn(*args, **kwargs) + + end_time = time.perf_counter() + current, peak = tracemalloc.get_traced_memory() + tracemalloc.stop() + + performance.total_time += end_time - start_time + performance.total_mem += peak + + return result + + return wrapper + + +performance.counter = 0 +performance.total_time = 0.0 +performance.total_mem = 0 From 515f7aefbf869dc74a726f21542b6f176ff5eef6 Mon Sep 17 00:00:00 2001 From: Sude Naz Ay Date: Wed, 28 Oct 2026 21:20:36 +0300 Subject: [PATCH 2/5] Create functions_sude_naz_ay.py --- Week04/functions_sude_naz_ay.py | 71 +++++++++++++++++++++++++++++++++ 1 file changed, 71 insertions(+) create mode 100644 Week04/functions_sude_naz_ay.py diff --git a/Week04/functions_sude_naz_ay.py b/Week04/functions_sude_naz_ay.py new file mode 100644 index 00000000..f301f4bc --- /dev/null +++ b/Week04/functions_sude_naz_ay.py @@ -0,0 +1,71 @@ + + +import sys + +# x positional-only (default 0), e positional-or-keyword (default 1) +custom_power = lambda x=0, /, e=1: x ** e + + +def custom_equation( + x: int = 0, + y: int = 0, + /, + a: int = 1, + b: int = 1, + *, + c: int = 1, +) -> float: + """ + Calculate (x**a + y**b) / c. + + :param x: first base (positional-only) + :type x: int + :param y: second base (positional-only) + :type y: int + :param a: exponent of x (positional-or-keyword) + :type a: int + :param b: exponent of y (positional-or-keyword) + :type b: int + :param c: divisor (keyword-only) + :type c: int + :return: the value of (x**a + y**b) / c + :rtype: float + """ + return (x ** a + y ** b) / c + + +_call_counts: dict[str, int] = {} +_total_calls: int = 0 + + +def fn_w_counter() -> (int, dict[str, int]): + """ + Count the calls made to this function together with caller information. + + :return: total number of calls and a dictionary mapping each caller's + ``__name__`` to the number of calls made from that caller + :rtype: tuple[int, dict[str, int]] + """ + global _total_calls + caller = sys._getframe(1).f_globals.get("__name__", "") + _total_calls += 1 + _call_counts[caller] = _call_counts.get(caller, 0) + 1 + return _total_calls, dict(_call_counts) + + +if __name__ == "__main__": + assert custom_power(2) == 2 + assert custom_power(2, 3) == 8 + assert custom_power(2, e=2) == 4 + + assert custom_equation(2, 3) == 5.0 + assert custom_equation(2, 3, 2) == 7.0 + assert custom_equation(2, 3, 2, 3) == 31.0 + assert custom_equation(3, 5, a=2, b=3, c=4) == 33.5 + assert custom_equation(3, 5, 2, b=3, c=4) == 33.5 + assert custom_equation(3, 5, 2, 3, c=4) == 33.5 + + for i in range(10): + fn_w_counter() + assert fn_w_counter() == (11, {'__main__': 11}) + print("All examples passed.") From 814010a338d78c56182901d91683af578fa55443 Mon Sep 17 00:00:00 2001 From: Sude Naz Ay Date: Wed, 28 Oct 2026 21:23:31 +0300 Subject: [PATCH 3/5] Update decorators_sude_naz_ay.py --- Week04/decorators_sude_naz_ay.py | 54 ++++++++++++++++++++++++-------- 1 file changed, 41 insertions(+), 13 deletions(-) diff --git a/Week04/decorators_sude_naz_ay.py b/Week04/decorators_sude_naz_ay.py index 2660d467..9d4514b8 100644 --- a/Week04/decorators_sude_naz_ay.py +++ b/Week04/decorators_sude_naz_ay.py @@ -4,23 +4,40 @@ def performance(fn): - @functools.wraps(fn) - def wrapper(*args, **kwargs): - performance.counter += 1 - - tracemalloc.start() - start_time = time.perf_counter() + """ + Decorator that measures execution time and memory of the wrapped function. - result = fn(*args, **kwargs) + Statistics are stored as attributes on the decorator itself: - end_time = time.perf_counter() - current, peak = tracemalloc.get_traced_memory() - tracemalloc.stop() + - ``performance.counter``: number of decorated-function calls + - ``performance.total_time``: total time spent in seconds + - ``performance.total_mem``: total memory consumed in bytes - performance.total_time += end_time - start_time - performance.total_mem += peak + :param fn: the function to decorate + :type fn: callable + :return: the wrapped function + :rtype: callable + """ - return result + @functools.wraps(fn) + def wrapper(*args, **kwargs): + started_here = not tracemalloc.is_tracing() + if started_here: + tracemalloc.start() + tracemalloc.reset_peak() + mem_before = tracemalloc.get_traced_memory()[0] + start = time.perf_counter() + + try: + return fn(*args, **kwargs) + finally: + elapsed = time.perf_counter() - start + peak = tracemalloc.get_traced_memory()[1] + if started_here: + tracemalloc.stop() + performance.counter += 1 + performance.total_time += elapsed + performance.total_mem += max(peak - mem_before, 0) return wrapper @@ -28,3 +45,14 @@ def wrapper(*args, **kwargs): performance.counter = 0 performance.total_time = 0.0 performance.total_mem = 0 + + +if __name__ == "__main__": + + @performance + def build_list(n: int) -> list[int]: + return [i * i for i in range(n)] + + build_list(100_000) + build_list(50_000) + print(performance.counter, performance.total_time, performance.total_mem) From 3aede767148d861d4f47d85bc1f2cf818a233869 Mon Sep 17 00:00:00 2001 From: Sude Naz Ay Date: Wed, 28 Oct 2026 21:25:02 +0300 Subject: [PATCH 4/5] Update functions_sude_naz_ay.py --- Week04/functions_sude_naz_ay.py | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/Week04/functions_sude_naz_ay.py b/Week04/functions_sude_naz_ay.py index f301f4bc..cbb7402e 100644 --- a/Week04/functions_sude_naz_ay.py +++ b/Week04/functions_sude_naz_ay.py @@ -1,6 +1,4 @@ - - -import sys +"""Week04 - Introduction to Python: functions.""" # x positional-only (default 0), e positional-or-keyword (default 1) custom_power = lambda x=0, /, e=1: x ** e @@ -47,7 +45,7 @@ def fn_w_counter() -> (int, dict[str, int]): :rtype: tuple[int, dict[str, int]] """ global _total_calls - caller = sys._getframe(1).f_globals.get("__name__", "") + caller = __name__ _total_calls += 1 _call_counts[caller] = _call_counts.get(caller, 0) + 1 return _total_calls, dict(_call_counts) From 11aea60b9767261f202f5c36748a3b5f0a268370 Mon Sep 17 00:00:00 2001 From: Sude Naz Ay Date: Wed, 28 Oct 2026 21:26:10 +0300 Subject: [PATCH 5/5] Update decorators_sude_naz_ay.py --- Week04/decorators_sude_naz_ay.py | 54 ++++++++------------------------ 1 file changed, 13 insertions(+), 41 deletions(-) diff --git a/Week04/decorators_sude_naz_ay.py b/Week04/decorators_sude_naz_ay.py index 9d4514b8..2660d467 100644 --- a/Week04/decorators_sude_naz_ay.py +++ b/Week04/decorators_sude_naz_ay.py @@ -4,40 +4,23 @@ def performance(fn): - """ - Decorator that measures execution time and memory of the wrapped function. + @functools.wraps(fn) + def wrapper(*args, **kwargs): + performance.counter += 1 - Statistics are stored as attributes on the decorator itself: + tracemalloc.start() + start_time = time.perf_counter() - - ``performance.counter``: number of decorated-function calls - - ``performance.total_time``: total time spent in seconds - - ``performance.total_mem``: total memory consumed in bytes + result = fn(*args, **kwargs) - :param fn: the function to decorate - :type fn: callable - :return: the wrapped function - :rtype: callable - """ + end_time = time.perf_counter() + current, peak = tracemalloc.get_traced_memory() + tracemalloc.stop() - @functools.wraps(fn) - def wrapper(*args, **kwargs): - started_here = not tracemalloc.is_tracing() - if started_here: - tracemalloc.start() - tracemalloc.reset_peak() - mem_before = tracemalloc.get_traced_memory()[0] - start = time.perf_counter() - - try: - return fn(*args, **kwargs) - finally: - elapsed = time.perf_counter() - start - peak = tracemalloc.get_traced_memory()[1] - if started_here: - tracemalloc.stop() - performance.counter += 1 - performance.total_time += elapsed - performance.total_mem += max(peak - mem_before, 0) + performance.total_time += end_time - start_time + performance.total_mem += peak + + return result return wrapper @@ -45,14 +28,3 @@ def wrapper(*args, **kwargs): performance.counter = 0 performance.total_time = 0.0 performance.total_mem = 0 - - -if __name__ == "__main__": - - @performance - def build_list(n: int) -> list[int]: - return [i * i for i in range(n)] - - build_list(100_000) - build_list(50_000) - print(performance.counter, performance.total_time, performance.total_mem)