diff --git a/1_variables.py b/1_variables.py index f2e18c7..771b6f3 100644 --- a/1_variables.py +++ b/1_variables.py @@ -67,9 +67,21 @@ def show_type(var): print(f"The type of {var} is {type(var)}") # Let's test our function with the variables defined above. -show_type(number) # Expected output: -show_type(number_float) # Expected output: -show_type(text) # Expected output: -show_type(boolean) # Expected output: -show_type(a_list) # Expected output: -show_type(a_tuple) # Expected output: + +# Expected output: +show_type(number) + +# Expected output: +show_type(number_float) + +# Expected output: +show_type(text) + +# Expected output: +show_type(boolean) + +# Expected output: +show_type(a_list) + +# Expected output: +show_type(a_tuple) diff --git a/2_classes.py b/2_classes.py index a828198..7415f37 100644 --- a/2_classes.py +++ b/2_classes.py @@ -22,7 +22,8 @@ class Person: # This is the constructor, whenever a new person is created it runs and can be used to assign the new person their attributes! def __init__(self, name, age, eye_color): - self.name = name # An attribute + # An attribute + self.name = name self.age = age self.eye_color = eye_color @@ -34,10 +35,14 @@ class Person: return f"{self.name} is too young ({self.age}) and can not apply for a driving permit." -pedro = Person("Pedro", 19, "Brown") # Creates a new person with the following values +# Creates a new person with the following values +pedro = Person("Pedro", 19, "Brown") -print(pedro.canApplyForLicence()) # Runs the canApplyForLicence method, and prints the return value +# Runs the canApplyForLicence method, and prints the return value +print(pedro.canApplyForLicence()) -Iain = Person("Iain", 16, "Blue") # Creates a Another new person with the following values (iain armitage if you wanted to know.) +# Creates a Another new person with the following values (iain armitage if you wanted to know.) +Iain = Person("Iain", 16, "Blue") -print(Iain.canApplyForLicence()) # Runs the canApplyForLicence method, and prints the return value +# Runs the canApplyForLicence method, and prints the return value +print(Iain.canApplyForLicence()) diff --git a/3_advanced_classes.py b/3_advanced_classes.py index 17cb4a0..274278a 100644 --- a/3_advanced_classes.py +++ b/3_advanced_classes.py @@ -22,51 +22,70 @@ Key Concepts: IMPORTANT: Python does not strictly enforce access control, so these conventions mainly serve as guidelines. """ -from abc import ABC, abstractmethod # This is the abstraction library in Python -from typing import Optional # Type hinting, not needed but reduces ambiguity. Note: not enforced at runtime. +# This is the abstraction library in Python +from abc import ABC, abstractmethod +# Type hinting, not needed but reduces ambiguity. Note: not enforced at runtime. +from typing import Optional -class Shape(ABC): # An abstract class, provides a blueprint for other classes to inherit. +# An abstract class, provides a blueprint for other classes to inherit. +class Shape(ABC): + # This method is required in all subclasses, ensuring they provide their own area calculation. @abstractmethod def area(self) -> float: """Implemented to ensure each subclass contains this method.""" - # This method is required in all subclasses, ensuring they provide their own area calculation. + class Rectangle(Shape): def __init__(self, width: float, height: float): - self.__width = width # A private attribute to store width. - self.__height = height # A private attribute to store height. + # A private attribute to store width. + self.__width = width + # A private attribute to store height. + self.__height = height - @property # This allows access to the private 'width' attribute in a controlled way. + # This allows access to the private 'width' attribute in a controlled way. + @property def width(self) -> float: return self.__width - @width.setter # This allows you to set the value of 'width' after initialization. + # This allows you to set the value of 'width' after initialization. + @width.setter def width(self, value: float): if value <= 0: raise ValueError("Width must be positive.") self.__width = value - @property # This allows access to the private 'height' attribute in a controlled way. + # This allows access to the private 'height' attribute in a controlled way. + @property def height(self) -> float: return self.__height - @height.setter # This allows you to set the value of 'height' after initialization. + # This allows you to set the value of 'height' after initialization. + @height.setter def height(self, value: float): if value <= 0: raise ValueError("Height must be positive.") self.__height = value def area(self) -> float: - return self.__width * self.__height # Calculates the area of the rectangle. + # Calculates the area of the rectangle. + return self.__width * self.__height def set_dimensions(self, width: Optional[float] = None, height: Optional[float] = None): if width is not None: - self.width = width # Sets 'width' using the setter. + # Sets 'width' using the setter. + self.width = width if height is not None: - self.height = height # Sets 'height' using the setter. + # Sets 'height' using the setter. + self.height = height rect = Rectangle(5, 10) -print(rect.area()) # Prints the area of the rectangle (5 * 10 = 50). -rect.set_dimensions(width=8) # Updates 'width' using the setter, height remains the same. -print(rect.area()) # Prints the new area of the rectangle (8 * 10 = 80). + +# Prints the area of the rectangle (5 * 10 = 50). +print(rect.area()) + +# Updates 'width' using the setter, height remains the same. +rect.set_dimensions(width=8) + +# Prints the new area of the rectangle (8 * 10 = 80). +print(rect.area()) diff --git a/4_decorators.py b/4_decorators.py index b3ce93b..25108a8 100644 --- a/4_decorators.py +++ b/4_decorators.py @@ -14,20 +14,24 @@ Key Concepts: """ import time - -def timed(function: any) -> any: #initial function takes in another function as a arguement - - def wrapper(*args: any, **kwargs: any) -> any: # the wrapper, takes in the arguements and keyword arguements from the function +# Initial function takes in another function as a arguement +def timed(function: any) -> any: + + # The wrapper, takes in the arguements and keyword arguements from the function + def wrapper(*args: any, **kwargs: any) -> any: before = time.time() output = function(*args, **kwargs) after = time.time() print(f"{function.__name__} with output of {output} took {after-before}s to execute.") - return output # ensure the output of the function is passed back + # Ensure the output of the function is passed back + return output return wrapper +# Python uses @ to use a wrapper on a function @timed -def exponential_function(n: int) -> int: # O(n^2) time complexity due to nested loops +# O(n^2) time complexity due to nested loops +def exponential_function(n: int) -> int: output = 0 for i in range(n): for j in range(i): @@ -35,6 +39,10 @@ def exponential_function(n: int) -> int: # O(n^2) time complexity due to nested return output # Test the decorated function -print(exponential_function(1000)) # This will take some time to compute -exponential_function(10000) # This will take noticeably more time to compute + +# This will take some time to compute +exponential_function(1000) + +# This will take noticeably more time to compute +exponential_function(10000)