diff --git a/Week02/types_furkan_bulut.py b/Week02/types_furkan_bulut.py new file mode 100644 index 00000000..e1d127a9 --- /dev/null +++ b/Week02/types_furkan_bulut.py @@ -0,0 +1,4 @@ +my_int = 2 +my_float = 1.03 +my_bool = True +my_complex = 6j \ No newline at end of file diff --git a/Week03/pyramid_furkan_bulut.py b/Week03/pyramid_furkan_bulut.py new file mode 100644 index 00000000..58ef5287 --- /dev/null +++ b/Week03/pyramid_furkan_bulut.py @@ -0,0 +1,34 @@ +def calculate_pyramid_height(number_of_blocks): + """ + Calculate the height of a pyramid that can be built with a given number of blocks. + + The function determines how many complete layers (height) can be formed + using the provided number of blocks. Each layer requires a number of blocks + equal to the layer number (1 block for the first layer, 2 blocks for the + second layer, etc.). + + :param number_of_blocks: The total number of blocks available to build the pyramid. + :type number_of_blocks: int + :return: The maximum height of the pyramid that can be built. + :rtype: int + + :raises ValueError: If `number_of_blocks` is less than 0. + + Example: + + calculate_pyramid_height(6) + 3 + calculate_pyramid_height(20) + 5 + """ + if number_of_blocks < 0: + raise ValueError("Number of blocks must be non-negative.") + + height_of_pyramid = 0 + block_counter = 0 + + while number_of_blocks >= (block_counter + (height_of_pyramid + 1)): + block_counter += height_of_pyramid + 1 + height_of_pyramid += 1 + + return height_of_pyramid diff --git a/Week03/sequences_furkan_bulut.py b/Week03/sequences_furkan_bulut.py new file mode 100644 index 00000000..954a5f44 --- /dev/null +++ b/Week03/sequences_furkan_bulut.py @@ -0,0 +1,17 @@ +def remove_duplicates(seq: list) -> list: + """ + This function removes duplicates from a list. + """ + return list(set(seq)) + +def list_counts(seq: list) -> dict: + """ + This function counts the number of occurrences of each item in a list. + """ + return {i: seq.count(i) for i in seq} + +def reverse_dict(d: dict) -> dict: + """ + This function reverses the keys and values of a dictionary. + """ + return {v: k for k, v in d.items()} diff --git a/Week04/decorators_furkan_bulut.py b/Week04/decorators_furkan_bulut.py new file mode 100644 index 00000000..dc887ea9 --- /dev/null +++ b/Week04/decorators_furkan_bulut.py @@ -0,0 +1,81 @@ +import time +import tracemalloc + +def performance(fn): + """ + Decorator to measure performance metrics of a function. + + This decorator tracks the execution time and memory usage of the decorated + function, printing the results after each call. + + :param fn: The function to be decorated. + :returns: A wrapped function that tracks performance metrics. + :raises Exception: If an error occurs during the execution of the decorated function. + """ + + setattr(performance, 'counter', 0) + setattr(performance, 'total_time', 0.0) + setattr(performance, 'total_mem', 0.0) + + def _performance(*args, **kwargs): + """ + Wrapper function that measures execution time and memory usage. + + :param args: Positional arguments to be passed to the decorated function. + :param kwargs: Keyword arguments to be passed to the decorated function. + """ + counter = getattr(performance, 'counter') + total_time = getattr(performance, 'total_time') + total_mem = getattr(performance, 'total_mem') + + counter += 1 + setattr(performance, 'counter', counter) + + tracemalloc.start() + start_time = time.time() + + try: + fn(*args, **kwargs) + except Exception as e: + print(f"Error occurred: {e}") + finally: + end_time = time.time() + current, peak = tracemalloc.get_traced_memory() + tracemalloc.stop() + + elapsed_time = end_time - start_time + memory_usage = peak + + total_time += elapsed_time + total_mem += memory_usage + + setattr(performance, 'total_time', total_time) + setattr(performance, 'total_mem', total_mem) + + print_results(fn.__name__, elapsed_time, memory_usage, counter, total_time, total_mem) + + return _performance + +def print_results(func_name, elapsed_time, memory_usage, counter, total_time, total_mem): + """ + Display the performance results of the decorated function. + + This function prints the name of the function being measured, the elapsed time, + memory usage, and the total statistics over all calls. + + :param func_name: The name of the function being measured. + :param elapsed_time: The time taken for the function to execute, in seconds. + :param memory_usage: The peak memory usage during the function execution, in bytes. + :param counter: Number of times the decorated function has been called. + :param total_time: Total execution time of the decorated function. + :param total_mem: Total peak memory usage of the decorated function. + """ + results = ( + f"Function Name: {func_name}\n" + f"Number of Calls: {counter}\n" + f"Elapsed Time: {elapsed_time:.6f} seconds\n" + f"Memory Usage: {memory_usage / 1024:.2f} KB\n" + f"Total Time: {total_time:.6f} seconds\n" + f"Total Memory Usage: {total_mem / 1024:.2f} KB\n" + ) + print(results) diff --git a/Week04/functions_furkan_bulut.py b/Week04/functions_furkan_bulut.py new file mode 100644 index 00000000..9d776a58 --- /dev/null +++ b/Week04/functions_furkan_bulut.py @@ -0,0 +1,86 @@ +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 a custom equation based on the parameters provided. + + This function computes the result of the equation: + (x^a + y^b) / c, where: + - `x` and `y` are the bases for exponentiation. + - `a` and `b` are the exponents for `x` and `y`, respectively. + - `c` serves as the divisor, which cannot be zero. + + :param x: + The base for exponentiation (positional only). Default is 0. + + :param y: + The base for exponentiation (positional only). Default is 0. + + :param a: + The exponent for `x` (positional or keyword). Default is 1. + This defines the power to which `x` is raised. + + :param b: + The exponent for `y` (positional or keyword). Default is 1. + This defines the power to which `y` is raised. + + :param c: + A keyword-only parameter representing the divisor. Default is 1. + If set to zero, a division by zero exception is raised. + + :raises ValueError: + If `c` is zero, a `ValueError` is raised indicating that division by zero + is not allowed. + + :returns: + The result of the equation (x^a + y^b) / c. + This will return a float value representing the computed result. + + :example: + + #>>> custom_equation(2, 3, a=2, b=2, c=1) + 13.0 + #>>> custom_equation(2, 3, a=2, b=2, c=0) + Traceback (most recent call last): + ... + ValueError: Division by Zero Exception + """ + if c == 0: + raise ValueError("Division by Zero Exception") + + return (x ** a + y ** b) / c + +def fn_w_counter() -> (int, dict[str, int]): + """ + A function that counts how many times it has been called and tracks callers. + + Each time this function is invoked, it increments a call counter and logs + the name of the caller (the module name). This can be useful for debugging + purposes or monitoring how frequently this function is used in different + parts of the application. + + :returns: + A tuple containing: + - The total number of times the function has been called across all instances. + - A dictionary mapping caller names (module names) to the number of times + they have invoked this function. + + :example: + + #>>> fn_w_counter() + (1, {'__main__': 1}) + #>>> fn_w_counter() + (2, {'__main__': 2}) + """ + if not hasattr(fn_w_counter, 'call_count'): + fn_w_counter.call_count = 0 + fn_w_counter.callers = {} + fn_w_counter.call_count += 1 + + caller_name = __name__ + + if caller_name in fn_w_counter.callers: + fn_w_counter.callers[caller_name] += 1 + else: + fn_w_counter.callers[caller_name] = 1 + return fn_w_counter.call_count, fn_w_counter.callers