Learn Python in 86400 Seconds
Welcome to 86400Sec! This course is designed to take you from zero to hero in Python programming in just 8 days. Each day features 3 hours of focused learning, breaking Python concepts into digestible lessons and practical examples. By the end of this course, you'll have the skills to build your own Python programs and tackle real-world problems.
Course Overview
- Total Duration: 86400 seconds (24 hours)
- Structure: 3 hours/day over 8 days
- Audience: Beginners who want to learn Python from scratch.
Learning Outcomes
- Python fundamentals: syntax, data types, and operations.
- Advanced concepts like functions, classes, and error handling.
- Data structures: tuples, lists, sets, and dictionaries.
Curriculum
-
- Introduction to Python
- Variables and data types
- Working with Strings
-
- Understanding tuples
- Working with lists
- Introduction to sets
- Basic operations: slicing, indexing, iteration
-
- Understanding and Working with Dictionaries
-
- Understanding Control flow statements
- Working with Conditional Statements and Loops
-
- Writing your own functions
- Parameters, arguments, and return values
-
- Understanding Error Handling Concept
- Understanding exceptions
- Using try, except, and finally
- Raising and customizing exceptions
-
- Classes and Object-Oriented Programming (OOP)
- Introduction to OOP
- Creating classes and objects
- Attributes and methods
-
- File Handling
Requirements
- To follow along with this course, you'll need:
- Python
- Jupyter Notebook
pip install notebook
Getting Started
1- Clone this repository to your local machine:
git@github.com:Zemzemfiras1/PythonIN-86400sec.git
2- Navigate to the course directory:
cd PythonIN-86400sec
3- Launch Jupyter Notebook:
jupyter notebook
4-Open the notebooks for each day's lessons and follow along.
Contributing
- Contributions, bug reports, and feature requests are welcome!
- Feel free to fork this repository and submit a pull request.
Python Basics
What is Python?
Python is a versatile, high-level programming language that is easy to learn and use. It is widely used in various domains such as web development, data science, machine learning, and more....
Why Use Python?
- Simple and readable syntax.
- Huge standard library.
- Cross-platform compatibility.
Section 1: Variables and Data Types
Variables
In Python, variables are used to store data values, and the type of each variable is automatically determined based on the value assigned to it
- Example: Variables & its Types
# int: Integer type, stores whole numbers.
int: 10
# float: Floating-point type, stores numbers with decimal points.
float: 3.14
# complex: Complex number type, stores numbers with both real and imaginary parts.
complex: (1+2j)
# str: String type, stores sequences of characters (text).
str: Hello, World!
# list: List type, an ordered and mutable collection of elements.
list: [1, 2, 3, 'apple']
# tuple: Tuple type, an ordered but immutable collection of elements.
tuple: (1, 2, 3, 'banana')
# dict: Dictionary type, stores key-value pairs.
dict: {'key1': 'value1', 'key2': 42}
# set: Set type, stores unordered collections of unique elements.
set: {1, 2, 3, 4}
# frozenset: Frozenset type, an immutable version of a set.
frozenset: frozenset({1, 2, 3, 4})
# bool: Boolean type, stores either True or False.
bool: True
# bytes: Bytes type, stores immutable sequences of bytes.
bytes: b'Hello'
# NoneType: Represents the absence of a value or a null value.
NoneType: None
Numeric and floats
- Create 2 variable a & b , then assign them numeric values eqaul to 77 and 3
a = 77
b = 3
- print them , Then make 4 new variables :
- sum_ab # sum => +
- diff_ab # Difference => -
- prod_ab # Product => *
- quot_ab # Qutient => /
# print the value assined to the variable "a"
print(a)
print ("The type of the value ",a," is :", type(a))
# print the value assined to the variable "b"
print(b)
print ("The type of the value ",b," is :", type(b))
sum_ab = a + b
print(sum_ab)
diff_ab = a - b
print(diff_ab)
prod_ab = a * b
print(prod_ab)
quot_ab = a / b
print(quot_ab)
#check the type of the value quot_ab :
print(type(quot_ab))
Section 2 :String Manipulation
Strings are sequences of characters enclosed in either single (') or double (") quotes in Python. String manipulation refers to the various operations you can perform on strings, such as concatenation, slicing, modification, and more. Python provides a variety of built-in methods that allow you to work with strings efficiently.
str1 = "Hello"
str2 = "Learner"
print(type(str1))
print(type(str2))
Concatination
without_space = str1+str2
print(without_space)
Add a space between the 2 strings
with_space = str1+" "+str2
print(with_space)
Length
Getting the number of characters in a string using len()
print("Length of Hello = ",len(str1))
print("Length of Learner = ",len(str2))
print("Length of 'Hello Learner' = ",len(with_space))
Upper & Lowercase
print(str1.lower()) # Expected output: hello
print(str2.upper()) # Expected output: LEARNER
Check for substring
text = "consistency if a picotal key in Learning "
# Check if lazy exist in text ??
print("Lazy" in text)
# Check if Cons exist in text ??
print("Cons" in text)
# Check if consistency exist in text ??
print("consistency" in text)
Replace Substring
text = "I love coding with R ."
new_text = text.replace("R", "Python")
print(new_text)
Remove Whitspaces
sentence = " Python is awesome !"
print(sentence)
clean_sentence = sentence.strip()
print(clean_sentence)
Split String
Given the string "apple,banana,cherry", split it into a list of fruits.
fruits = "apple,banana,cherry"
print(fruits)
splitted_fruits = fruits.split(",")
print(splitted_fruits) # Expected output: ['apple', 'banana', 'cherry']
Join Strings
words = ['I', 'Love', 'snakes']
sentence = " ".join(words)
print(sentence)
Count Occurrences
seq = "ATCGGGCATACG"
count = seq.count("G")
print(count)
String Slicing
Slicing allows you to extract a portion of a string by specifying the start and end indices. You can also specify a step, which allows you to skip characters.
string[start:end:step]
- Basic Slicing
text = "Python Programming"
# Slicing from index 0 to 5 (exclusive)
result = text[0:5]
print(result) # This slices the string from index 0 to 4 (because the end index is exclusive).
This slices the string from index 0 to 4 (because the end index is exclusive).
- Slicing with Step
text = "Python Programming"
# Slicing from index 0 to 12 with a step of 2
result = text[0:12:2]
print(result) # Output: Pto rg
This extracts every second character starting from index 0 up to index 11. The characters selected are "P", "t", "o", " ", "r", "g".
Omitting Start and End
You can omit the start and end values to slice from the beginning or to the end of the string:
text = "Python Programming"
# Slicing from the start to index 6 (exclusive)
result1 = text[:6]
print(result1) # Output: Python
# Slicing from index 7 to the end of the string
result2 = text[7:]
print(result2) # Output: Programming
Explanation:
[:6] slices from the beginning of the string up to (but not including) index 6.
[7:] slices from index 7 to the end of the string.
Negative Indices
In Python, negative indices can be used to slice the string from the end.
text = "Python Programming"
# Slicing from the second last character to the end
result = text[-2:]
print(result) # Output: ng
Explanation: Negative indices count from the end of the string. Here -2 starts from the second last character and slices to the end of the string.
Reverse Slicing
You can also use negative steps to slice a string in reverse order.
text = "Python Programming"
# Slicing in reverse order
result = text[::-1]
print(result) # Output: gnimmargorP nohtyP
Explanation: [::-1] reverses the string by stepping backwards through every character.
ENJOY !!
Section 3 : Lists
A list is a mutable, ordered collection of items. A list can contain elements of different data types, including numbers, strings, and even other lists.
my_list = [item1, item2, item3, ...]
- Items in the list are separated by commas.
- Lists are ordered, meaning the items have a defined order, and this order will not change unless you specifically reorder them.
- Lists are mutable, meaning you can change their contents (add, remove, or modify elements)
Basic Operations on Lists:
print the hole list
my_list = [1, 2, 3, 5,0 ,77]
print(my_list)
Length of the list:
Use len() to get the number of elements in a list
print(len(my_list)) # Prints the length of the list
Accessing elements: You can access elements in a list using their index.
print(my_list[0]) # Prints item 1
print(my_list[1]) # Prints item 2
print(my_list[5]) # Prints the last item
List Slicing
list slicing allows you to access a portion (sublist) of a list by specifying a range of indices. The syntax for slicing is:
my_list[start:end]
- start: The index of the first element you want to include in the slice.
- end: The index of the element where the slice should stop before. (The slice will not include this index.)
- To Better understand it lets print the indexes along with the values is to use the built-in enumerate() function, which returns both the index and the value as a tuple during iteration.
print("my list is : ", my_list)
for index, value in enumerate(my_list):
print(f"Index {index}: {value}")
print(my_list[1:4])
Another trick is to start counting comma from where to start and where it should end
my list is : [1 , 2 , 3 , 5 , 0 , 77]
comma indexes [v 1 v 2 v 3 v 4 v 5 v 6]
Lets Try to print the item3 > item 6
print(my_list[2:6])
Start Printing from the first item
print(my_list[0:])
Start Printing from the 3rd item
print(my_list[2:])
Print until the 3rd item
print(my_list[:3])
Print The last item
print(my_list[-1])
You can also print the entire ist using :
my_list[:]
Add an element (Item)
You can add elements to the list using append() funciton which allow the user to add an element to the end of the list.
my_list.append(100) # Adds 100 at the end of the list
print(my_list)
You can add elements to the list using insert() funciton which allow the user to add an element at a specific position (index) in the list.
Syntax :
list.insert(index, element)
my_list.insert(1,88) # Adds 4 at the end of the list
print(my_list)
print(my_list)
Remove an element (Item)
You can remove elements using remove() which Removes the first occurrence of the specified value from the list
my_list.remove(100)
print(my_list)
You can remove elements using del() which deletes an element at a specified index or the entire list.
del list[index]
del list[start:end] # For slicing
del list # To delete the entire list
del my_list[0]
print(my_list)
You can remove elements using pop() which removes and returns the element at the specified index (or the last element if no index is provided)
list.pop(index)
my_list.pop()
print(my_list)
Section 4 : Tuples
A tuple is a collection of ordered, immutable elements in Python. Tuples are defined by enclosing elements in parentheses () and are often used for fixed collections of related data.
- Structure :
my_tuple = (Value1, Value2, Value3, ....)
Tuples in Python are immutable, which means you cannot modify a tuple after it is created. This includes adding, removing, or changing individual elements. Immutability makes tuples:
- Safer to use as constants, preventing accidental changes.
- Hashable, allowing them to be used as keys in dictionaries or elements in sets.
Creating a tuple
- example 1 :
tuple1 = (42, "hello", 3.14)
print(tuple1)
- example 2 : use tuple() instructor
tuple2 = tuple(["42", "hello", "3.14"])
print(tuple2)
Accessing Tuple Elements
Tuples support indexing to access individual elements
my_tuple = (10, 20, 30, 40)
print(my_tuple[0]) # First element
print(my_tuple[-1]) # Last element
Slicing Tuples
Tuples can also be sliced to create subsets
my_tuple = (0, 1, 2, 3, 4, 5)
print(my_tuple[1:4]) # Elements from index 1 to 3
print(my_tuple[:3]) # First three elements
Concatenation
t1 = (1, 2)
t2 = (3, 4)
t3 = t1 + t2
print(t3)
Repetition
t = (1, 2)
print(t * 3)
Unpacking
Assign tuple elements to variables:
t = (1, 2, 3)
a, b, c = t
print(a, b, c)
occurrences
Using count(): Returns the number of occurrences of a value.
t = (1, 2, 2, 3)
print(t.count(2))
Indexing
Using index(): Returns the index of the first occurrence of a value.
print(t.index(3))
Converting Between Tuples and Other Types
Convert a list to a tuple:
lst = [1, 2, 3]
t = tuple(lst)
print(t)
Convert a tuple to a list:
t = (1, 2, 3)
lst = list(t)
print(lst)
Nested Tuples
t = ((1, 2), (3, 4))
print(t[0])
print(t[0][1])
Comparison
tuples are compared lexicographically, meaning they are compared element by element from left to right until a difference is found.
Key Notes on Tuple Comparisons:
- If the first elements are equal, the comparison moves to the next elements.
- If all elements are equal up to the shorter tuple's length, the shorter tuple is considered smaller.
- Lexicographic comparison mimics how words are compared in a dictionary.
t1 = (1, 2, 3)
t2 = (0, 1, 4)
print(t1 < t2) # Output: False
Tuple Comparison Process Given:
t1 = (1, 2, 3)
t2 = (0, 1, 4)
Compare the first elements:
t1[0] is 1, and t2[0] is 0.
Since 1 > 0, the comparison stops here.
The result of the comparison is determined solely by this first element comparison: 1 > 0.
Outcome : Since t1[0] > t2[0], the condition t1 < t2 is False.
(1, 2, 3) < (1, 2, 4) # True because 3 < 4
(1, 2) < (1, 2, 0) # True because shorter tuples are compared only to their length
(0, 4, 5) < (1, 2, 3) # True because 1 > 0
Deleting Tuples
t = (1, 2, 3)
del t
print(t)
Section 5 : Sets
What are Sets?
-
A set is a collection of unique and unordered elements in Python.
-
Defined using
{}
or theset()
function. -
Create an empty set named myset :
my_set = set()
# print my_set
print(my_set)
# check my_set type
print(type(my_set))
Adding Elements to Sets
- Lets create a simple set which contains these n° 1,2,3,4,5 :
my_set = {1, 2, 3, 4, 5}
print(my_set)
- Add add an element to my set eg. 5
new_set = my_set.add(0)
print(new_set)
Explanation : The reason new_set = my_set.add(5) gives you None is that the .add() method in Python does not return any value; it modifies the set in place. When a method doesn't return anything explicitly, Python implicitly returns None.
- Lets try
my_set.add(0)
print(my_set)
- Adding multiple elements to a set using the update()
print("Initial set :",my_set)
# Add multiple elements
my_set.update([6, 7, 8])
print("Updated set :", my_set)
Removing Elements from Sets
- To remove an element for a set , you may use either .remove() which Raises a KeyError if the element is not found.
- Use Case: When you're certain the element exists in the set.
print("Initial set : ", my_set)
# Remove n° 3
my_set.remove(3)
print(my_set)
-
Re-run the above cell and checkout what is the output
-
To remove an element for a set , you may use either .discard() Does not raise an error if the element is not found; it simply does nothing.
-
Use Case: When you're not sure if the element exists and want to avoid errors.
-
Try to remove the non-existant element "3" removed before
# Remove n° 3
my_set.discard(3)
print(my_set)
Summary
| Method | Behavior if the element is not in the set | Raises Error? | Use Case |
|-------------|-------------------------------------------|---------------|----------------------------------------|
| .remove()
| Raises a KeyError
. | Yes | Use when you're sure the element exists. |
| .discard()
| Does nothing. | No | Use when you're not sure if the element exists. |
Membership Testing
Check if an element exists in a set using the in
keyword.
print(my_set)
print(3 in my_set)
print(6 in my_set)
### Mathematical Operations Mathematical Operations on Sets :
- Union (
|
or.union()
) - Intersection (
&
or.intersection()
) - Difference (
-
or.difference()
) - Symmetric Difference (
^
or.symmetric_difference()
)
set_a = {1, 2, 3, 4}
set_b = {3, 4, 5, 6}
Union
print("set_a",set_a)
print("set_b",set_b)
print("Union:", set_a | set_b)
Intersection
print("set_a",set_a)
print("set_b",set_b)
print("Intersection:", set_a & set_b)
Difference
print("set_a",set_a)
print("set_b",set_b)
# This returns elements that are in set_a but not in set_b
# unique to set_a
print("Difference (A - B):", set_a - set_b)
#This returns elements that are in set_b but not in set_a.
# unique to set_b
print("Difference (B - A):", set_b - set_a)
Symmetric Difference
#The symmetric difference consists of the parts of A and B that do not overlap.
print("Symmetric Difference:", set_a ^ set_b)
Copying Sets
Copying sets is essential when you want to work with a new set derived from an existing set without altering the original set. Without copying, any modifications to the new set would also affect the original set (if both sets reference the same memory location). By creating a copy, you ensure that the original data remains unchanged.
original_set = {1, 2, 3}
copy_set = original_set # No copying, both refer to the same set
copy_set.add(4)
print("Original Set:", original_set) # Output: {1, 2, 3, 4}
print("copy Set:", copy_set) # Output: {1, 2, 3, 4}
- remove the element 4 from the copied set
print("Original Set:", original_set)
print("copy Set:", copy_set)
copy_set.remove(4)
print("modified_set :",copy_set)
Sets as Filters
- Create an empty set named set_with_duplicates containing {1, 2, 2, 3, 4, 4, 5}:
set_with_duplicates = {1, 2, 2, 3, 4, 4, 5}
print("Set removes duplicates and only let unique element:", set_with_duplicates)
Summary
Key Differences Between List, Tuple, and Set
Feature | List | Tuple | Set |
---|---|---|---|
Definition | Ordered, mutable collection of elements. | Ordered, immutable collection of elements. | Unordered, mutable collection of unique elements. |
Syntax | Defined using square brackets [] . | Defined using parentheses () . | Defined using curly braces {} or the set() function. |
Order | Maintains the order of elements. | Maintains the order of elements. | Does not maintain any specific order. |
Mutability | Mutable: Can add, remove, or modify elements. | Immutable: Elements cannot be changed after creation. | Mutable: Can add or remove elements, but not change individual values. |
Duplicates | Allows duplicate elements. | Allows duplicate elements. | Does not allow duplicate elements. |
Performance | Slower than tuples due to mutability. | Faster than lists for iterations due to immutability. | Faster lookups and membership tests due to hashing. |
Use Case | Suitable for collections that may change. | Suitable for fixed collections of data. | Suitable for collections of unique items. |
Methods | Extensive: e.g., append() , remove() . | Limited: e.g., count() , index() . | Specialized: e.g., add() , remove() , union() , intersection() . |
Homogeneity | Can store heterogeneous elements. | Can also store heterogeneous elements. | Can also store heterogeneous elements, but all elements must be hashable. |
Example | [1, 2, 3] | (1, 2, 3) | {1, 2, 3} or set([1, 2, 3]) |
ENJOY !!
Section 6 : Dictionaries
A Python dictionary is an unordered collection of items. It is a collection of key-value pairs, where each key is unique, and the value can be any Python object.
my_dict = {key1: value1, key2: value2, key3: value3 ... }
or
my_dict = dict(key1=value1, key2=value2, key3=value3 ...)
Creating a Dictionary
To create a dictionary, you can use curly braces {} or the dict() constructor.
my_dict_exp1 = {'name': 'Alice', 'age': 25, 'city': 'New York'}
print(my_dict_exp1)
my_dict_exp2 = dict(name='Firas', work='PhD Student', city='Tunisia')
print(my_dict_exp2)
Accessing Values by Key
using square brackets []
You can access the value associated with a specific key by using square brackets [].
print(my_dict_exp1['name'])
Using the get() Method
The get() method returns the value for the given key if it exists, otherwise, it returns None (or a default value if provided).
# get the value assigned to the key age in my_dict_exp1
print(my_dict_exp1.get('age'))
# get the value assigned to the key gender in my_dict_exp1
print(my_dict_exp1.get('gender'))
# get the value assigned to the key age then gender in my_dict_exp1
# if the value do not exist return a text 'notfound ha ha ha'
print(my_dict_exp1.get('age','not found ha ha ha'))
print(my_dict_exp1.get('gender','not found ha ha ha'))
Accessing Keys keys() - Returns a view of all keys in the dictionary.
print(my_dict_exp1.keys())
Accessing Values values() - Returns a view of all values in the dictionary.
print(my_dict_exp1.values())
Accessing Items keys() - Returns a view of all keys in the dictionary.
print(my_dict_exp1.items())
Modifying Dictionaries
Adding Items
You can add new key-value pairs by simply assigning a value to a new key.
my_dict_exp2['email'] = 'zemzemfiras@GMail.com'
print(my_dict_exp2)
Updating Items
You can change the value of an existing key by assigning a new value.
my_dict_exp2['email'] = 'zemzemfiras@gmail.com'
print(my_dict_exp2)
Now we are only using this exmaple
my_dict_exp2 = dict(name='Firas', work='PhD Student', city='Tunisia', email='zemzemfiras@gmail.com')
print(my_dict_exp2)
Removing Items
Use the del keyword to remove a key-value pair.
del my_dict_exp2['city']
print(my_dict_exp2)
use the pop() method to remove and return a value
removed_value = my_dict_exp2.pop('name') # Removes 'name' and returns 'Alice'
print("removed value is :",removed_value)
Clearing Dictionaries
my_dict_exp1.clear() # The dictionary is now empty
print(my_dict_exp1)
Nested Dictionaries
A nested dictionary is simply a dictionary that contains other dictionaries as values. This allows for hierarchical data representation, making it useful for complex data structures where values themselves are dictionaries or require grouping into logical structures.
Importance of Nested Dictionaries:
-
Hierarchical Data Representation: They allow the representation of complex data structures, such as organizational structures, configuration settings, and nested objects in APIs.
-
Grouping and Categorizing: Nested dictionaries help to group related data together, which makes it easier to work with and access.
-
Improved Readability: They make code more readable and structured, especially when dealing with multidimensional data, such as JSON-like objects.
-
Efficient Storage and Access: They help in organizing data efficiently, where each dictionary within the main dictionary can hold its own set of key-value pairs.
Basic Example of a Nested Dictionary
A simple case where one dictionary contains another dictionary as a value.
Inside the nested dictionary 'address', we have keys like 'street', 'city', and 'zip'.
person = {
'name': 'Alice',
'age': 30,
'address': {
'street': '123 Main St',
'city': 'Wonderland',
'zip': '12345'
}
}
# Accessing nested dictionary values
print(person['address']['city']) # Output: Wonderland
Creating Databases with Nested Dictionaries
an example of storing data about multiple students, where each student has their own dictionary.
students = {
'student1': {
'name': 'Alice',
'age': 20,
'grades': {
'math': 90,
'english': 85
}
},
'student2': {
'name': 'Bob',
'age': 22,
'grades': {
'math': 80,
'english': 88
}
}
}
print(students)
Accessing a nested Dictionary
# print Student 1 nested Dict
print(students['student1'])
# print Student 2 nested Dict
print(students['student2'])
Accessing a value in nested dictionary
# Accessing Bob's age
print(students['student2']['age'])
# Accessing the grade of Alice (student1) in Math
print(students['student1']['grades']['math'])
Configurations with Nested Dictionaries
Nested dictionaries are often used to represent complex configuration files, like settings for a web application.
config = {
'database': {
'host': 'localhost',
'port': 5432,
'user': 'admin',
'password': 'securepass'
},
'logging': {
'level': 'DEBUG',
'log_file': '/var/log/app.log'
}
}
print(config)
Species Characteristics and Habitats
# Accessing database configurations
print(config['database']['host']) # Output: localhost
print(config['logging']['log_file']) # Output: /var/log/app.log
species_data = {
'lion': {
'classification': 'Mammal',
'habitat': {
'continent': 'Africa',
'environment': 'Savanna',
'climate': 'Tropical'
},
'diet': 'Carnivore',
'conservation_status': 'Vulnerable'
},
'elephant': {
'classification': 'Mammal',
'habitat': {
'continent': 'Africa',
'environment': 'Grasslands',
'climate': 'Tropical'
},
'diet': 'Herbivore',
'conservation_status': 'Endangered'
},
'penguin': {
'classification': 'Bird',
'habitat': {
'continent': 'Antarctica',
'environment': 'Coastal',
'climate': 'Polar'
},
'diet': 'Carnivore',
'conservation_status': 'Least Concern'
},
'kangaroo': {
'classification': 'Mammal',
'habitat': {
'continent': 'Australia',
'environment': 'Outback',
'climate': 'Arid'
},
'diet': 'Herbivore',
'conservation_status': 'Near Threatened'
}
}
print(species_data)
# Accessing specific information:
# 1. Access the habitat of the elephant
elephant_habitat = species_data['elephant']['habitat']
print(f"Elephant Habitat: {elephant_habitat}")
# Output: Elephant Habitat: {'continent': 'Africa', 'environment': 'Grasslands', 'climate': 'Tropical'}
# 2. Access the conservation status of the lion
lion_status = species_data['lion']['conservation_status']
print(f"Lion Conservation Status: {lion_status}")
# Output: Lion Conservation Status: Vulnerable
# 3. Access the diet of the penguin
penguin_diet = species_data['penguin']['diet']
print(f"Penguin Diet: {penguin_diet}")
# Output: Penguin Diet: Carnivore
ENJOY !!
Section 7 : Control flow statements
Control flow statements dictate the order in which instructions in a program are executed. Python provides several mechanisms for controlling flow:
- Conditional Statements: Execute different blocks of code based on conditions.
- Loops: Repeat code execution under specified conditions.
Conditional Statements
Conditional statements in Python allow you to execute certain blocks of code based on whether a condition is True or False. These conditions are evaluated using comparison operators or boolean expressions, which can help in making decisions in your code. The main conditional statements in Python are if, if-else, and if-elif-else.
If-Else Statements
If-else statements allow your program to make decisions based on certain conditions.
Syntax
if condition:
# Code block executed if condition is True
else:
# Code block executed if condition is False
Basic If-Else Example
x = 10
if x > 5:
print("x is greater than 5")
else:
print("x is less than or equal to 5")
Example: Check for a Specific DNA Base
write a Python program to check if a DNA sequence contains the base A (adenine)
dna_sequence = "ATCGGATCGA"
if "A" in dna_sequence:
print("The sequence contains adenine (A).")
else:
print("The sequence does not contain adenine (A).")
If-Elif-Else Example:
Sometimes, you need to check multiple conditions. This is where elif (else if) comes into play. It allows you to check multiple conditions in sequence.
if condition1:
# Code block executed if condition1 is True
elif condition2:
# Code block executed if condition1 is False and condition2 is True
else:
# Code block executed if all the above conditions are False
Basic If-Else Example
score = 85
if score >= 90:
print("Grade: A")
elif score >= 80:
print("Grade: B")
elif score >= 70:
print("Grade: C")
else:
print("Grade: F")
Example: Classify DNA Sequence Based on Length
use if-elif-else to classify DNA sequences based on their length (e.g., short, medium, long sequences).
dna_sequence = "ATGCGTACG"
if len(dna_sequence) < 50:
print("This is a short DNA sequence.")
elif len(dna_sequence) <= 200:
print("This is a medium-length DNA sequence.")
else:
print("This is a long DNA sequence.")
Loops in Python
Loops are used to repeatedly execute a block of code. Python provides two types of loops: for loops and while loops
For Loop
The for loop is generally used when you know the number of iterations you want to perform or when you're working with a sequence of elements (such as a list, string, or tuple).
for variable in sequence:
# Code block to execute
Basic Example
fruits = ["apple", "banana", "cherry"]
for fruit in fruits:
print(fruit)
Example: Counting Specific Bases in a DNA Sequence
We can use a for loop to iterate over a DNA sequence and count the occurrence of each base
dna_sequence = "ATCGGATCGA"
count_A = 0
count_T = 0
count_C = 0
count_G = 0
for base in dna_sequence:
if base == "A":
count_A += 1
elif base == "T":
count_T += 1
elif base == "C":
count_C += 1
elif base == "G":
count_G += 1
print(f"A: {count_A}, T: {count_T}, C: {count_C}, G: {count_G}")
For Loop with Range
Hint : The range() function generates a sequence of numbers, which can be used with for loops.
for i in range(start, stop, step):
# Code block to execute
example
for i in range(1, 6): # Range from 1 to 5
print(i)
example : Extracting Substrings from DNA Sequence
You can use a for loop combined with range() to extract specific sections of a DNA sequence (substrings).
dna_sequence = "ATCGGATCGA"
# Extract every 3rd base from the DNA sequence
for i in range(0, len(dna_sequence), 3):
print(dna_sequence[i:i+3]) # Extracts a substring of length 3
Nested For Loops
You can have one for loop inside another. This is useful for iterating over multi-dimensional data structures like lists of lists.
matrix = [[1, 2, 3], [4, 5, 6], [7, 8, 9]]
for row in matrix:
for element in row:
print(element)
While Loop
A while loop repeatedly executes a block of code as long as a specified condition is True.
while condition:
# Code block to execute as long as condition is True
Example
count = 0
while count < 5:
print(count)
count += 1 # Increase count by 1 in each iteration
Example: Searching for a Pattern in a DNA Sequence
You can use a while loop when you need to iterate through a sequence and search for specific patterns, such as a certain motif in a DNA sequence.
dna_sequence = "ATCGGATCGAATGATGCTAG"
pattern = "ATG"
index = 0
while index < len(dna_sequence):
index = dna_sequence.find(pattern, index)
if index == -1: # No more occurrences found
break
print(f"Pattern '{pattern}' found at index {index}")
index += len(pattern) # Move past the found pattern
Break Statements
The break statement is used to exit the current loop (either for or while) prematurely when a specific condition is met. After the break statement is executed, the control moves to the first statement after the loop.
Syntax 1 :
for item in iterable:
# code block
if condition_to_break:
break # Exit the loop if the condition is met
# other code
Syntax 2 :
while condition:
# code block
if condition_to_break:
break # Exit the loop if the condition is met
# other code
Break in a for loop
The following example finds the first occurrence of the number 5 in a list of numbers and then exits the loop using the break statement.
numbers = [1, 3, 5, 7, 9, 5, 11]
for number in numbers:
if number == 5:
print("Found the number 5!")
break # Exit the loop after finding the first 5
### Break in a while loop
This example demonstrates the use of break in a while loop, which exits once the variable count exceeds 5.
count = 0
while count <= 5:
print(count)
if count == 3:
break # Exit the loop when count is 3
count += 1
Example: Stop Searching for Pattern After a Certain Number of Occurrences
dna_sequence = "ATGCGATGCTGATGGAAT"
pattern = "ATG"
occurrence_limit = 2
count = 0
index = 0
while count < occurrence_limit:
index = dna_sequence.find(pattern, index)
if index == -1: # No more occurrences found
break
print(f"Pattern '{pattern}' found at index {index}")
count += 1
index += len(pattern) # Move past the found pattern
Continue Statement
The continue statement is used to skip the current iteration of a loop and move to the next iteration. It is often used when you want to skip certain conditions but continue processing the remaining items.
Syntax1:
while condition:
# code block
if condition_to_skip:
continue # Skip the rest of the code and move to the next iteration
# other code
Syntax2:
for item in iterable:
# code block
if condition_to_skip:
continue # Skip the rest of the code and move to the next iteration
# other code
Continue in a for loop
The following example skips the number 5 in the list of numbers, but prints the rest.
numbers = [1, 3, 5, 7, 9, 5, 11]
for number in numbers:
if number == 5:
continue # Skip the rest of the loop when number is 5
print(number)
Continue in a while loop
This example skips the iteration when the variable count is 3, but continues the loop for other values.
count = 0
while count <= 5:
count += 1
if count == 3:
continue # Skip the iteration when count is 3
print(count)
Example: Skip a Specific Base in a DNA Sequence
dna_sequence = "ATCGGATCGA"
for base in dna_sequence:
if base == "G":
continue # Skip 'G' bases
print(base)
Practice
### EXercice 1 : Write a program that checks if a given DNA sequence contains both "ATG" (start codon) and "TAA" (stop codon).
dna_sequence = input("Enter DNA sequence: ")
if "ATG" in dna_sequence and ("UAG" in dna_sequence or "UAA" in dna_sequence or "UGA" in dna_sequence):
print("The sequence contains both the start and stop codons.")
else:
print("The sequence does not contain both start and stop codons.")
Exercice 2 :
Write a program to count how many times the codon "ATG" appears in a DNA sequence.
dna_sequence = "ATGCGATGCTGATGGAAT"
count_ATG = 0
#print(len(dna_sequence))
#print(range(len(dna_sequence) - 2))
for i in range(len(dna_sequence) - 2): # Loop through the sequence
#print(i)
#print(dna_sequence[i:(i+3)])
if dna_sequence[i:i+3] == "ATG":
count_ATG += 1
print(f"Codon 'ATG' appears {count_ATG} times.")
Exercice 3 :
Write a program that continuously asks the user for DNA sequences and checks if the sequence is valid (only contains A, T, C, G) until a valid sequence is entered.
valid_bases = set("ATCG")
while True:
dna_sequence = input("Enter a DNA sequence: ")
if set(dna_sequence.upper()).issubset(valid_bases):
print("Valid DNA sequence.")
break
else:
print("Invalid DNA sequence. Please enter only A, T, C, and G.")
Exercice 4 :
Write a program that stops when it finds the first "CG" pair in a DNA sequence and skips all other "CG" pairs.
dna_sequence = "ATGCGATGCGCGT"
index = 0
while index < len(dna_sequence):
index = dna_sequence.find("CG", index)
if index == -1: # No more occurrences found
break
print(f"'CG' found at index {index}")
index += 2 # Move past the found "CG"
Enjoy
Section 8 : Functions
A function is a block of reusable code that performs a specific task. Functions allow us to break down complex problems into smaller, more manageable parts. Python provides a simple way to define and use functions.
In this lesson, we will cover:
- Defining a function
- Function parameters
- Return values
- Function scope
- Lambda functions
- Recursive functions
Defining a Function
To define a function in Python, we use the def keyword followed by the function name, parentheses (), and a colon :. After the colon, we write the code block that should execute when the function is called.
Syntax:
def function_name():
# Code to be executed
print("Hello, this is a function!")
Example
# Make a fuction that prints "Hello Learners"
def Greeting():
print("Hello Learners")
# Call Greeting() function
Greeting()
Functions Parameters
Functions can accept inputs, known as parameters. These parameters are specified within the parentheses of the function definition.
Syntax:
def function_name(parameter1, parameter2):
# Code that uses the parameters
print(f"Hello {parameter1}, you are {parameter2} years old.")
Example
# function name : introduce
# Parameter 1 : name
# Parameter 2 : age
def introduce(name, age):
print(f"Hello {name}, you are {age} years old.")
# Calling the function with arguments alice for name / 25 for age
introduce("Alice", 25)
Return Values
A function can return a value using the return keyword. This value can then be used elsewhere in the program.
Syntax:
def function_name():
return value
Example
def add_numbers(a, b):
return a + b
result = add_numbers(10, 5)
print(f"The sum is: {result}")
Local Scope
Variables created inside a function are local to that function and cannot be accessed outside of it. This is called local scope. Variables defined outside the function are considered global variables and can be accessed anywhere in the program.
Example
def show_local_scope():
local_variable = "I am local"
print(local_variable)
show_local_scope()
# Uncommenting the next line will cause an error
# print(local_variable) # This will give an error because local_variable is not accessible outside the function
lets try to print local variable outside
def show_local_scope():
local_variable = "I am local"
show_local_scope()
# Uncommenting the next line will cause an error
# print(local_variable) # This will give an error because local_variable is not accessible outside the function
lets make a global variable : global_variable then implement it in our function
global_variable = "I am global"
def show_local_scope():
print(global_variable)
show_local_scope()
Default Parameters
You can assign default values to parameters in a function. This way, if no argument is passed when calling the function, the default value will be used.
Syntax:
def function_name(parameter1=default_value):
# Code using the parameter
print(parameter1)
Example:
def greet(name="Guest"):
print(f"Hello, {name}!")
greet("Alice")
greet()
Lambda Functions
A lambda function is a small anonymous function defined using the lambda keyword. These functions are typically used for short, one-off operations.
Syntax:
lambda arguments: expression
multiply = lambda x, y: x * y
print(multiply(4, 5)) # Output: 20
Recursive Functions
A recursive function is one that calls itself in its definition. It is often used to solve problems that can be broken down into smaller, similar subproblems (e.g., calculating factorials, traversing trees).
Syntax:
def recursive_function():
# base case
if condition:
return result
# recursive case
else:
return recursive_function()
Example
def factorial(n):
# Base case: factorial of 0 or 1 is 1
if n == 0 or n == 1:
return 1
# Recursive case
else:
return n * factorial(n - 1)
print(factorial(5))
Functions as Arguments
You can pass functions as arguments to other functions. This allows for greater flexibility and allows for higher-order functions.
Example:
def apply_function(f, x):
return f(x)
def square(n):
return n * n
result = apply_function(square, 5)
print(result) # Output: 25
Appling Functions in Bioinformatics
Example: Calculating GC Content
This function calculates the GC content of a DNA sequence.
-
GC content is the percentage of G and C bases in the sequence.
-
Args:
-
sequence (str): A string representing the DNA sequence (e.g., 'ATGCGT').
-
Returns: float: The GC content as a percentage.
-
def gc_content(sequence):
gc_count = sequence.count('G') + sequence.count('C')
return (gc_count / len(sequence)) * 100
sequence = "ATGCGTACG"
gc_percentage = gc_content(sequence)
print(f"GC content: {gc_percentage:.2f}%")
Example: Counting Nucleotides in a DNA Sequence
This function counts the occurrences of each nucleotide in a DNA sequence.
-
Args:
-
sequence (str): A string representing the DNA sequence (e.g., 'ATGCGT').
-
Returns: dict: A dictionary with counts for 'A', 'T', 'C', and 'G'.
-
def count_nucleotides(sequence):
return {'A': sequence.count('A'),
'T': sequence.count('T'),
'C': sequence.count('C'),
'G': sequence.count('G')}
sequence = "ATGCGTACG"
nucleotide_counts = count_nucleotides(sequence)
print(f"Nucleotide counts: {nucleotide_counts}")
Example: Finding a Subsequence in a DNA Sequence
This function checks if a given subsequence is found in a DNA sequence.
-
Args:
-
sequence (str): The DNA sequence to search within.
-
subsequence (str): The subsequence to search for.
-
-
Returns: bool: True if subsequence is found, False otherwise.
def find_subsequence(sequence, subsequence):
if subsequence in sequence:
return True
else:
return False
sequence = "ATGCGTACG"
subsequence = "CGT"
found = find_subsequence(sequence, subsequence)
print(f"Subsequence found: {found}")
Example: Translating DNA to Protein This function translates a DNA sequence into a protein sequence. It translates each codon (3 nucleotides) into an amino acid.
-
Args:
-
dna_sequence (str): The DNA sequence to translate.
-
Returns: str: The corresponding protein sequence.
-
def translate_dna_to_protein(dna_sequence):
codon_table = {
"ATA": "I", "ATC": "I", "ATT": "I", "ATG": "M",
"ACA": "T", "ACC": "T", "ACG": "T", "ACT": "T",
"AAC": "N", "AAT": "N", "AAA": "K", "AAG": "K",
"AGC": "S", "AGT": "S", "AGA": "R", "AGG": "R",
"CTA": "L", "CTC": "L", "CTG": "L", "CTT": "L",
"CCA": "P", "CCC": "P", "CCG": "P", "CCT": "P",
"CAC": "H", "CAT": "H", "CAA": "Q", "CAG": "Q",
"CGA": "R", "CGC": "R", "CGG": "R", "CGT": "R",
"GTA": "V", "GTC": "V", "GTG": "V", "GTT": "V",
"GCA": "A", "GCC": "A", "GCG": "A", "GCT": "A",
"GAC": "D", "GAT": "D", "GAA": "E", "GAG": "E",
"GGA": "G", "GGC": "G", "GGG": "G", "GGT": "G",
"TCA": "S", "TCC": "S", "TCG": "S", "TCT": "S",
"TTC": "F", "TTT": "F", "TTA": "L", "TTG": "L",
"TAC": "Y", "TAT": "Y", "TAA": "*", "TAG": "*",
"TGC": "C", "TGT": "C", "TGA": "*", "TGG": "W",
"CTA": "L", "CTC": "L", "CTG": "L", "CTT": "L",
}
protein = ""
# Iterate over the sequence in steps of 3 nucleotides (codons)
for i in range(0, len(dna_sequence), 3):
codon = dna_sequence[i:i+3]
if codon in codon_table:
protein += codon_table[codon]
return protein
dna_sequence = "ATGGCCAAGGTTTAA"
protein_sequence = translate_dna_to_protein(dna_sequence)
print(f"Protein sequence: {protein_sequence}")
ENJOY !!
Section 9 : Error Handling
Error handling is a critical part of writing reliable Python code. In this course, we'll dive deep into Python's error-handling mechanism, starting from the basics and moving towards advanced topics like custom exceptions, context managers, and logging errors. By the end of this course, you'll have a solid understanding of how to manage errors effectively in your Python programs.
Basics of Error Handling
What are Errors and Exceptions?
-
Errors: Critical problems that are typically not recoverable (e.g., syntax errors, system-level issues).
-
Exceptions: Conditions that arise during the execution of a program but can be handled and recovered from.
Try-Except Block
The try-except block is the fundamental building block of error handling in Python. It allows you to catch and handle exceptions that occur during the execution of a program. By using try-except, you can prevent your program from crashing and provide meaningful responses to errors.
Why Use try-except?
- Prevent your program from crashing due to runtime errors.
- Gracefully handle unexpected situations and provide meaningful error messages.
- Allow execution of alternative logic when errors occur.
Syntax:
try:
# Code that might raise an exception
except ExceptionType:
# Code to handle the exception
Lets try to print this code :
print(10 / 0)
By using try-except, you can prevent your program from crashing and provide meaningful responses to errors.
try:
result = 10 / 0
except:
print("An error occurred.") # if it causes an error print "An error occurred."
Using the Exception Object
To access details about the exception, use the as keyword.
try:
x = 10 / 0 # will crach and provide an error
except ZeroDivisionError as e: # if it is an error print details about it without crashing.
print(f"Error occurred: {e}")
Multiple except Blocks
You can use multiple except blocks to handle different types of exceptions.
try:
x = int("hello") # Will raise a ValueError
except ZeroDivisionError:
print("Division by zero error")
except ValueError:
print("Invalid input: Could not convert to an integer")
Generic Exception Handling
To handle any exception, use except Exception. While it's better to catch specific exceptions, this can be useful for debugging.
Syntax:
try:
# Code that might raise an exception
except Exception as e:
print(f"An unexpected error occurred: {e}")
Example 1
try:
result = 10 / "a" # TypeError
except Exception as e:
print(f"An unexpected error occurred: {e}")
Example 2: Reading a File Safely
try:
with open("file.txt", "r") as file:
data = file.read()
except FileNotFoundError:
print("Error: The file does not exist.")
except PermissionError:
print("Error: You do not have permission to access this file.")
or making it more general
try:
with open("file.txt", "r") as file:
data = file.read()
except Exception as e:
print(f"Error: {e}")
Else and Finally
The else and finally clauses add more control and flexibility to Python's try-except mechanism, allowing you to handle code execution in different scenarios, such as when no exception occurs or when cleanup operations are needed.
Else Clause
The else block is executed only if no exception occurs in the try block. This is useful for separating error handling (except) from normal execution when everything works as expected.
- If an exception occurs: The else block is skipped.
- If no exception occurs: The else block is executed.
Syntax
try:
# Code that might raise an exception
except ExceptionType:
# Code to handle the exception
else:
# Code to execute if no exception occurred
try:
result = 10 / 2 # No exception occurs
except ZeroDivisionError:
print("Error: Division by zero.")
else:
print(f"Division successful, result is {result}")
finally Clause
The finally block is executed no matter what, whether an exception is raised or not. This is useful for cleanup actions, like closing a file or releasing a resource.
Syntax
try:
# Code that might raise an exception
except ExceptionType:
# Code to handle the exception
finally:
# Code to execute regardless of what happens
Example
try:
x = int(input("Enter the numerator: "))
y = int(input("Enter the denominator: "))
div = x / y # Perform division
except ValueError as e:
print(f"Error: Invalid input. Please enter integers only. {e}")
except ZeroDivisionError as e:
print(f"Error: Cannot divide by zero. {e}")
else:
print(f"Division was successful: {x}/{y} = {div}")
finally:
print("This will always execute.")
raise Statement in Python
The raise statement in Python is used to explicitly raise an exception. It allows you to signal that something unexpected has occurred and gives you control over the error being raised.
- ExceptionType: The type of exception you want to raise (e.g., ValueError, TypeError, KeyError, or a custom exception).
- Error message: An optional message providing more details about the error.
Syntax
raise ExceptionType("Error message")
Example 1 : Input Validation
def validate_age(age):
if age < 0:
raise ValueError("Age cannot be negative")
print(f"Valid age: {age}")
# Test the function
try:
validate_age(-5)
except ValueError as e:
print(f"Error: {e}")
Example 2: Raising TypeError
def add_numbers(a, b):
if not isinstance(a, (int, float)) or not isinstance(b, (int, float)):
raise TypeError("Both arguments must be numbers")
return a + b
# Test the function
try:
result = add_numbers(10, "20")
except TypeError as e:
print(f"Error: {e}")
Reraising an Exception
If you want to re-raise an exception that you've caught, you can use raise without specifying an exception. This is useful for adding context to an error before passing it on.
try:
x = 10 / 0
except ZeroDivisionError:
print("Handling ZeroDivisionError")
raise
Using raise with a Cause
The raise statement can be used with a from keyword to link a new exception to an original one. This is helpful for debugging complex issues.
try:
int("abc") # Raises ValueError
except ValueError as e:
raise TypeError("Conversion error occurred") from e
Using Assert for Debugging
The assert statement is a debugging tool in Python used to test assumptions in your code. It helps ensure that a condition is true at a specific point in the program. If the condition evaluates to False, assert raises an AssertionError and optionally displays an error message.
Syntax
assert condition, "Error message (optional)"
- condition: A boolean expression that you expect to be True.
- "Error message": Optional; describes why the assertion failed.
If the condition evaluates to False:
- An AssertionError is raised.
- The optional error message (if provided) is displayed.
When to Use assert
- To test assumptions in your code while developing or debugging.
- To ensure inputs to a function meet certain criteria.
- To check the state of variables at critical points in your code.
Example
x = 10
assert x > 0, "x must be positive"
print("Assertion passed!")
Try a negative x
x = -5
assert x > 0, "x must be positive"
print("Assertion passed!")
Example 2 :
assert 2 + 2 == 4, "Math error"
assert 2 + 2 == 5, "Math error" # This will raise AssertionError
Validating Function Input
def divide(a, b):
assert b != 0, "Denominator cannot be zero"
return a / b
# Test the function
print(divide(10, 2)) # Valid
print(divide(10, 0)) # Raises AssertionError
Example 3 : Checking Data Integrity
data = [1, 2, 3, 4]
assert all(isinstance(x, int) for x in data), "All elements in the list must be integers"
print("Data is valid!")
Handling Signals and External Errors
Signals are a mechanism used by operating systems to notify a process of an event. Python's signal module allows you to handle these signals gracefully.
Common Use Cases for Signals
- Handling Ctrl+C (keyboard interrupt) gracefully.
- Responding to timeouts or resource limits.
- Coordinating between processes in multiprocessing or parallel programs. Note : This part will be developped in another part.
ENJOY !!
Section 10 : Class
what is it !!!
A class is a blueprint for creating objects. Objects are instances of a class, and they bundle together data (attributes) and behavior (methods) to model real-world entities.
Syntax
class ClassName:
# Attributes and methods go here
## Class's Components
Constructor (init)
The constructor is a special method that is called automatically when an object is created. It initializes the object’s attributes.
class MyClass:
def __init__(self, name, age):
self.name = name # Assigning the value to self.name
self.age = age # Assigning the value to self.age
def greeting(self):
return f"Hello, {self.name}"
# Instantiate the class with the correct arguments
person1 = MyClass("Mario", 30)
print(person1.name)
print(person1.age)
# Call the greet method to see the result
print(person1.greeting())
Atributes
Attributes in a class are variables or data that are associated with a class and its objects. They are used to store information relevant to the class and its instances. Attributes can be broadly categorized as:
-
Instance Attributes: Unique to each object and set in the constructor.
-
Class Attributes: Shared by all objects of the class.
class MyClass:
class_attribute = "Shared by all instances" # Class attribute
def __init__(self, name):
self.name = name # Instance attribute
Class Attribute:
-
Class attributes are accessed through the class itself or through any instance. However, modifying a class attribute through an instance will modify it only for that specific instance, not the class itself.
-
These are attributes that are shared across all instances of a class.
-
They are defined within the class but outside any methods.
-
Class attributes are accessed using the class name or an instance of the class.
-
class MyClass:
class_attribute = "Shared by all instances" # Class attribute
def __init__(self, name):
self.name = name # Instance attribute
# Creating instances
obj1 = MyClass("Alice")
obj2 = MyClass("Bob")
# Accessing the class attribute via the class
print(MyClass.class_attribute) # Shared by all instances
# Accessing the class attribute via an instance
print(obj1.class_attribute) # Shared by all instances
print(obj2.class_attribute) # Shared by all instances
Instance Attribute:
-
Instance attributes are unique to each object created from the class and can only be accessed through the instance.
-
These are attributes specific to an instance of a class.
-
They are typically defined in the init method and are prefixed with self.
-
Each instance of the class has its own copy of the instance attributes.
-
# Accessing the instance attribute
print(obj1.name) # Alice
print(obj2.name) # Bob
# Modifying the class attribute via an instance
obj1.class_attribute = "Modified by obj1"
print(obj1.class_attribute) # Modified by obj1 (specific to obj1)
print(obj2.class_attribute) # Shared by all instances (not modified for obj2)
Creating an Object
Syntax
Object = Class_Name(Attributes)
# Define a class
class Person:
def __init__(self, name, age):
self.name = name # Instance attribute
self.age = age # Instance attribute
def introduce(self): # Instance method
return f"My name is {self.name}, and I am {self.age} years old."
# Create an object (instance of Person)
person1 = Person("BatMan", 25)
# Access attributes
print(person1.name) # Output: BatMAn
print(person1.age) # Output: 25
# Call a method
print(person1.introduce()) # Output: My name is Alice, and I am 25 years old.
Methods
Methods are functions defined within a class. They define the behavior of objects. Methods take self as the first argument to access object attributes.
Instance Methods:
Work with an instance of the class (self refers to the instance).
class MyClass:
def __init__(self, name):
self.name = name
def greet(self):
return f"Hello, {self.name}!"
print(MyClass("SuperMan").greet())
# or Object = Class_Name(Attributes)
person = MyClass("BatMan")
print(person.greet())
Class Methods:
Work on the class itself. Use @classmethod and the cls parameter.
class MyClass:
class_attribute = "Hello, World!"
@classmethod
def class_method(cls, param):
print(f"Class Attribute: {cls.class_attribute}")
print(f"Parameter: {param}")
# Call the class method
MyClass.class_method("Example")
Static Methods:
Do not depend on instance or class. Use @staticmethod.
class Example:
class_attribute = "Shared"
def __init__(self, value):
self.instance_attribute = value
@classmethod
def class_method(cls):
return f"Class Attribute: {cls.class_attribute}"
@staticmethod
def static_method():
return "This is a static method"
# Usage
obj = Example("Unique Value")
print(obj.class_method()) # Output: Class Attribute: Shared
print(obj.static_method()) # Output: This is a static method
Inheritance
Inheritance allows a class (child class) to inherit attributes and methods from another class (parent class).
# Parent class
class Animal:
def __init__(self, species):
self.species = species
def make_sound(self):
return "Some generic sound"
# Child class
class Dog(Animal):
def make_sound(self):
return "Woof!"
# Create an instance of Dog
dog = Dog("Canine")
print(dog.species) # Output: Canine
print(dog.make_sound()) # Output: Woof!
Encapsulation
Encapsulation restricts access to some attributes or methods to ensure they are not modified directly.
-
Use a single underscore (_) for protected attributes.
-
Use a double underscore (__) for private attributes.
class BankAccount:
def __init__(self, balance):
self.__balance = balance # Private attribute
def deposit(self, amount):
self.__balance += amount
def get_balance(self):
return self.__balance
# Create an account
account = BankAccount(1000)
account.deposit(500)
print(account.get_balance()) # Output: 1500
Polymorphism
- Polymorphism allows methods to have the same name but behave differently based on the object.
class Cat:
def make_sound(self):
return "Meow"
class Dog:
def make_sound(self):
return "Woof"
animals = [Cat(), Dog()]
for animal in animals:
print(animal.make_sound()) # Output: Meow, Woof
Example
class Car:
# Class attribute
wheels = 4
# Constructor
def __init__(self, brand, model, year):
self.brand = brand
self.model = model
self.year = year
# Instance method
def display_details(self):
return f"{self.year} {self.brand} {self.model}"
# Create instances
car1 = Car("Toyota", "Camry", 2020)
car2 = Car("Honda", "Civic", 2018)
# Access attributes and methods
print(car1.display_details()) # Output: 2020 Toyota Camry
print(car2.display_details()) # Output: 2018 Honda Civic
Summary of Key Terms
Term | Description |
---|---|
Class | A blueprint for creating objects. |
Object | An instance of a class. |
Attribute | A variable that holds data for the object. |
Method | A function defined in a class that operates on objects. |
Constructor | The __init__ method used to initialize object attributes. |
Inheritance | A way to create a new class using an existing class as a base. |
Encapsulation | Restricting access to certain attributes or methods. |
Polymorphism | Allowing methods to have the same name but behave differently in different contexts. |
ENJOY !!
Section 11 : File Handling
File handling is an essential part of programming that involves creating, reading, writing, and manipulating files. Python provides built-in functions and modules to perform file handling operations efficiently.
Checking File Existence
Before performing file operations, it’s good practice to check if a file exists.
Using os Module
import os
if os.path.exists('README.md'):
print('File exists... ')
else: print('File Not Found')
Using os Module
from pathlib import Path
myfile = Path('README.md')
if myfile.exists():
print('File exists... ')
else: print('File Not Found')
Exception Handling
try:
with open('exercices.txt', 'r') as file:
print(file.read())
except FileNotFoundError:
print('File not found.')
except IOError as e:
print(f'An error occurred: {e}')
Opening and Closing Files
To open a file python uses the open() fuction .
Syntax
file = open('filename', mode)
-
filename :Name of the file to be opened.
-
mode : Specifies the mode in which the file is opened (e.g., read, write).
-
Common file modes:
-
'r' : Read (default mode). File must exist.
-
'w' : Write. Creates a new file or truncates an existing one.
-
'x' : Create. Fails if the file exists.
-
'a' : Append. Adds to the end of the file.
-
'b' : Binary mode.
-
't' : Text mode (default).
-
'+' : Open for both reading and writing.
-
-
Method 1
# open file in read mode 'r'
file = open("shortstory.txt","r")
# Read file content
file.read()
if we add "file.close()" to the above cell the file will be closed
# open file in read mode 'r'
file = open("shortstory.txt","r")
# Read file content
file.read()
file.close()
Note that printing content file is different to read mode as we could print a file content of closed file
# open file in read mode 'r'
file = open("shortstory.txt","r")
# Read file content
print(file.read())
file.close()
Method 2 : Using with Statement (Preferred Method)
The with statement ensures the file is closed automatically:
with open('shortstory.txt', 'r') as file:
content = file.read()
print(content) # File is closed after this block
Reading Files
Python provides multiple methods to read files based on requirements...
Reading the Entire File
Reads the entire file content as a string
with open('shortstory.txt', 'r') as file:
content = file.read()
print(content)
Read Line by Line
Useful for processing files line by line
with open('shortstory.txt', 'r') as file:
for line in file:
print(line) # Removes newline character \n
Returns all lines as a list of strings
with open('shortstory.txt', 'r') as file:
lines = file.readlines()
print(lines)
Read Fixed Number of Characters
with open('shortstory.txt', 'r') as file:
content = file.read(110) # Reads the first 110 characters
print(content)
Writing to Files
Writing New Content
# as we dont have a practiceFile.txt in our directory
# 'w' will automatically create a new file
with open('practiceFile.txt', 'w') as file:
file.write("this is a new line ")
with open('practiceFile.txt', 'r') as file:
print(file.read())
Write another line : "Date: coming soon"
with open('practiceFile.txt', 'w') as file:
file.write("Title : Nextflow Course")
with open('practiceFile.txt', 'r') as file:
print(file.read())
Appending Content
To avoid overwriting in your file, and append content use 'a' mode and the newline character ( \n ) (optional)
with open('practiceFile.txt', 'a') as file:
file.write('\nDate: coming soon.')
with open('practiceFile.txt', 'r') as file:
print(file.read())
Writing Multiple Lines
lines = ['\nFiras Zemzem ', '\nzemzemfiras@gmail.com \n']
with open('practiceFile.txt', 'a') as file:
file.writelines(lines) # Writes a list of strings
with open('practiceFile.txt', 'r') as file:
print(file.read())
## File Positioning
Python provides methods to manipulate the file pointer during reading or writing
file.seek() : Moves the file pointer!
- 0 (default): Start of the file.
- 1: Current position.
- 2: End of the file.
# Corrected code
with open('practiceFile.txt', 'a+') as file: # Allows both writing(appending) and reading
file.write("Tunisia")
file.seek(0) # Move pointer to the start of the file
print(file.read()) # Read the content to verify
File Copying
Copy the contents of one file to another
with open('practiceFile.txt', 'r') as src, open('ContactINFO.txt', 'w') as dest:
dest.write(src.read())
CSV File Handling
Loading the Dataset
Using csv.reader
when you want to iterate over rows as lists.
import csv
with open('employee_records.csv', 'r') as file:
reader = csv.reader(file)
for row in reader:
print(row)
Using csv.DictReader
csv.DictReader reads the file into dictionaries where the keys are the column headers.
import csv
with open('employee_records.csv', 'r') as file:
reader = csv.DictReader(file)
for row in reader:
print(row)
csv.DictReader is particularly useful if you want to access specific fields by their names.
with open('employee_records.csv', 'r') as file:
reader = csv.DictReader(file)
for row in reader:
print(f"Name: {row['Name']}, \t Department: {row['Department']}, \t Salary : {row['Salary']}")
Add lines
import csv
# Add a new row to the CSV file
new_row = [9, 'Bob Brown', 'Engineering', 70000, '2024-01-15']
# Open the file in append mode ('a') to add a new row
with open('employee_records.csv', 'a', newline='') as file:
writer = csv.writer(file)
writer.writerow(new_row)
# Read and print the content of the CSV file
with open('employee_records.csv', 'r') as file:
reader = csv.reader(file)
for row in reader:
print(row)
Filtering rows based on conditions
import csv
with open('employee_records.csv', 'r') as file:
reader = csv.DictReader(file)
for row in reader:
if row['Department'] == 'Engineering':
print(row)
Sorting and grouping data
import csv
with open('employee_records.csv', 'r') as infile, open('sorted_employees.csv', 'w', newline='') as outfile:
reader = csv.DictReader(infile)
sorted_data = sorted(reader, key=lambda x: int(x['Salary']), reverse=True)
writer = csv.DictWriter(outfile, fieldnames=reader.fieldnames)
writer.writeheader()
writer.writerows(sorted_data)
Check for Missing Data
import csv
# Open the CSV file for reading
with open('employee_records.csv', 'r') as file:
reader = csv.reader(file)
# Track if any missing data is found
missing_data_found = False
# Check each row for missing data
# Iterates through the rows in the CSV file,
# starting the row numbering from 1 (useful for reporting row numbers).
for row_number, row in enumerate(reader, start=1):
# print (row_number)
if any(field.strip() == '' for field in row): # Check for empty or whitespace-only fields
#print (row_number)
print(f"Missing data found in row {row_number}: {row}")
missing_data_found = True
if not missing_data_found:
print("No missing data found in the CSV file.")