diff --git a/Week01/info_furkan_cetiner.py b/Week01/info_furkan_cetiner.py new file mode 100644 index 00000000..652fd78a --- /dev/null +++ b/Week01/info_furkan_cetiner.py @@ -0,0 +1,2 @@ +student_id = "210316074" +full_name = "Furkan Çetiner" 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..08033425 --- /dev/null +++ b/Week04/decorators_furkan_cetiner.py @@ -0,0 +1,52 @@ +from __future__ import annotations + +import time +import tracemalloc +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 + + # 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] + + return cast(F, wrapper) \ 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..a07d1375 --- /dev/null +++ b/Week04/functions_furkan_cetiner.py @@ -0,0 +1,36 @@ +from __future__ import annotations + +import inspect +from typing import Any, Dict, Tuple + + +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: + + 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 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