From 2fc18646e046f67fa478693ea33e00710d788856 Mon Sep 17 00:00:00 2001 From: FurkanCetinerr <126960728+FurkanCetinerr@users.noreply.github.com> Date: Tue, 23 Sep 2025 20:50:13 +0300 Subject: [PATCH 1/5] Teslim edildi. --- "Week01/info_furkan_\303\247etiner.py" | 2 ++ 1 file changed, 2 insertions(+) create mode 100644 "Week01/info_furkan_\303\247etiner.py" diff --git "a/Week01/info_furkan_\303\247etiner.py" "b/Week01/info_furkan_\303\247etiner.py" new file mode 100644 index 00000000..652fd78a --- /dev/null +++ "b/Week01/info_furkan_\303\247etiner.py" @@ -0,0 +1,2 @@ +student_id = "210316074" +full_name = "Furkan Çetiner" From 1200eedcdc3aa9eed7b73d65ec864bc97030f4c2 Mon Sep 17 00:00:00 2001 From: FurkanCetinerr <126960728+FurkanCetinerr@users.noreply.github.com> Date: Wed, 7 Jan 2026 19:24:56 +0300 Subject: [PATCH 2/5] All assignments completed between weeks 2 and 5 --- .../info_furkan_cetiner.py | 0 Week02/types_furkan_cetiner.py | 4 ++ Week03/pyramid_furkan_cetiner.py | 9 ++++ Week03/sequences_furkan_cetiner.py | 26 +++++++++++ Week04/decorators_furkan_cetiner.py | 34 +++++++++++++++ Week04/functions_furkan_cetiner.py | 34 +++++++++++++++ Week05/emails_furkan_cetiner.py | 43 +++++++++++++++++++ 7 files changed, 150 insertions(+) rename "Week01/info_furkan_\303\247etiner.py" => Week01/info_furkan_cetiner.py (100%) create mode 100644 Week02/types_furkan_cetiner.py create mode 100644 Week03/pyramid_furkan_cetiner.py create mode 100644 Week03/sequences_furkan_cetiner.py create mode 100644 Week04/decorators_furkan_cetiner.py create mode 100644 Week04/functions_furkan_cetiner.py create mode 100644 Week05/emails_furkan_cetiner.py diff --git "a/Week01/info_furkan_\303\247etiner.py" b/Week01/info_furkan_cetiner.py similarity index 100% rename from "Week01/info_furkan_\303\247etiner.py" rename to Week01/info_furkan_cetiner.py diff --git a/Week02/types_furkan_cetiner.py b/Week02/types_furkan_cetiner.py new file mode 100644 index 00000000..0b011710 --- /dev/null +++ b/Week02/types_furkan_cetiner.py @@ -0,0 +1,4 @@ +my_int = 20 +my_float = 9.11 +my_bool = True +my_complex = 1 + 6j \ No newline at end of file diff --git a/Week03/pyramid_furkan_cetiner.py b/Week03/pyramid_furkan_cetiner.py new file mode 100644 index 00000000..cfff808f --- /dev/null +++ b/Week03/pyramid_furkan_cetiner.py @@ -0,0 +1,9 @@ +def calculate_pyramid_height(number_of_blocks): + height = 0 + used_blocks = 0 + + while used_blocks + (height + 1) <= number_of_blocks: + height += 1 + used_blocks += height + + return height \ No newline at end of file diff --git a/Week03/sequences_furkan_cetiner.py b/Week03/sequences_furkan_cetiner.py new file mode 100644 index 00000000..61fb80a3 --- /dev/null +++ b/Week03/sequences_furkan_cetiner.py @@ -0,0 +1,26 @@ +def remove_duplicates(seq: list) -> list: + + result = [] + for item in seq: + if item not in result: + result.append(item) + return result + + +def list_counts(seq: list) -> dict: + + counts = {} + for item in seq: + if item in counts: + counts[item] += 1 + else: + counts[item] = 1 + return counts + + +def reverse_dict(d: dict) -> dict: + + reversed_dict = {} + for key, value in d.items(): + reversed_dict[value] = key + return reversed_dict \ No newline at end of file diff --git a/Week04/decorators_furkan_cetiner.py b/Week04/decorators_furkan_cetiner.py new file mode 100644 index 00000000..76a900e2 --- /dev/null +++ b/Week04/decorators_furkan_cetiner.py @@ -0,0 +1,34 @@ +import time +import tracemalloc +import functools + +def performance(func): + """ + Fonksiyonun performansını ölçen ve istatistiklerini saklayan dekoratör. + """ + if not hasattr(performance, "counter"): + performance.counter = 0 + performance.total_time = 0.0 + performance.total_mem = 0 + + @functools.wraps(func) + def wrapper(*args, **kwargs): + tracemalloc.start() + start_time = time.perf_counter() + result = func(*args, **kwargs) + end_time = time.perf_counter() + + current_mem, peak_mem = tracemalloc.get_traced_memory() + tracemalloc.stop() + + performance.counter += 1 + performance.total_time += (end_time - start_time) + performance.total_mem += peak_mem + + return result + + return wrapper + +performance.counter = 0 +performance.total_time = 0.0 +performance.total_mem = 0 \ No newline at end of file diff --git a/Week04/functions_furkan_cetiner.py b/Week04/functions_furkan_cetiner.py new file mode 100644 index 00000000..5487a709 --- /dev/null +++ b/Week04/functions_furkan_cetiner.py @@ -0,0 +1,34 @@ +import inspect + +# 1. custom_power: lambda function with positional-only x +custom_power = lambda x=0, /, e=1: x**e + +# 2. custom_equation: positional-only, positional-or-keyword, and keyword-only params +def custom_equation(x: int = 0, y: int = 0, /, a: int = 1, b: int = 1, *, c: int = 1) -> float: + """ + Calculates (x**a + y**b) / c with specific parameter constraints. + :param x: Base 1, positional-only + :param y: Base 2, positional-only + :param a: Exponent 1, positional-or-keyword + :param b: Exponent 2, positional-or-keyword + :param c: Divisor, keyword-only + :returns: Result as float + """ + return float((x**a + y**b) / c) + +# 3. fn_w_counter: tracks total calls and caller names +def fn_w_counter() -> tuple[int, dict]: + """ + Counts the number of calls and tracks the caller (__name__). + """ + if not hasattr(fn_w_counter, "calls"): + fn_w_counter.calls = 0 + fn_w_counter.callers = {} + + caller_frame = inspect.currentframe().f_back + caller_name = caller_frame.f_globals.get('__name__', 'unknown') + + fn_w_counter.calls += 1 + fn_w_counter.callers[caller_name] = fn_w_counter.callers.get(caller_name, 0) + 1 + + return (fn_w_counter.calls, fn_w_counter.callers) \ No newline at end of file diff --git a/Week05/emails_furkan_cetiner.py b/Week05/emails_furkan_cetiner.py new file mode 100644 index 00000000..cdc896dc --- /dev/null +++ b/Week05/emails_furkan_cetiner.py @@ -0,0 +1,43 @@ +import re + +class Emails(list): + """ + Emails class that extends list and validates email addresses. + """ + def __init__(self, email_list): + validated_data = self.validate(email_list) + super().__init__(validated_data) + self.data = list(validated_data) + + @staticmethod + def validate(email_list): + """ + Validates that all items are strings and follow email format. + Removes duplicates while preserving order (or just unique set). + """ + seen = [] + email_regex = r'^[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,}$' + + for email in email_list: + if not isinstance(email, str): + raise ValueError("All items must be strings.") + + if not re.match(email_regex, email): + raise ValueError(f"Invalid email format: {email}") + + if email not in seen: + seen.append(email) + + return seen + + def __repr__(self): + """ + Returns a string representation that can recreate the object. + """ + return f"Emails({list(self)})" + + def __str__(self): + """ + Returns a user-friendly string representation. + """ + return f"Email List: {list(self)}" \ No newline at end of file From a639d8076193964712e7e60909026d86759a77f6 Mon Sep 17 00:00:00 2001 From: FurkanCetinerr <126960728+FurkanCetinerr@users.noreply.github.com> Date: Wed, 7 Jan 2026 19:32:27 +0300 Subject: [PATCH 3/5] fix: specialized dictionary type hint for fn_w_counter --- Week04/functions_furkan_cetiner.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Week04/functions_furkan_cetiner.py b/Week04/functions_furkan_cetiner.py index 5487a709..48b48617 100644 --- a/Week04/functions_furkan_cetiner.py +++ b/Week04/functions_furkan_cetiner.py @@ -17,7 +17,7 @@ def custom_equation(x: int = 0, y: int = 0, /, a: int = 1, b: int = 1, *, c: int return float((x**a + y**b) / c) # 3. fn_w_counter: tracks total calls and caller names -def fn_w_counter() -> tuple[int, dict]: +def fn_w_counter() -> tuple[int, dict[str, int]]: """ Counts the number of calls and tracks the caller (__name__). """ From 313c8ef6a66aac71e2276db844603f1998252dd3 Mon Sep 17 00:00:00 2001 From: FurkanCetinerr <126960728+FurkanCetinerr@users.noreply.github.com> Date: Wed, 7 Jan 2026 19:36:34 +0300 Subject: [PATCH 4/5] fix: changed return annotation to literal tuple to match test requirements --- Week04/functions_furkan_cetiner.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Week04/functions_furkan_cetiner.py b/Week04/functions_furkan_cetiner.py index 48b48617..52a544bd 100644 --- a/Week04/functions_furkan_cetiner.py +++ b/Week04/functions_furkan_cetiner.py @@ -17,7 +17,7 @@ def custom_equation(x: int = 0, y: int = 0, /, a: int = 1, b: int = 1, *, c: int return float((x**a + y**b) / c) # 3. fn_w_counter: tracks total calls and caller names -def fn_w_counter() -> tuple[int, dict[str, int]]: +def fn_w_counter() -> (int, dict[str, int]): """ Counts the number of calls and tracks the caller (__name__). """ From af6dcc3191a6e100167be3cd243f2ecb910ca966 Mon Sep 17 00:00:00 2001 From: FurkanCetinerr <126960728+FurkanCetinerr@users.noreply.github.com> Date: Wed, 7 Jan 2026 19:42:01 +0300 Subject: [PATCH 5/5] fix: changed return annotation to test requirements --- Week04/decorators_furkan_cetiner.py | 74 ++++++++++++++++++----------- Week04/functions_furkan_cetiner.py | 60 ++++++++++++----------- 2 files changed, 77 insertions(+), 57 deletions(-) diff --git a/Week04/decorators_furkan_cetiner.py b/Week04/decorators_furkan_cetiner.py index 76a900e2..08033425 100644 --- a/Week04/decorators_furkan_cetiner.py +++ b/Week04/decorators_furkan_cetiner.py @@ -1,34 +1,52 @@ +from __future__ import annotations + import time import tracemalloc -import functools - -def performance(func): - """ - Fonksiyonun performansını ölçen ve istatistiklerini saklayan dekoratör. - """ - if not hasattr(performance, "counter"): - performance.counter = 0 - performance.total_time = 0.0 - performance.total_mem = 0 - - @functools.wraps(func) - def wrapper(*args, **kwargs): - tracemalloc.start() - start_time = time.perf_counter() - result = func(*args, **kwargs) - end_time = time.perf_counter() - - current_mem, peak_mem = tracemalloc.get_traced_memory() - tracemalloc.stop() - - performance.counter += 1 - performance.total_time += (end_time - start_time) - performance.total_mem += peak_mem +from functools import wraps +from typing import Any, Callable, TypeVar, cast + +F = TypeVar("F", bound=Callable[..., Any]) + + +def performance(func: F) -> F: + + @wraps(func) + def wrapper(*args: Any, **kwargs: Any) -> Any: + # ensure stats exist + wrapper.counter += 1 # type: ignore[attr-defined] + + # time + t0 = time.perf_counter() + + # memory (tracemalloc) + tracing_already = tracemalloc.is_tracing() + if not tracing_already: + tracemalloc.start() + + before_current, before_peak = tracemalloc.get_traced_memory() + try: + result = func(*args, **kwargs) + finally: + after_current, after_peak = tracemalloc.get_traced_memory() + dt = time.perf_counter() - t0 + + wrapper.total_time += dt # type: ignore[attr-defined] + + # Use peak delta as "consumed" approximation for this call + delta_peak = after_peak - before_peak + if delta_peak < 0: + delta_peak = 0 + wrapper.total_mem += int(delta_peak) # type: ignore[attr-defined] + + # Don't disrupt global tracing if it was already enabled + if not tracing_already: + tracemalloc.stop() return result - return wrapper + # required attributes + wrapper.counter = 0 # type: ignore[attr-defined] + wrapper.total_time = 0.0 # type: ignore[attr-defined] + wrapper.total_mem = 0 # type: ignore[attr-defined] -performance.counter = 0 -performance.total_time = 0.0 -performance.total_mem = 0 \ No newline at end of file + return cast(F, wrapper) \ No newline at end of file diff --git a/Week04/functions_furkan_cetiner.py b/Week04/functions_furkan_cetiner.py index 52a544bd..a07d1375 100644 --- a/Week04/functions_furkan_cetiner.py +++ b/Week04/functions_furkan_cetiner.py @@ -1,34 +1,36 @@ +from __future__ import annotations + import inspect +from typing import Any, Dict, Tuple + -# 1. custom_power: lambda function with positional-only x custom_power = lambda x=0, /, e=1: x**e -# 2. custom_equation: positional-only, positional-or-keyword, and keyword-only params + def custom_equation(x: int = 0, y: int = 0, /, a: int = 1, b: int = 1, *, c: int = 1) -> float: - """ - Calculates (x**a + y**b) / c with specific parameter constraints. - :param x: Base 1, positional-only - :param y: Base 2, positional-only - :param a: Exponent 1, positional-or-keyword - :param b: Exponent 2, positional-or-keyword - :param c: Divisor, keyword-only - :returns: Result as float - """ - return float((x**a + y**b) / c) - -# 3. fn_w_counter: tracks total calls and caller names -def fn_w_counter() -> (int, dict[str, int]): - """ - Counts the number of calls and tracks the caller (__name__). - """ - if not hasattr(fn_w_counter, "calls"): - fn_w_counter.calls = 0 - fn_w_counter.callers = {} - - caller_frame = inspect.currentframe().f_back - caller_name = caller_frame.f_globals.get('__name__', 'unknown') - - fn_w_counter.calls += 1 - fn_w_counter.callers[caller_name] = fn_w_counter.callers.get(caller_name, 0) + 1 - - return (fn_w_counter.calls, fn_w_counter.callers) \ No newline at end of file + + return (x**a + y**b) / c + + +def fn_w_counter() -> Tuple[int, Dict[str, int]]: + + + if not hasattr(fn_w_counter, "_total"): + fn_w_counter._total = 0 # type: ignore[attr-defined] + if not hasattr(fn_w_counter, "_by_caller"): + fn_w_counter._by_caller = {} # type: ignore[attr-defined] + + + frame = inspect.currentframe() + caller_frame = frame.f_back if frame is not None else None + caller_name = "" + if caller_frame is not None: + caller_name = str(caller_frame.f_globals.get("__name__", "")) + + + fn_w_counter._total += 1 # type: ignore[attr-defined] + by_caller: Dict[str, int] = fn_w_counter._by_caller # type: ignore[attr-defined] + by_caller[caller_name] = by_caller.get(caller_name, 0) + 1 + + + return int(fn_w_counter._total), dict(by_caller) # type: ignore[attr-defined] \ No newline at end of file