Tutorials | Python Tutorial

1. Syntax and Basic Constructs:

Python is a high-level, interpreted programming language known for its readability and ease of use. Here are some fundamental aspects:

1. Comments:

In Python, comments start with the # symbol. They are used to provide explanations within the code.

# This is a single-line comment

“””
This is a
multi-line comment
“””

2. Variables and Data Types:

Variables are used to store data. Python is dynamically typed, so you don’t need to declare the data type explicitly.


# Variable assignment
name = "John"
age = 25
height = 5.9
is_student = True

3. Print Statement:

The print statement is used to display output.

print("Hello, World!")
print("Name:", name, "Age:", age)

 

4. Indentation:

Python uses indentation to define blocks of code. Proper indentation is crucial for the code’s structure.

if age >= 18:
print("You are an adult.")
else:
print("You are a minor.")

 

5. Conditionals:

Python uses if, elif, and else for conditionals.


if condition:
# code to execute if condition is True
elif another_condition:
# code to execute if another_condition is True
else:
# code to execute if none of the conditions are True

6. Loops:

Python provides for and while loops.

# For loop
for i in range(5):
print(i)

# While loop
count = 0
while count < 5:
print(count)
count += 1

7. Functions:

Functions are defined using the def keyword.

def greet(name):
print(“Hello, ” + name + “!”)

# Function call
greet(“Alice”)

8. Lists:

Lists are mutable sequences.


fruits = ['apple', 'orange', 'banana']
print(fruits[0]) # Output: apple

9. Dictionaries:

Dictionaries are collections of key-value pairs.

person = {'name': 'John', 'age': 25, 'city': 'New York'}
print(person['name']) # Output: John

 

10. Classes and Objects:

Python is an object-oriented language.

python

class Dog:
def __init__(self, name, age):
self.name = name
self.age = age

def bark(self):
print(“Woof!”)

# Creating an instance of the Dog class
my_dog = Dog(“Buddy”, 3)
my_dog.bark()

11. Modules and Imports:

Python code can be organized into modules, and you can import functions and classes from other modules.

python

# Importing the math module
import math

# Using a function from the math module
result = math.sqrt(25)
print(result) # Output: 5.0

2. File Handling:

File handling in Python involves working with files, reading data from files, and writing data to files. Here are some basic file handling operations in Python:

Opening a File:

To open a file, you can use the open() function. The simplest form takes the file name and the mode (read, write, append, etc.).

# Open a file in read mode
file = open(“example.txt”, “r”)

# Open a file in write mode
file = open(“example.txt”, “w”)

# Open a file in append mode
file = open(“example.txt”, “a”)


Reading from a File:

You can read the contents of a file using various methods. The read() method reads the entire file, while readline() reads one line at a time.

# Reading the entire file
content = file.read()
print(content)

# Reading one line at a time
line = file.readline()
print(line)


You can also iterate over the file object to read line by line.

python
# Reading lines using a loop

for line in file:
print(line)

Writing to a File:

To write data to a file, you can use the write() method.

# Writing to a file
file.write("Hello, World!\n")
file.write("Python is awesome.")

Closing a File:

It’s important to close a file after working with it using the close() method. This ensures that the resources associated with the file are properly released.

python
file.close()

Using “with” Statement:

The with statement is a cleaner way to open and close files. It automatically takes care of closing the file, even if an exception occurs.

with open("example.txt", "r") as file:
content = file.read()
print(content)
# File is automatically closed outside the "with" block

Appending to a File:

If you want to add content to the end of an existing file without overwriting its current content, open it in append mode.

with open("example.txt", "a") as file:
file.write("Appending new content.")

Checking if a File Exists:

You can use the os.path module to check if a file exists before attempting to open it.

import os

file_path = “example.txt”

if os.path.exists(file_path):
with open(file_path, “r”) as file:
content = file.read()
print(content)
else:
print(“File does not exist.”)


These are basic file handling operations in Python. For more advanced file manipulation or working with different file formats, Python provides additional libraries like csv, json, pickle, etc., depending on your specific requirements.

3. Object-Oriented Programming (OOP):

Object-Oriented Programming (OOP) is a programming paradigm that uses objects and classes to structure code. It is based on the concept of “objects,” which can contain data in the form of fields (often known as attributes or properties), and code in the form of procedures (often known as methods). OOP promotes the organization of code into reusable and modular components.

Here are the key principles of Object-Oriented Programming:

1. Classes and Objects:

  • Class: A class is a blueprint or template for creating objects. It defines the properties and behaviors that its objects will have.

    python
    class Dog:
    def __init__(self, name, age):
    self.name = name
    self.age = age
    def bark(self):
    print("Woof!")
    # Creating an object (instance) of the Dog class
    my_dog = Dog("Buddy", 3)
  • Object: An object is an instance of a class. It represents a real-world entity and has its own unique state and behavior.

    python
    print(my_dog.name) # Output: Buddy
    my_dog.bark() # Output: Woof!

2. Encapsulation:

Encapsulation is the bundling of data (attributes) and the methods that operate on the data within a single unit or class. It hides the internal details of the object and only exposes what is necessary.

python

class Car:
def __init__(self, make, model):
self.make = make
self.model = model
self.__fuel = 100 # Private attribute

def drive(self):
print(“Vroom!”)

def get_fuel_level(self):
return self.__fuel

my_car = Car(“Toyota”, “Camry”)
my_car.drive()
print(my_car.get_fuel_level()) # Accessing private attribute using a method

3. Inheritance:

Inheritance allows a class (subclass or derived class) to inherit the properties and methods of another class (superclass or base class). It promotes code reuse and the creation of a hierarchy of classes.

python

class Animal:
def __init__(self, name):
self.name = name

def speak(self):
pass # Abstract method

class Dog(Animal):
def speak(self):
return “Woof!”

class Cat(Animal):
def speak(self):
return “Meow!”

my_dog = Dog(“Buddy”)
my_cat = Cat(“Whiskers”)

print(my_dog.speak()) # Output: Woof!
print(my_cat.speak()) # Output: Meow!

4. Polymorphism:

Polymorphism allows objects of different types to be treated as objects of a common type. It enables a single interface to represent different types of objects.

python

def animal_sound(animal):
return animal.speak()

print(animal_sound(my_dog)) # Output: Woof!
print(animal_sound(my_cat)) # Output: Meow!

5. Abstraction:

Abstraction involves simplifying complex systems by modeling classes based on the essential properties and behaviors they share. It allows you to focus on what an object does rather than how it achieves its functionality.

python

from abc import ABC, abstractmethod

class Shape(ABC):
@abstractmethod
def area(self):
pass

class Circle(Shape):
def __init__(self, radius):
self.radius = radius

def area(self):
return 3.14 * self.radius ** 2

class Square(Shape):
def __init__(self, side):
self.side = side

def area(self):
return self.side ** 2

my_circle = Circle(5)
my_square = Square(4)

print(my_circle.area()) # Output: 78.5
print(my_square.area()) # Output: 16

Object-Oriented Programming provides a powerful way to structure and organize code, making it more maintainable, scalable, and modular. It is widely used in software development to model real-world entities and their interactions.

4. Modules and Packages:

In Python, modules and packages are mechanisms for organizing code into reusable and maintainable components. They help manage the complexity of larger projects and promote code reuse. Here’s an overview of modules and packages:

Modules:

A module is a single Python file that contains code. It can define functions, classes, and variables that can be reused in other Python scripts.

Creating a Module:

Suppose you have a file named my_module.py:

python
# my_module.py
def greet(name):
return f"Hello, {name}!"
def square(x):
return x ** 2

Using a Module:

You can use the functions from the module in another script by importing it.

python

# main.py

import my_module

print(my_module.greet(“Alice”)) # Output: Hello, Alice!
print(my_module.square(3)) # Output: 9

Packages:

A package is a way of organizing related modules into a directory hierarchy. It contains a special file named __init__.py to indicate that the directory should be treated as a package.

Creating a Package:

Suppose you have a package structure like this:

lua
my_package/
|-- __init__.py
|-- module1.py
|-- module2.py

The __init__.py file can be empty or contain package-level initialization code.

Using a Package:

You can use modules from a package in a similar way to standalone modules.

python

# main.py

from my_package import module1, module2

print(module1.function1()) # Output: Function 1 from module1
print(module2.function2()) # Output: Function 2 from module2

Subpackages:

Packages can also contain subpackages, forming a nested hierarchy.

lua
my_package/
|– __init__.py
|– module1.py
|– module2.py
|– subpackage1/
| |– __init__.py
| |– module3.py
|– subpackage2/
|– __init__.py
|– module4.py

Using a Subpackage:

python

# main.py

from my_package.subpackage1 import module3
from my_package.subpackage2.module4 import function4

print(module3.function3()) # Output: Function 3 from module3
print(function4()) # Output: Function 4 from module4

Namespace:

Modules and packages introduce a namespace, preventing naming conflicts. For example, if two modules have a function with the same name, you can differentiate them using the module name.

python

# main.py

from my_module import greet as module_greet
from my_package.module1 import function1 as package_function1

print(module_greet(“Bob”)) # Output: Hello, Bob!
print(package_function1()) # Output: Function 1 from module1

Importing Everything:

While it’s generally recommended to import only what you need, you can import everything from a module or package using from module import *. However, this is often discouraged due to potential namespace pollution.

python

# main.py

from my_module import *
from my_package.module1 import *

print(greet(“Charlie”)) # Output: Hello, Charlie!
print(function1()) # Output: Function 1 from module1

In summary, modules and packages in Python provide a way to organize and structure code, making it more modular and maintainable. They help avoid naming conflicts, promote code reuse, and facilitate the creation of scalable and well-organized projects.

5. Working with Libraries (e.g., NumPy, Pandas):

Working with libraries in Python, such as NumPy and Pandas, can significantly enhance your ability to manipulate data and perform scientific computing. Here’s an overview of working with these two popular libraries:

NumPy:

NumPy is a powerful library for numerical operations in Python. It provides support for large, multi-dimensional arrays and matrices, along with a collection of mathematical functions to operate on these arrays.

Installing NumPy:

bash
pip install numpy

Importing NumPy:

python
import numpy as np

Creating NumPy Arrays:

python
# Creating a 1D array
arr_1d = np.array([1, 2, 3])
# Creating a 2D array (matrix)
arr_2d = np.array([[1, 2, 3], [4, 5, 6]])
# Creating an array with a range of values
arr_range = np.arange(0, 10, 2)# Start from 0, end at 10 (exclusive), step by 2
# Creating an array with evenly spaced values
arr_linspace = np.linspace(0, 1, 5) # Start from 0, end at 1, with 5 evenly spaced values

Basic Operations:

python
# Array addition, subtraction, multiplication, and division
result_add = arr_1d + arr_1d
result_sub = arr_1d - arr_1d
result_mul = arr_1d * arr_1d
result_div = arr_1d / arr_1d
# Element-wise operations
result_sqrt = np.sqrt(arr_1d)
result_sin = np.sin(arr_1d)

Indexing and Slicing:

python
# Accessing elements
element = arr_1d[0]
# Slicing
subset = arr_1d[1:3] # Elements from index 1 to 2 (3-1)
# Indexing in 2D array
element_2d = arr_2d[1, 2] # Row 1, Column 2

Pandas:

Pandas is a powerful data manipulation library built on top of NumPy. It provides data structures like Series and DataFrame for efficient data manipulation and analysis.

Installing Pandas:

bash
pip install pandas

Importing Pandas:

python
import pandas as pd

Creating Pandas Series and DataFrame:

python
# Creating a Pandas Series
s = pd.Series([1, 3, 5, np.nan, 6, 8])
# Creating a Pandas DataFrame from a NumPy array
df = pd.DataFrame(np.random.randn(6, 4), columns=list('ABCD'))

Loading and Saving Data:

python
# Reading data from a CSV file into a DataFrame
df_csv = pd.read_csv('example.csv')
# Writing DataFrame to a CSV file
df_csv.to_csv('output.csv', index=False)

Basic DataFrame Operations:

python
# Displaying the first few rows of a DataFrame
print(df.head())
# Descriptive statistics
print(df.describe()) # Transposing a DataFrame
df_transposed = df.T

Indexing and Selection:

python
# Selecting a column
column_a = df['A']
# Selecting multiple columns
subset_df = df[['A', 'C']]
# Selecting rows based on a condition
filtered_df = df[df['B'] > 0]
# Accessing a specific element
element_df = df.at[0, 'A']

6. Working with APIs:

Working with APIs (Application Programming Interfaces) in Python allows you to interact with external services and retrieve or send data. Here’s a basic overview of how to work with APIs using the requests library in Python.

Installing requests:

bash
pip install requests

Making GET Requests:

The requests library makes it easy to send HTTP requests and handle responses.

python

import requests

# Example: Making a GET request to a public API
response = requests.get(“https://jsonplaceholder.typicode.com/todos/1”)

# Checking if the request was successful (status code 200)
if response.status_code == 200:
# Accessing the response data (in JSON format for this example)
data = response.json()
print(data)
else:
print(“Error:”, response.status_code)

Query Parameters:

You can include query parameters in your GET request.

python

import requests

# Example: Making a GET request with query parameters
url = “https://api.example.com/data”
params = {“param1”: “value1”, “param2”: “value2”}
response = requests.get(url, params=params)

if response.status_code == 200:
data = response.json()
print(data)
else:
print(“Error:”, response.status_code)

Making POST Requests:

To send data to an API using a POST request:

python

import requests

# Example: Making a POST request with data
url = “https://api.example.com/post_endpoint”
data = {“key1”: “value1”, “key2”: “value2”}
response = requests.post(url, json=data)

if response.status_code == 200:
result = response.json()
print(result)
else:
print(“Error:”, response.status_code)

Handling Authentication:

If the API requires authentication, you can provide credentials using the auth parameter.

python

import requests

# Example: Making a GET request with authentication
url = “https://api.example.com/authenticated_data”
auth = (“username”, “password”)
response = requests.get(url, auth=auth)

if response.status_code == 200:
data = response.json()
print(data)
else:
print(“Error:”, response.status_code)

Error Handling:

Always check the status code to handle errors appropriately.

python

import requests

response = requests.get(“https://api.example.com/some_endpoint”)

if response.status_code == 200:
data = response.json()
print(data)
else:
print(“Error:”, response.status_code)

Rate Limiting and Pagination:

Some APIs may have rate limits or paginated responses. Be sure to check the API documentation for any specific guidelines.

python

import requests

# Example: Handling paginated responses
url = “https://api.example.com/paginated_data”
page = 1

while True:
response = requests.get(url, params={“page”: page})

if response.status_code == 200:
data = response.json()

if not data:
break # No more data, exit the loop

# Process the data
print(data)

page += 1 # Move to the next page
else:
print(“Error:”, response.status_code)
break

Always refer to the API documentation for specific details on how to interact with a particular API, including required headers, authentication methods, and available endpoints.

7. String Manipulation:

String manipulation is a fundamental aspect of programming, and Python provides a rich set of tools for working with strings. Here are some common string manipulation operations in Python:

1. Concatenation:

Joining two or more strings together.

python
str1 = "Hello"
str2 = "World"

result = str1 + ", " + str2
print(result)
# Output: Hello, World

2. String Formatting:

Creating formatted strings using different methods.

Using % formatting:

python
name = "Alice"
age = 25

formatted_str = "My name is %s and I am %d years old." % (name, age)
print(formatted_str)
# Output: My name is Alice and I am 25 years old.

Using format() method:

python
formatted_str = "My name is {} and I am {} years old.".format(name, age)
print(formatted_str)
# Output: My name is Alice and I am 25 years old.

Using f-strings (Python 3.6 and later):

python
formatted_str = f"My name is {name} and I am {age} years old."
print(formatted_str)
# Output: My name is Alice and I am 25 years old.

3. String Methods:

Python provides various methods for string manipulation, such as upper(), lower(), strip(), replace(), find(), and more.

python
sentence = " Python is fun! "
uppercase_sentence = sentence.upper()
lowercase_sentence = sentence.lower()
stripped_sentence = sentence.strip()
replaced_sentence = sentence.replace("fun", "awesome")
index_of_python = sentence.find("Python")
print(uppercase_sentence)
print(lowercase_sentence)
print(stripped_sentence)
print(replaced_sentence)
print(index_of_python)

4. String Slicing:

Extracting a portion of a string.

python
text = "Python Programming"
substring1 = text[0:6] # Extract characters from index 0 to 5

substring2 = text[7:] # Extract characters from index 7 to the end
substring3 = text[-11:-1] # Extract characters from index -11 to -2
print(substring1)
print(substring2)
print(substring3)

5. Splitting and Joining:

Splitting a string into a list of substrings and joining them back.

python
sentence = "Python is a powerful programming language"
words = sentence.split() # Split the sentence into a list of words

joined_sentence = "-".join(words) # Join the words using a hyphen

print(words)

print(joined_sentence)

6. Checking Substrings:

Checking if a substring exists in a string.

python
sentence = "Python is easy and powerful"

contains_python = "Python" in sentence
contains_java = "Java" in sentence
print(contains_python)
print(contains_java)

7. String Length:

Getting the length of a string using the len() function.

python
sentence = "This is a long sentence."
length = len(sentence)
print(length)

8. List Comprehensions:

List comprehensions provide a concise way to create lists in Python. They allow you to create a new list by specifying the elements you want to include, the conditions they must satisfy, and any operations to perform on each element. Here’s an overview of list comprehensions:

Basic List Comprehension:

The basic syntax of a list comprehension is:

python
new_list = [expression for item in iterable if condition]

  • expression: The expression to evaluate and include in the new list.
  • item: The variable representing each element in the iterable.
  • iterable: The source of elements (e.g., a list, tuple, or range).
  • condition (optional): A condition that determines whether to include the element.

Example 1:

Creating a list of squares of numbers from 0 to 9.

python
squares = [x**2 for x in range(10)]
print(squares)
# Output: [0, 1, 4, 9, 16, 25, 36, 49, 64, 81]

Example 2:

Filtering out even numbers from a list.

python
numbers = [1, 2, 3, 4, 5, 6, 7, 8, 9]
odd_numbers = [x for x in numbers if x % 2 != 0]
print(odd_numbers)
# Output: [1, 3, 5, 7, 9]

Nested List Comprehension:

You can also use nested list comprehensions to create more complex structures.

Example 3:

Creating a matrix (2D list) using nested comprehensions.

python
matrix = [[i * j for j in range(3)] for i in range(4)]
print(matrix)
# Output: [[0, 0, 0], [0, 1, 2], [0, 2, 4], [0, 3, 6]]

Conditionals in List Comprehension:

You can use conditionals for more complex filtering or transformation.

Example 4:

Creating a list of even squares.

python
even_squares = [x**2 for x in range(10) if x % 2 == 0]
print(even_squares)
# Output: [0, 4, 16, 36, 64]

Using if-else in List Comprehension:

You can also include an if-else expression within a list comprehension.

Example 5:

Creating a list of even/odd labels.

python
labels = ["Even" if x % 2 == 0 else "Odd" for x in range(5)]
print(labels)
# Output: ['Even', 'Odd', 'Even', 'Odd', 'Even']

List comprehensions provide a concise and readable way to create lists in Python. However, it’s important to use them judiciously to maintain code readability. In some cases, a regular loop may be more appropriate, especially for complex operations or when the logic becomes hard to understand within a list comprehension.

9. Dictionaries and Sets:

Dictionaries and sets are two important data structures in Python that allow you to store and manipulate collections of data.

Dictionaries:

A dictionary is an unordered collection of key-value pairs. Each key in a dictionary must be unique, and the keys are associated with values.

Creating a Dictionary:

python
# Using curly braces
my_dict = {'name': 'John', 'age': 25, 'city': 'New York'} # Using the dict() constructor
another_dict = dict(name='Alice', age=30, city='London')

Accessing Values:

python
# Accessing values using keys
name = my_dict['name']
age = my_dict.get('age')

Modifying and Adding Elements:

python
# Modifying a value
my_dict['age'] = 26
# Adding a new key-value pair
my_dict['gender'] = 'Male'

Dictionary Methods:

python
# Getting all keys and values
keys = my_dict.keys()
values = my_dict.values() # Checking if a key is present
is_name_present = 'name' in my_dict
# Removing a key-value pair
removed_value = my_dict.pop('city')

Sets:

A set is an unordered collection of unique elements. Sets are useful for tasks that involve testing membership and eliminating duplicate entries.

Creating a Set:

python
# Using curly braces
my_set = {1, 2, 3, 4, 5}
# Using the set() constructor
another_set = set([3, 4, 5, 6, 7])

Set Operations:

python
# Adding elements to a set
my_set.add(6)
# Removing an element from a set
my_set.remove(3)
# Set union, intersection, and difference
set_union = my_set.union(another_set)
set_intersection = my_set.intersection(another_set)
set_difference = my_set - another_set

Set Methods:

python
# Checking if an element is in a set
is_present = 4 in my_set
# Getting the length of a set
set_length = len(my_set)
# Clearing all elements from a set my_set.clear()

Dict Comprehensions and Set Comprehensions:

Similar to list comprehensions, you can use dict comprehensions and set comprehensions to create dictionaries and sets in a concise manner.

Dict Comprehension:

python
my_dict = {x: x**2 for x in range(5)}
# Output: {0: 0, 1: 1, 2: 4, 3: 9, 4: 16}

Set Comprehension:

python
my_set = {x for x in range(5)}
# Output: {0, 1, 2, 3, 4}

Dictionaries and sets are versatile data structures that can be used in various scenarios, such as representing mappings, counting occurrences, and handling unique elements. Understanding their properties and methods is essential for efficient Python programming.

10. Regular Expressions:

Regular expressions (regex or regexp) are powerful tools for pattern matching and text manipulation. They provide a concise and flexible way to search, match, and extract information from strings in Python.

Basics of Regular Expressions:

In Python, the re module provides support for regular expressions.

Example 1: Matching a Simple Pattern

python
import re
pattern = r"apple"
text = "I love apples and oranges."

match = re.search(pattern, text)

if match:
print("Found:", match.group())
else:
print("Not found")

Example 2: Using Character Classes

python
import re
pattern = r"[aeiou]"
text = "Hello, World!"
matches = re.findall(pattern, text)
print("Vowels:", matches)

Common Patterns:

  • . (Dot): Matches any character except a newline.
  • ^ (Caret): Matches the start of a string.
  • $ (Dollar): Matches the end of a string.
  • [] (Character Class): Matches any one of the enclosed characters.
  • [^] (Negated Character Class): Matches any character that is not in the enclosed characters.
  • * (Star): Matches 0 or more occurrences of the preceding character.
  • + (Plus): Matches 1 or more occurrences of the preceding character.
  • ? (Question Mark): Matches 0 or 1 occurrence of the preceding character.
  • {n}: Matches exactly n occurrences of the preceding character.
  • {n,}: Matches n or more occurrences of the preceding character.
  • {n, m}: Matches between n and m occurrences of the preceding character.
  • \ (Backslash): Escapes a special character, allowing you to match it literally.

Using Regular Expressions in Python:

Example 3: Matching Email Addresses

python
import re
pattern = r"[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,}"
text = "Contact us at support@example.com or info@company.com"
emails = re.findall(pattern, text)
print("Emails:", emails)

Example 4: Extracting Phone Numbers

python

import re

pattern = r”\b\d{3}[-.\s]?\d{3}[-.\s]?\d{4}\b”
text = “Contact us at 555-123-4567 or 123.456.7890”

phone_numbers = re.findall(pattern, text)
print(“Phone Numbers:”, phone_numbers)

Example 5: Substituting Text

python
import re
pattern = r"\b(\w+)-(\d+)\b"
text = "apple-123, banana-456, cherry-789"

replaced_text = re.sub(pattern, r”\2-\1″, text)

print("Replaced Text:", replaced_text)

Regular expressions are a powerful tool, but they can also be complex. It’s important to understand the basics and gradually build on that knowledge. The Python re module documentation is a valuable resource for learning more about regular expressions: Python re module documentation.

11. Testing in Python:

Testing is a crucial aspect of software development to ensure that code behaves as expected and remains correct as it evolves. In Python, the standard library includes a module called unittest for creating and running tests. Additionally, there are other third-party testing frameworks such as pytest that offer additional features and a more concise syntax. Here, we’ll focus on the built-in unittest module.

unittest Basics:

Writing a Simple Test Case:

python
import unittest
def add(a, b):
return a + b
class TestAddFunction(unittest.TestCase):
def test_add_positive_numbers(self):
self.assertEqual(add(2, 3), 5)
def test_add_negative_numbers(self):
self.assertEqual(add(-2, -3), -5)
if __name__ == '__main__':
unittest.main()

Running Tests:

Save the test file (e.g., test_module.py) and run:

bash
python test_module.py

Common Assertions:

  • assertEqual(a, b): Tests if a is equal to b.
  • assertNotEqual(a, b): Tests if a is not equal to b.
  • assertTrue(x): Tests if x is True.
  • assertFalse(x): Tests if x is False.
  • assertIsNone(x): Tests if x is None.
  • assertIsNotNone(x): Tests if x is not None.
  • assertRaises(exception, callable, *args, **kwargs): Tests if callable raises exception.

pytest:

pytest is a third-party testing framework that simplifies test creation and provides additional features.

Writing the Same Test with pytest:

python
def test_add_positive_numbers():
assert add(2, 3) == 5

def test_add_negative_numbers():

assert add(-2, -3) == -5

Running Tests with pytest:

Save the test file (e.g., test_module.py) and run:

bash
pytest test_module.py

Test Discovery:

Both unittest and pytest support test discovery, which allows you to organize tests in a specific directory structure, and the test runner will find and execute them.

Directory Structure for Test Discovery:

lua
project/
|-- main_module.py

|-- test/
| |-- __init__.py
| |-- test_main_module.py

Run tests from the project root:

bash
python -m unittest discover -s test
# or
pytest test

Mocking and Test Doubles:

When testing, it’s common to use mock objects or test doubles to isolate the code under test. The unittest.mock module in the standard library provides facilities for creating mock objects.

Continuous Integration (CI):

Integrating tests into a CI system, such as Travis CI or GitHub Actions, helps ensure that tests are automatically run whenever changes are pushed to the repository.

Example .travis.yml for Travis CI:

yaml
language: python
python:
– “3.7”
– “3.8”
– “3.9”
install:
– pip install -r requirements.txt
script:
– python -m unittest discover -s test

This basic setup assumes that your tests are in a test directory, and dependencies are listed in a requirements.txt file.

12. Virtual Environments:

Virtual environments are isolated environments for Python projects that allow you to manage dependencies and avoid conflicts between different projects. They are a recommended practice to ensure that your project’s dependencies are isolated from the global Python environment. Python provides the built-in venv module for creating virtual environments.

Creating a Virtual Environment:

  1. Open a terminal or command prompt.

  2. Navigate to your project directory.

  3. Run the following command to create a virtual environment:

    For Unix/macOS:

    bash
    python3 -m venv venv

    For Windows:

    bash
    python -m venv venv

    Here, venv is the name of the virtual environment. You can choose a different name if you prefer.

Activating the Virtual Environment:

  1. Activate the virtual environment:

    For Unix/macOS:

    bash
    source venv/bin/activate

    For Windows:

    bash
    venv\Scripts\activate

    After activation, your terminal prompt should change, indicating that the virtual environment is active.

Installing Dependencies:

While the virtual environment is active, you can use pip to install packages, and they will be isolated to the virtual environment.

bash
pip install package_name

Deactivating the Virtual Environment:

When you’re done working on your project, you can deactivate the virtual environment.

bash
deactivate

Using requirements.txt:

You can create a requirements.txt file to list your project’s dependencies. This file makes it easy for others to recreate the virtual environment.

bash
pip freeze > requirements.txt

To install dependencies from a requirements.txt file:

bash
pip install -r requirements.txt

Example Workflow:

bash
# Create virtual environment
python -m venv venv
# Activate virtual environment

source venv/bin/activate # On Unix/macOS

venv\Scripts\activate # On Windows

# Install dependencies

pip install package_name
# Save dependencies to requirements.txt
pip freeze > requirements.txt
# Deactivate virtual environment
deactivate

venv vs. virtualenv:

venv is the built-in module for creating virtual environments in Python 3.3 and later. For earlier versions, you might use the third-party virtualenv package. However, venv is generally recommended for Python 3.3 and later, as it provides improvements over virtualenv.

pipenv and poetry:

For more advanced dependency management and workflow, you may consider using tools like pipenv or poetry. They provide additional features such as handling virtual environments, dependencies, and scripts in a more integrated way.

The choice of tools depends on your project’s needs and your preferences.

13. PEP 8 conventions and Pythonic coding style

PEP 8 (Python Enhancement Proposal 8) is the official style guide for Python code. It provides conventions for writing readable and maintainable code. Following PEP 8 is a good practice as it helps create a consistent and Pythonic coding style across different projects. Here are some key conventions and Pythonic coding principles outlined in PEP 8:

Indentation:

  • Use 4 spaces per indentation level.
  • Prefer spaces over tabs.
python

# Good
def my_function():
if x > 0:
print(“Positive”)
else:
print(“Non-positive”)

# Bad (using tabs)
def my_function():
if x > 0:
print(“Positive”)
else:
print(“Non-positive”)

Maximum Line Length:

  • Limit all lines to a maximum of 79 characters for code and 72 for docstrings.
  • Use parentheses for line continuation.
python

# Good
def long_function_name(
arg1, arg2, arg3,
arg4, arg5):
# function body

# Bad
def long_function_name(arg1, arg2, arg3, arg4, arg5): # line is too long
# function body

Imports:

  • Imports should usually be on separate lines and should be grouped in the following order:
    1. Standard library imports.
    2. Related third-party imports.
    3. Local application/library specific imports.
python

# Good
import os
import sys

from math import sqrt

import my_local_module

# Bad (unorganized imports)
import my_local_module, sys, os
from math import sqrt

Blank Lines:

  • Surround top-level function and class definitions with two blank lines.
  • Method definitions inside a class should be separated by a single blank line.
python

# Good
def function1():
pass


def function2():
pass


class MyClass:

def method1(self):
pass

def method2(self):
pass

# Bad (missing blank lines)
def function1():
pass
def function2():
pass

class MyClass:
def method1(self):
pass
def method2(self):
pass

Naming Conventions:

  • Use lowercase with underscores for function and variable names.
  • Use CapWords (or CamelCase) for class names.
  • Avoid single-character names except for temporary variables.
  • Use descriptive names, and don’t use l, O, or I as single-character variable names.
python

# Good
def calculate_average(numbers):
total_sum = 0
for num in numbers:
total_sum += num
return total_sum / len(numbers)

class MyClass:
def __init__(self, name, age):
self.name = name
self.age = age

# Bad (non-descriptive names)
def avg(nums):
s = 0
for n in nums:
s += n
return s / len(nums)

class A:
def __init__(self, n, a):
self.n = n
self.a = a

These are just a few examples of PEP 8 conventions and Pythonic coding style. It’s recommended to read the complete PEP 8 document for a comprehensive understanding of the guidelines: PEP 8 — Style Guide for Python Code.

Adhering to PEP 8 conventions makes your code more readable and consistent, and it helps maintain a standard style across different Python projects. Many code editors and IDEs have built-in tools or plugins that can automatically check and format your code according to PEP 8 guidelines.