Summary

This document provides a revision of CSET101, focusing on introductory Python programming concepts. The material covers fundamental topics such as functions, lambda functions, and data structures like lists and sets.

Full Transcript

Revision CSET101 Dr Greetta Pinheiro Python Basics Python is called dynamic because variable types are determined at runtime, allowing flexibility in assigning different types of values to the same variable during execution. It is strongly typed because it enforces type rules strictly,...

Revision CSET101 Dr Greetta Pinheiro Python Basics Python is called dynamic because variable types are determined at runtime, allowing flexibility in assigning different types of values to the same variable during execution. It is strongly typed because it enforces type rules strictly, preventing implicit type conversions that could lead to unexpected behavior (e.g., adding a string and an integer raises an error). Control flow structures, including conditional statements (if, elif, else) to make decisions and loops (for and while) for repetitive execution. Additionally, it supports exception handling (try, except, finally) and control statements (break, continue, pass) to manage flow within loops and blocks. Pass By Value vs. Returning a Value Consider the following code def increment_func(x): def increment_func(x): x=x+1 x=x+1 return x x=1 increment_func(x) x=1 print(x) x = increment_func(x) print(x) Ru Ru n n 1 2 3 Functions set(): Creates a set from a sequence of elements. m = set([1, 2, 3, 4, 4, 5]) print(m) map(): Applies a function to every item in an iterable. map() applies the function to each element and returns a map object, which is usually converted to a list. numbers = list(map(int, ['1', '2', '3', '4’])) print(numbers) split(): Splits a string into a list of substrings. len(): Returns the number of elements in a collection. Functions sum(): Sums the elements of a collection. numbers = [10, 20, 30] total = sum(numbers) print(total) add(): Adds an element to a set. If the element already exists, the set does not change. Sets only store unique elements, so if you try to add a duplicate, nothing happens. m = {1, 2, 3} m.add(4) print(my_set) Functions discard(): This method removes an element from a set if it exists. If the element is not present, it does nothing. m = {1, 2, 3} m.discard(2) print(m) remove() (which raises an error if the element is not present) and discard() (which silently ignores missing elements). append() Used to add an element to the end of a list. It modifies the original list by adding the new element. Syntax of append() list.append(element) list: The list to which you want to add the element (list variable name) element: The item you want to append to the list. This can be any data type (int, float, string, list, etc.). (list item) append() append() modifies the list in place, meaning the original list is changed. The appended element always goes to the end of the list. The append() method does not return anything, so you should not assign the result to another variable (e.g., new_list = my_list.append(4) is incorrect). Examples Examples What is a lambda() Function? It can have any number of arguments but only one expression. Used when a simple, short function is needed. >>>lambda x: x + 10 Syntax of lambda(): lambda arguments: expression Arguments: Parameters you pass to the function. Expression: The single line of operation that the function returns. Example >>>add = lambda a, b: a + b >>>print(add(2, 3)) When a small function is needed temporarily. Typically used in combination with functions like map(), filter(), and sorted(). Lambda vs Def Functions lambda functions are single-line anonymous functions. def functions are multi-line and can have complex logic. # Using def def add(a, b): return a + b # Using lambda add = lambda a, b: a + b Using lambda() with map() The map() function applies a function to all items in an input list. >>>numbers = [1, 2, 3, 4] >>>squares = list(map(lambda x: x ** 2, numbers)) >>>print(squares) Output: [1, 4, 9, 16] Using lambda() with filter() The filter() function filters elements from a list based on a condition. >>>numbers = [1, 2, 3, 4, 5] >>>evens = list(filter(lambda x: x % 2 == 0, numbers)) >>>print(evens) Output: [2, 4] Uisng lambda() with sorted() Use sorted() to sort lists of tuples based on specific elements, like the second value in each tuple. >>>items = [(1, 2), (3, 1), (5, 4)] >>>sorted_items = sorted(items, key=lambda x: x) >>>print(sorted_items) Output: [(3, 1), (1, 2), (5, 4)] Sorting a List of Dictionaries using sorted() and lambda() Sort a list of dictionaries using a specific key from each dictionary. >>>students = [ {'name': 'Alice', 'grade': 85}, {'name': 'Bob', 'grade': 90}, {'name': 'Charlie', 'grade': 82} ] >>>sorted_students = sorted(students, key=lambda x: x['grade'], reverse=True) >>>print(sorted_students) Output: [{'name': 'Bob', 'grade': 90}, {'name': 'Alice', 'grade': 85}, {'name': 'Charlie', 'grade': 82}] range() Used inside a for loop to iterate over a sequence of numbers It generates a sequence of numbers, which can be used to control the number of iterations or to access specific elements of data structures like lists Syntax of range(): range(stop): Generates numbers from 0 to stop - 1. range(start, stop): Generates numbers from start to stop - 1. range(start, stop, step): Generates numbers from start to stop - 1, with increments of step. range() range() is extremely useful in loops when you need to specify a number of iterations or want to access elements by index. The step parameter in range() allows for flexible iteration, such as skipping numbers or iterating backward with a negative step. Examples https://data-flair.training/blogs/python-variable-scope/ Scope of Variables in Python The scope of a variable determines where it can be accessed or modified within a program. The scope is crucial because it helps prevent conflicts and unexpected behaviors when multiple parts of a program use variables with the same name. Local Scope: variable declared inside a function has a local scope. It can only be accessed within that function. NameError Scope of Enclosing Scope (Nonlocal): An enclosing scope exists when one Variables in function is nested inside another. Python Contd.. A variable in the enclosing function's scope is accessible in the nested function but not vice versa. Global Scope: Scope of A variable declared outside any Variables in function or class has a global scope. It can be accessed anywhere in the Python Contd.. program. To modify a global variable inside a function, the global keyword must be used. Scope of Variables in Python Contd.. Build-in Scope: The built-in scope contains all the built-in functions and exceptions in Python, such as len(), print(), min(), etc. or constants like True and False. These functions are globally accessible from any part of your code unless overridden. TypeErro r The LEGB Rule Defines the search order for variables in Python: Local Scope: If the variable is not found locally, search in the enclosing scope. Enclosing Scope: If not found in the enclosing function, search in the global scope. Global Scope: If not found globally, search the built-in scope. Built-in Scope: If the variable is not found in any of the above, it will look in the built-in scope. Scope of Variables in Python Contd.. Scope Type Definition Where Accessible Variables defined inside a Inside the function only. Local function. Enclosing Variables in an outer Inside the enclosing function (enclosing) function for nested and nested functions. functions. Global Variables defined at the top Anywhere in the script/module. level of the script/module. Built-in Predefined variables/functions Globally accessible unless provided by Python (e.g., print, overridden. len). What is Recursion? In Python, recursion occurs when a function calls itself directly or indirectly as part of its execution. Recursion is often used in problems that can be broken down into similar sub- problems, like calculating factorials, generating Fibonacci sequences, or solving problems related to trees and graphs. Every recursive function needs a condition to stop recursion, known as the base case. Without a base case, recursion would continue indefinitely, causing an error. Dr. Greetta Pinheiro Recursion Base Case The base case is the condition that stops the recursion. Without a base case, the recursive function would call itself indefinitely, leading to a stack overflow error. It represents the simplest instance of the problem, for which the answer is already known and doesn’t require further recursive calls. Recursive Case The recursive case is the part of the function where the function calls itself with a modified argument, gradually moving towards the base case. Each recursive call should bring the function closer to the base case, helping to reduce the problem size step by step. Dr. Greetta Pinheiro Example: Factorial Calculation It is the product of all positive integers less than or equal to n, if n=5, 5!=5×4×3×2×1=120 Using recursion, the factorial of n can be expressed as: n!=n×(n−1)! The base case will be when n= 1(since 1!=1, there’s no need to call the function further). The recursive case will be n*factorial(n−1), where the function keeps calling itself with a smaller value of Dr.n.Greetta Pinheiro https://www.youtube.com/ watch?v=BNeOE1qMyRA&t=1s Dr. Greetta Pinheiro Example: Base Case: The condition where the recursion stops. In this function, the base case is when k 0, the function calls itself with k - 1 and adds the result of that recursive call to k. This forms a chain of function calls, reducing the value of k by 1 each time, until it hits the base case. Output: Recursion Example Results 1 3 6 Example: Base case: where the function adds the element l[j] to total if it's not a list (i.e., it's an integer). Recursive Case: if the element l[j] is a list, the function makes a recursive call to process this sublist. The function keeps calling itself until it reaches individual integers inside the nested lists. What is a Class? A class is like a blueprint or template used to create objects. It defines a set of attributes (data) and methods (functions) that describe an object’s behavior. Analogy: Think of a class as a blueprint for a house. It describes the structure (like the number of rooms) but doesn't create an actual house. You need to use this blueprint to build houses (objects). Syntax: >>>class Dog: # A class to define Dog attributes and behavior >>> pass What is an Object? An object is an instance of a class. When you create an object, you are creating something based on the class blueprint. Analogy: If a class is the blueprint for a house, then an object is the actual house built using that blueprint. Example: >>>my_dog = Dog() # This creates an object (an instance) of the Dog class What is a Method? A method is a function that is defined inside a class and describes the behaviors or actions that objects of the class can perform. Methods typically operate on data (attributes) that belong to the class. Analogy: If the class is a blueprint for a house, a method would describe something the house can do, like "open doors" or "turn on lights." Example : What is __init__ in Python? __init__ is a special method in Python, also known as a constructor. It is automatically called when an object (instance) of a class is created. The primary role of __init__ is to initialize the object's attributes with values when a new object is instantiated. What is self in Python? self refers to the current instance of the class. It allows methods in the class to access the object's attributes and methods. When you create an object, self refers to that particular instance, allowing you to work with its attributes and methods. self must be the first parameter in any instance method of a class, though you do not explicitly pass it when calling the method. Encapsulation Encapsulation is the concept of bundling data (attributes) and methods (functions) that operate on the data into a single unit (class), and restricting direct access to some of the object’s components to ensure control over how they are accessed or modified. Why Encapsulation? It protects the internal state of an object and hides its complexity from the outside world. It enforces data integrity by restricting how attributes can be changed. Abstraction Abstraction is the concept of hiding the complexity of a system and exposing only the essential features to the user. In Python, abstraction can be achieved using abstract classes or interfaces, but can also be implemented by simply hiding the implementation details while showing only the necessary parts. Why Abstraction? It helps reduce complexity and allows the programmer to focus on interactions at a higher level without worrying about internal details. Inheritance Inheritance is a mechanism where a new class (child class) inherits attributes and methods from an existing class (parent class). This helps promote code reuse and establish a relationship between classes. Why Inheritance? It allows us to create new classes based on existing ones, reducing redundancy and improving maintainability. Inheritance Syntax The syntax for inheritance was already introduced during class declaration Child is the name of the subclass Parent is the name of the superclass for multiple inheritance, superclasses are declared as a comma-separated list of class names Inheritance Syntax (cont.) Superclasses may be either Python- or user-defined classes Define a Parent Class: Write a class with attributes and methods. Create a Child Class: Use the parent class as an argument in parentheses. Access Parent's Attributes and Methods: Use the child class instance to call methods or access attributes. Inheritance Syntax (cont.) A subclass inherits all the methods of its superclass A subclass can override (replace or augment) methods of the superclass Just define a method of the same name Although not enforced by Python, keeping the same arguments (as well as pre- and post-conditions) for the method is highly recommended When augmenting a method, call the superclass method to get its functionality A subclass can serve as the superclass for other classes OOP Inheritance (cont.) Conceptual superclass example: base class Mammal parent class single inheritance subclass derived Dog Cat class child class class hierarchy Labrador Poodle Labradoodle multiple inheritance Overriding a Method init is frequently overridden because many subclasses need to both (a) let their superclass initialize their data, and (b) initialize their own data, usually in that order Child classes can redefine methods from parent classes. Multiple Inheritance Python supports multiple inheritance In the class statement, replace the single superclass name with a comma- separated list of superclass names When looking up an attribute, Python will look for it in “method resolution order” (MRO) which is approximately left-to-right, depth-first There are (sometimes) subtleties that make multiple inheritance tricky to use, Example of Multilevel Inheritance class A: def A_method(self): print("method for A") class B(A): def B_method(self): print("method for B") bb = B() bb.B_method() method for B class C(B): def C_method(self): print("method for c") cc = C() cc. A_method() method for cc. A method B_method() for B cc.C_method() method for c Valid or Invalid __init__ initializes the instance variable This is a valid class name. The constructor is properly defined definition with an with self. empty body, and Invalid because __private is a private an instance (obj) is variable and cannot be accessed created correctly. directly. Use a getter method instead. Invalid because super() can only be used inside a class method. Valid or Invalid The self parameter is missing in Invalid because age is not Python supports method overloading the method definition. All instance defined as an attribute of the using default argument values. methods must include self as the Example class. first parameter. Valid or Invalid Instance attributes brand and model are correctly initialized and accessed. Invalid because circular Invalid because display is inheritance (where two an instance method and Polymorphism is correctly implemented by classes inherit from each requires an object to call it. overriding the sound method in the derived class other) is not allowed in Valid or Invalid Invalid because self is not defined outside an instance method. Invalid because Python does not support method overloading by signatures; the second definition overwrites super() correctly calls the greet method the first. from the parent class. Operators Arithmetic Operators (**, *,/,//,%,+,_) Comparison Operators (=, ==) Python Assignment Operators (=, +=, -=, *=, /=) Logical Operators (and, or, not) Bitwise Operators (&, |, ~, >>, >> "Hello" + " " + "World" 'Hello World' ❖ String Repetition (Duplicate strings): repeat a string multiple times using the * operator. >>> "Echo" * 3 'EchoEchoEcho' String Operations ❖ Accessing Individual Characters (Get a character): access individual characters in a string using their index within square brackets []. Indexing starts from 0 for the first character. >>> word = "Index" >>> print(word) 'I' >>> print(word) 'x' ❖ Slicing a String (Get a substring): extract a portion of a string, called a substring, using slicing. Slicing uses a colon : within the square brackets [] to specify the start and end positions. >>> word = "Substring" >>> print(word[2:7]) 'bstr' String Operations ❖ Get the string length: The len() function returns the number of characters in a string. >>> word = "Length" >>> print(len(word)) 6 ❖ Split a string: The split() function breaks down a string into a list of substrings based on a specified delimiter. >>> sentence = "This is a sentence." >>> print(sentence.split()) ['This', 'is', 'a', 'sentence.'] String Operations ❖ Joining Strings (Combine a list of strings): The join() function combines a list of strings into a single string, using a specified delimiter. >>> words = ['This', 'is', 'a', 'sentence.'] >>>print( ' '.join(words)) 'This is a sentence.' Replacing Substrings (Substitute substrings): replace occurrences of a substring within a string using the replace() function. >>> word = "Substitution" >>> print(word.replace("sub", "re")) 'restitution' String Operations ❖ Change case: Python provides functions like lower(), upper(), capitalize(), and title() for changing the case of characters within a string. lower() - Converts all characters to lowercase upper() - Converts all characters to uppercase capitalize() - Capitalizes the first character title() - Capitalizes the first character of each word >>> word = "Case" >>> print(word.lower()) 'case' >>> print(word.upper()) 'CASE' String Operations Strip whitespace: Functions like strip(), lstrip(), and rstrip() are used to remove leading and trailing whitespace characters (spaces, tabs, newlines) from a string. lstrip() removes leading spaces, while rstrip() removes trailing spaces. >>> word = " Stripped " >>> word.strip() 'Stripped' Search for substrings: The find() function can be used to locate a specific substring within a string. It returns the starting index of the first occurrence or -1 if the substring is not found. >>> word = "Searching" >>> word.find("arch") 2 String Operations ❖ Checking for Substrings : Use the in keyword to check if a substring exists within a string: >>> "apple" in "apple pie" True >>> "banana" in "apple pie" False String Operations ❖ String Formatting: Using methods like format() or f-strings (f"...") for dynamically inserting variables into strings. Python provides a lot of flexibility with strings, making it easy to manipulate and work with text in various ways. The operations above are fundamental tools in any Python programmer's toolkit. String-Applicable Sequence Operations Since strings (unlike lists) are immutable, sequence-modifying operations are not applicable to strings. For example: one cannot add, delete, or replace characters of a string. All string operations that “modify” a string return a new string that is a modified version of the original string. Checking the Contents of a String Searching , Modifyin g and Splitting Strings Other Escape Characters used Code in Python Result Example Output \' Single Quote print ('It\'s alright.') It's alright. \\ Backslash print ("This will insert one \\ (backslash).“) This will insert one \ (backslash). Hello World! \n New Line print("Hello\nWorld!“) Hello World! \r Carriage Return print("Hello\rWorld!“) \t Tab print("Hello\tWorld!“) Hello World! \b Backspace print ("Hello \bWorld!“) HelloWorld! #A backslash followed by three integers will result in \ooo Octal value a octal value: Hello print ("\110\145\154\154\157“) #A backslash followed by an 'x' and a hex number \xhh Hex value represents a hex value: Hello print("\x48\x65\x6c\x6c\x6f“) MCQs: 1. Answers Some string methods alter the string they are 4. Indicate which of the following is true. called on, while others return a new altered a) String method isdigit returns true version of the string. if the a) TRUE string applied to contains any digits. b) FALSE b) String method isdigit returns true if the string applied to contains only digits. 2. The find method returns the number of occurrences of a character or substring 4. Indicate which of the following s.replace('c','e’) within a given string. returns for s = 'abcabc’. a) TRUE a) 'abeabc’ b) 'abeabe’ b) FALSE 4. Which of the results below does s. 3. Which of the results below does s[2:4] return strip(‘-’) for the string s = 'abcdef’. return for the string s = ‘---ERROR---’. a) 'cd’ b) 'bcd’ c) 'bc’ d) 'cde' b) '---ERROR’ b) 'ERROR---’ c) 'ERROR' String Slicing in Python Slicing notation: The general syntax for slicing is string [start:end: step], where: ▪ start is the index of the first character you want to include in the slice (inclusive). ▪ end is the index of the first character you do not want to include (exclusive). ▪ step is an optional value that determines how many characters to skip between each included character. By default, it is 1. Illustrative Examples: 1.Basic Slicing: Extracts a substring from a given range of indices. >>>text = "PythonProgramming" >>>print(text[0:6]) 'Python' This extracts characters starting at index 0 and ends before index 6. Illustrative Examples: 2.Slicing with Steps: The step argument lets you skip characters. By default, the step is 1. >>>text = "PythonProgramming" >>>print(text[0:12:2]) 'PtoPorm' This extracts every second character from index 0 to 11. Illustrative Examples: 3.Reverse Slicing: You can reverse a string using a negative step value. >>>text = "Python" >>>print(text[::-1]) 'nohtyP' This reverses the string, as -1 means to step backward. Illustrative Examples: 4.Handling Out-of-Bounds Indices: When slicing, if you provide indices that go beyond the string's length, Python automatically adjusts them to valid boundaries. >>>text = "Python" >>>print(text[0:50]) 'Python' Since the string ends at index 5, Python adjusts 50 to the last valid index. Illustrative Examples: 5. Immutability: strings are immutable in Python. Slicing does not modify the original string; instead, it creates a new string containing the extracted portion. >>>text = "Python" >>>sliced_text = text[1:4] >>>print(sliced_text) 'yth' >>>print(text) 'Python' Use Cases for String Slicing: ∙ Extracting substrings for manipulation: Useful for extracting and manipulating parts of strings. >>>full_name = "John Doe" >>>first_name = full_name[:4] >>>print(first_name) 'John' ∙ Working with file paths: Slicing is helpful when working with file paths to extract file names, extensions, etc. ∙ Processing text data: When dealing with strings representing text, you can use slicing to extract words, sentences, or specific portions for further processing. >>>sentence = "The quick brown fox" >>>words = sentence[:9] >>>print(words) 'The quick' Extract characters from the given strings and combine them to form a new string using string slicing Keywor Description Use Case Examples d pass A null operation; does Used in blocks or functions nothing. Used as a where no code is required at placeholder for code to the moment, but the structure be implemented later. needs to be intact. continue Skips the rest of the Used to skip specific iterations current loop iteration in loops without terminating and moves to the next the loop entirely. iteration. break Immediately exits the Used to terminate a loop when loop, skipping all a specific condition is met. remaining iterations. for loop while loop Used when the number of Used when the number of iterations is predetermined, or iterations is not predetermined when iterating over a sequence and depends on a condition. (like a list, string, tuple, or Keeps executing as long as the range). condition evaluates to True. Stops automatically when the Requires explicit initialization sequence ends. and manual update of loop Often used for fixed-length variables. iterations. Commonly used for conditional or infinite loops. Looping Statements (Control flow statements) Feature for Loop while Loop Iteration Basis Iterates over a Iterates based on a sequence or range. condition. Initialization Implicit (handled Explicit (requires internally). manual setup). Condition Check Stops when the Continues until the sequence is exhausted. condition is False. Loop Variable Update Handled automatically. Must be updated manually. Typical Use Case Known number of Unknown number of iterations or sequences. iterations or conditions. Readability More concise for fixed Flexible but can lead to iterations. errors if poorly Example: Reverse a string Example: Print elements of a list Example: Count vowels in a string Exceptions Even if a statement or expression is syntactically correct, it may cause an error when an attempt is made to execute it. Errors detected during execution are called exceptions and are not unconditionally fatal. Most exceptions are not handled by programs, however, and result in error messages like “cannot divide by zero” or “cannot concatenate ‘str’ and ‘int’ objects”. 94 Exceptions (cont’d.) Exception handler: code that responds when exceptions are raised and prevents program from crashing – In Python, written as try/except statement General format: try: statements except exceptionName: statements Try suite: statements that can potentially raise an exception Handler: statements contained in except block 95 Exception s (cont’d.) 96 Predict the output. 97 Write a program that prompts the user to enter their age. If the age is not a positive integer, raise a ValueError. Opening & Closing Files We need a file variable or file handle to work with files in Python. This file object can be created by using open( ) function or file( ) function. Open( ) function creates a file object, which is used later to access the file using the functions related to file manipulation. Its syntax is following - =open(,) File accessing modes - – read(r): To read the file – write(w): to write to the file Python External File – append(a): to Write at the end of Progra (Second file. m a ry Storage Read ) from file (Load) Opening & Closing Files... Opened the File Here the point is that the file “Hello.txt” which is used here is pre- built and stored in the same folder where Python is installed. The file is closed. A program describing the functions of file handling. Output File Modes Mode Description r To read the file which is already existing. rb Read Only in binary format. r+ To Read and write but the file pointer will be at the beginning of the file. rb+ To Read and write binary file. But the file pointer will be at the beginning of the file. w Only writing mode, if file is existing the old file will be overwritten else the new file will be created. wb Binary file only in writing mode, if file is existing the old file will be overwritten else the new file will be created. wb+ Binary file only in reading and writing mode, if file is existing the old file will be overwritten else the new file will be created. a Append mode. The file pointer will be at the end of the file. ab Append mode in binary file. The file pointer will be at the end of the file. a+ Appending and reading if the file is existing then file pointer will be at the end of the file else new file will be created for reading and writing. ab+ Appending and reading in binary file if the file is existing then file pointer will be at the end of the file else new file will be created for reading and writing. Reading a File A Program to read “Hello.txt” File. Output Hello.txt file was created using notepad.| Reading a File... Reading first 10 characters from the file “Hello.txt” Output 1. We can also use readline( ) function which can read one line at a time from the file. 2. Same readlines( ) function is used to read many lines. Writing to a File We can write characters into file by using following two methods - 1. write (string) 2. writelines (sequence of lines) write( ) : it takes a sting as argument and adds to the file. We have to use ‘\n’ in string for end of line character. writelines ( ) : if we want to write list, tupleinto the file then we use writelines ( ) function. A program to write in “Hello.txt” This “Hello.txt” is created using above program. Output Writing to a File... A Program to use writelines() function Output “Hello.txt” File is created using the above program. Writing User Input to the File. Taking data from the user writing this and the file nt. data txt”. to “Stud Student File is e created by using the above Output program. Operations in Binary File. If we want to write structure such as list, dictionary etc and also we want to read it then we have to use a module in python known as pickle. Pickling means converting structure into byte stream before writing the data into file. And when we read a file then a opposite operation is to be done means unpickling. Pickle module has two methods - dump( ) to write and load( ) to read. Write a program that opens a file called "data.txt" and reads its content. Handle the FileNotFoundError exception. Python Sandbox | Turtle Mode Turtle Graphics Turtle Graphics ? Tkinter in Python Tkinter is Python's built-in library for creating Graphical User Interfaces (GUI). It provides tools to build windows, buttons, menus, text boxes, and other graphical elements for desktop applications. Tkinter is included with Python, you don't need to install it separately if you have Python installed. To check if Tkinter is installed: Creating a Window: Use Tk() to create the main application window. Add widgets like Label, Button, and Entry, and manage layouts using. pack() for stacking,.grid() for row-column layouts,.place() for exact positioning. ALL THE BEST !!

Use Quizgecko on...
Browser
Browser