Fundamental of Python

What is Python?

Python is a high-level, general-purpose programming language known for its simplicity, readability, and versatility. It’s widely used in various fields, including web development, data science, machine learning, automation, and scientific computing.

History and Evolution of Python

  • Creation: Python was created by Guido van Rossum in the late 1980s.
  • Release: The first version of Python was released in 1991.
  • Growth: Python’s popularity has grown significantly over the years, thanks to its user-friendly syntax and extensive libraries.
  • Community: A large and active community of developers contributes to Python’s development and supports its users.

Key Features of Python

  • Readability: Python’s code is often described as “pseudocode-like,” making it easy to read and understand.
  • Simplicity: Python’s syntax is clean and concise, reducing the amount of code needed to accomplish tasks.
  • Versatility: Python can be used for a wide range of applications, from simple scripts to complex data analysis projects.
  • Cross-platform compatibility: Python runs on various operating systems, including Windows, macOS, and Linux.
  • Extensive libraries: Python comes with a rich standard library and a vast ecosystem of third-party libraries for tasks like web development, data science, machine learning, and more.

Applications of Python

  • Web development: Python frameworks like Django and Flask are popular for building web applications.
  • Data science: Python’s libraries like NumPy, Pandas, and Matplotlib are essential for data analysis, manipulation, and visualization.
  • Machine learning: Python’s libraries like TensorFlow and PyTorch are widely used for building machine learning models.
  • Automation: Python can be used to automate repetitive tasks and streamline workflows.
  • Scientific computing: Python’s libraries like SciPy and SymPy are used for scientific calculations and simulations.

Setting Up Python Environment

Installing Python

  1. Visit the official Python website (https://www.python.org/downloads/).
  2. Download the appropriate installer for your operating system.   
  3. Follow the installation instructions.

Numbers

Python supports three numerical data types:

  • Integers: Whole numbers without decimal points (e.g., 10, -5, 0).
  • Floating-point numbers: Numbers with decimal points (e.g., 3.14, -2.5).
  • Complex numbers: Numbers with a real and an imaginary part (e.g., 2+3j).
Eg:
# Examples of different number types
integer_number = 10
floating_point_number = 3.14
complex_number = 2+3j

print(integer_number)
print(floating_point_number)
print(complex_number)

Strings

Strings are sequences of characters enclosed in single or double quotes.

Eg:
string1 = "Hello, world!"
string2 = 'This is a string.'

print(string1)
print(string2)

Boolean Values

Boolean values represent True or False. They are often used in conditional statements.

Eg:
is_true = True
is_false = False

print(is_true)
print(is_false)

Variables and Assignment

Variables are used to store data. You can assign values to variables using the = operator.

Eg:
x = 10
name = "Alice"
is_student = True

print(x)
print(name)
print(is_student)

Type Conversion

You can convert values from one data type to another using built-in functions:

  • int(): Converts to an integer.
  • float(): Converts to a floating-point number.
  • str(): Converts to a string.
  • bool(): Converts to a boolean value.   
Eg:
number = 10.5
string_number = str(number)
integer_number = int(number)

print(string_number)
print(integer_number)

Remember: When converting between data types, be mindful of potential data loss or rounding errors.

In Python, operators are symbols used to perform operations on values (operands). Expressions combine variables, values, and operators to compute a result. This chapter explores various operators and how they work.

Arithmetic Operators

  • Addition (+): Adds two operands.
  • Subtraction (-): Subtracts the second operand from the first.
  • Multiplication (*): Multiplies two operands.
  • Division (/): Divides the first operand by the second (results in a float).   
  • Floor division (//): Divides the first operand by the second and returns the whole number result (integer).
  • Modulo (%): Returns the remainder after division.
  • Exponentiation (**): Raises the first operand to the power of the second.
Python
x = 5
y = 3

# Examples of arithmetic operators
result_add = x + y
result_sub = x - y
result_mul = x * y
result_div = x / y
result_floor_div = x // y
result_mod = x % y
result_exp = x ** y

print(result_add)  # Output: 8
print(result_sub)  # Output: 2
print(result_mul)  # Output: 15
print(result_div)  # Output: 1.6666666666666667 (float)
print(result_floor_div)  # Output: 1 (integer quotient)
print(result_mod)  # Output: 2 (remainder)
print(result_exp)  # Output: 125 (5 raised to the power of 3)

Comparison Operators

  • Equal (==): Checks if two operands are equal.
  • Not equal (!=): Checks if two operands are not equal.
  • Greater than (>): Checks if the left operand is greater than the right operand.   
  • Less than (<): Checks if the left operand is less than the right operand.
  • Greater than or equal to (>=): Checks if the left operand is greater than or equal to the right operand.
  • Less than or equal to (<=): Checks if the left operand is less than or equal to the right operand.   
Python
x = 10
y = 5

# Examples of comparison operators
result_equal = x == y
result_not_equal = x != y
result_greater = x > y
result_less = x < y
result_greater_equal = x >= y
result_less_equal = x <= y

print(result_equal)  # Output: False
print(result_not_equal)  # Output: True
print(result_greater)  # Output: True
print(result_less)  # Output: False
print(result_greater_equal)  # Output: True
print(result_less_equal)  # Output: False

Logical Operators

  • And (and): Returns True only if both operands are True.
  • Or (or): Returns True if at least one operand is True.
  • Not (not): Inverts the logical value of the operand.
Python
x = True
y = False

# Examples of logical operators
result_and = x and y
result_or = x or y
result_not = not x

print(result_and)  # Output: False (both operands need to be True)
print(result_or)  # Output: True (at least one operand is True)
print(result_not)  # Output: False (inverts True to False)

Note: Logical operators follow short-circuit evaluation. In and, if the first operand is False, the second operand is not evaluated. Similarly, for or, if the first operand is True, the second operand is skipped.

Bitwise Operators

Bitwise operators work on the binary representation of numbers. Here are some common examples:

  • Bitwise AND (&): Performs a bitwise AND operation on each bit of the operands.
  • Bitwise OR (|): Performs a bitwise OR operation on each bit of the operands.
  • Bitwise XOR (^): Performs a bitwise XOR operation on each bit of the operands.
  • Left shift (<<): Shifts the bits of the left operand to the left by the number of bits specified by the right operand.
  • Right shift (>>): Shifts the bits

Control flow statements are used to alter the normal sequential execution of a program. They allow you to make decisions and repeat code blocks based on certain conditions.

Conditional Statements

if Statement:

  • Executes a block of code if a condition is True.
Python
x = 10

if x > 0:
    print("x is positive")

if-else Statement:

  • Executes one block of code if a condition is True, and another block if it’s False.
Python
x = -5

if x > 0:
    print("x is positive")
else:
    print("x is negative")

if-elif-else Statement:

  • Provides multiple conditions to check.
Python
x = 0

if x > 0:
    print("x is positive")
elif x < 0:
    print("x is negative")
else:
    print("x is zero")

Looping Statements

for Loop:

  • Iterates over a sequence (like a list, tuple, or string).
Python
numbers = [1, 2, 3, 4, 5]

for number in numbers:
    print(number)

while Loop:

  • Repeats a block of code as long as a condition is True.
Python
count = 0

while count < 5:
    print(count)
    count += 1

break and continue Statements

  • break: Exits the loop immediately.
  • continue: Skips the current iteration and continues with the next one.
Python
numbers = [1, 2, 3, 4, 5]

for number in numbers:
    if number == 3:
        break
    print(number)

for number in numbers:
    if number % 2 == 0:
        continue
    print(number)

Nested Loops

  • Loops within loops.
Python
for row in range(3):
    for col in range(4):
        print(row, col)

Example:

Python
for i in range(1, 11):
    for j in range(1, 11):
        product = i * j
        print(f"{i} x {j} = {product}")
This will print the multiplication table from 1 to 10.

Defining Functions

Functions are reusable blocks of code that perform specific tasks. They help to organize your code, make it more modular, and improve readability.

Python
def greet(name):
    print("Hello, " + name + "!")

greet("Alice")

In this example, greet is the function name, and name is a parameter. When you call the function, you provide an argument ("Alice") that is passed to the parameter.

Function Parameters and Arguments

  • Parameters: Variables defined within parentheses when a function is defined.
  • Arguments: Values passed to the function when it is called.
Python
def add(x, y):
    return x + y

result = add(3, 5)
print(result)  # Output: 8

Function Return Values

Functions can return values using the return statement. This allows you to use the result of the function in other parts of your code.

Python
def calculate_area(length, width):
    area = length * width
    return area

area_of_rectangle = calculate_area(5, 3)
print(area_of_rectangle)  # Output: 15

Scope of Variables

The scope of a variable determines where it can be accessed.

  • Global variables: Defined outside of functions and can be accessed anywhere in the program.
  • Local variables: Defined inside functions and can only be accessed within that function.
Python
global_variable = 10

def my_function():
    local_variable = 20
    print(global_variable)  # Accessing global variable
    print(local_variable)  # Accessing local variable

my_function()

Recursive Functions

A recursive function is a function that calls itself. They are often used to solve problems that can be broken down into smaller, similar subproblems.

Python

def factorial(n):
if n == 0:
return 1
else:
return n * factorial(n – 1)

result = factorial(5)
print(result) # Output: 120

In this example, the factorial function calculates the factorial of a number using recursion. If the number is 0, it returns 1. Otherwise, it calculates the factorial of n-1 and multiplies it by n.   

 

Lists are ordered collections of items. They can contain elements of different data types.

Creating Lists

my_list = [1, 2, 3, “hello”, True]
empty_list = []

Accessing Elements

You can access elements in a list using their index, starting from 0.

my_list = [10, 20, 30]
first_element = my_list[0] # Output: 10
second_element = my_list[1] # Output: 20

Slicing Lists

You can extract a portion of a list using slicing.

my_list = [1, 2, 3, 4, 5]
sublist = my_list[1:4] # Output: [2, 3, 4]

Modifying Lists

You can add, remove, or modify elements in a list.

my_list = [10, 20, 30]
my_list.append(40) # Add an element
my_list.insert(1, 50) # Insert an element at a specific index
my_list.remove(20) # Remove an element by value
del my_list[0] # Remove an element by index
my_list[1] = 60 # Modify an element

List Methods

Lists have many built-in methods that can be used to perform various operations.

my_list = [1, 2, 3, 2, 4]
length = len(my_list) # Get the length of the list
count = my_list.count(2) # Count the occurrences of an element
index = my_list.index(3) # Find the index of an element
my_list.sort() # Sort the list in ascending order
my_list.reverse() # Reverse the order of elements

List Comprehension

List comprehensions provide a concise way to create new lists based on existing lists.

numbers = [1, 2, 3, 4, 5]
squared_numbers = [x**2 for x in numbers] # Output: [1, 4, 9, 16, 25]

This is equivalent to:

squared_numbers = []
for x in numbers:
squared_numbers.append(x**2)

Tuples are similar to lists, but they are immutable, meaning their elements cannot be changed once created.

Creating Tuples

my_tuple = (1, 2, 3, “hello”)
empty_tuple = ()

Accessing Elements

You can access elements in a tuple using their index, just like with lists.

my_tuple = (10, 20, 30)
first_element = my_tuple[0] # Output: 10
second_element = my_tuple[1] # Output: 20

Slicing Tuples

You can extract a portion of a tuple using slicing.

my_tuple = (1, 2, 3, 4, 5)
subtuple = my_tuple[1:4] # Output: (2, 3, 4)

Tuples are Immutable

Unlike lists, tuples cannot be modified after creation. Attempting to change an element will raise an error.

my_tuple = (1, 2, 3)
my_tuple[0] = 10 # This will raise an error

Tuple Packing and Unpacking

  • Packing: Multiple values can be packed into a tuple using parentheses.
  • Unpacking: Elements of a tuple can be unpacked into individual variables.

# Packing
my_tuple = (1, 2, 3)

# Unpacking
a, b, c = my_tuple
print(a) # Output: 1
print(b) # Output: 2
print(c) # Output: 3

Note: Tuples are often used for immutable data structures, such as coordinates, configuration settings, or return values from functions.

Dictionaries are unordered collections of key-value pairs. Each key is unique, and it maps to a corresponding value.

Creating Dictionaries

my_dict = {“name”: “Alice”, “age”: 30, “city”: “New York”}
empty_dict = {}

Accessing Values

You can access the value associated with a key using square brackets.

my_dict = {“name”: “Alice”, “age”: 30}
name = my_dict[“name”] # Output: “Alice”
age = my_dict[“age”] # Output: 30

Modifying Dictionaries

You can add, remove, or modify key-value pairs in a dictionary.

my_dict = {“name”: “Alice”, “age”: 30}
my_dict[“city”] = “New York” # Add a new key-value pair
my_dict[“age”] = 31 # Modify an existing value
del my_dict[“name”] # Remove a key-value pair

Dictionary Methods

Dictionaries have many built-in methods that can be used to perform various operations.

my_dict = {“name”: “Alice”, “age”: 30, “city”: “New York”}
keys = my_dict.keys() # Get a list of keys
values = my_dict.values() # Get a list of values
items = my_dict.items() # Get a list of key-value pairs
has_key = “name” in my_dict # Check if a key exists

Dictionary Comprehension

Dictionary comprehensions provide a concise way to create new dictionaries based on existing dictionaries or other iterables.

numbers = [1, 2, 3, 4, 5]
squares = {x: x**2 for x in numbers} # Output: {1: 1, 2: 4, 3: 9, 4: 16, 5: 25}

This is equivalent to:

squares = {}
for x in numbers:
squares[x] = x**2

Sets are unordered collections of unique elements. They are useful for performing set operations like union, intersection, difference, and symmetric difference.   

Creating Sets

my_set = {1, 2, 3, 4, 5}
empty_set = set()

Set Operations

  • Union (|): Combines elements from both sets, removing duplicates.
  • Intersection (&): Returns elements that are common to both sets.
  • Difference (-): Returns elements that are in the first set but not in the second.
  • Symmetric difference (^): Returns elements that are in either set but not both.

set1 = {1, 2, 3}
set2 = {3, 4, 5}

union_set = set1 | set2 # Output: {1, 2, 3, 4, 5}
intersection_set = set1 & set2 # Output: {3}
difference_set = set1 – set2 # Output: {1, 2}
symmetric_difference_set = set1 ^ set2 # Output: {1, 2, 4, 5}

Set Methods

Sets have many built-in methods that can be used to perform various operations.

my_set = {1, 2, 3, 4, 5}
my_set.add(6) # Add an element
my_set.remove(3) # Remove an element (raises an error if not found)
my_set.discard(7) # Remove an element if it exists (no error if not found)
my_set.pop() # Remove a random element and return it
my_set.clear() # Remove all elements

Set Comprehension

Set comprehensions provide a concise way to create new sets based on existing sets or other iterables.

numbers = [1, 2, 3, 2, 4]
unique_numbers = {x for x in numbers} # Output: {1, 2, 3, 4}

This is equivalent to:

unique_numbers = set()
for x in numbers:
unique_numbers.add(x)

Importing Modules

Modules are Python files containing functions, classes, and variables. You can import modules into your code using the import statement.

import math

result = math.sqrt(4)
print(result) # Output: 2.0

You can also import specific functions or classes from a module:

from math import pi

print(pi) # Output: 3.141592653589793

Creating Your Own Modules

To create your own module, save your Python code in a file with a .py extension. Then, import the module in another Python file.

# my_module.py
def greet(name):
print(“Hello, ” + name + “!”)

# main.py
import my_module

my_module.greet(“Alice”)

Using Packages

Packages are directories containing multiple modules. To use a package, you need to import it using the dot notation.

import my_package.my_module

my_package.my_module.greet(“Bob”)

The Standard Library

Python comes with a rich standard library that provides a wide range of modules for various tasks. Some common modules include:

  • math: Mathematical functions (e.g., sqrt, sin, cos).
  • random: Functions for generating random numbers.
  • os: Functions for interacting with the operating system.
  • time: Functions for working with time and dates.
  • datetime: Functions for manipulating date and time objects.
  • json: Functions for working with JSON data.
  • urllib: Functions for working with URLs and web requests.

You can find a complete list of modules in the official Python documentation.

Opening and Closing Files

To work with files, you need to open them first and then close them when you’re finished.

file = open(“myfile.txt”, “r”) # Open the file in read mode
content = file.read()
file.close() # Close the file

Reading and Writing Data

  • Reading: Use the read() method to read the entire contents of the file as a string.
  • Writing: Use the write() method to write data to the file.

# Reading
file = open(“myfile.txt”, “r”)
content = file.read()
print(content)
file.close()

# Writing
file = open(“newfile.txt”, “w”)
file.write(“This is a new file.”)
file.close()

File Modes

  • r: Read mode (default).
  • w: Write mode (creates a new file or overwrites an existing one).
  • a: Append mode (appends data to the end of an existing file).
  • r+: Read and write mode.

Context Managers (with statement)

The with statement is a convenient way to work with files, as it automatically closes the file when you’re done.

with open(“myfile.txt”, “r”) as file:
content = file.read()
print(content)

Example:

# Reading a file line by line
with open(“data.txt”, “r”) as file:
for line in file:
print(line.strip())

# Writing data to a file
with open(“output.txt”, “w”) as file:
file.write(“Hello, world!\n”)
file.write(“This is another line.”)

Note: When working with large files, it’s more efficient to read them line by line using the readline() method or iterating over the file object.

Exceptions are errors that occur during program execution. Python provides mechanisms to handle exceptions gracefully, preventing your program from crashing.

Try-Except Blocks

A try-except block is used to catch and handle exceptions.

try:
# Code that might raise an exception
result = 10 / 0
except ZeroDivisionError:
print(“Error: Division by zero”)
except Exception as e:
print(“An unexpected error occurred:”, e)

Raising Exceptions

You can raise exceptions using the raise keyword.

def divide(x, y):
if y == 0:
raise ZeroDivisionError(“Cannot divide by zero”)
return x / y

try:
result = divide(10, 0)
except ZeroDivisionError as e:
print(e)

Custom Exceptions

You can create your own custom exceptions by defining new classes that inherit from the Exception class.

class MyCustomError(Exception):
pass

def check_age(age):
if age < 0:
raise MyCustomError(“Age cannot be negative”)

try:
check_age(-10)
except MyCustomError as e:
print(e)

Example:

try:
file = open(“nonexistent_file.txt”, “r”)
content = file.read()
except FileNotFoundError:
print(“File not found.”)
except Exception as e:
print(“An unexpected error occurred:”, e)
else:
print(“File content:”, content)
finally:
if file:
file.close()
print(“File closed.”)

This example demonstrates the use of a try-except-else-finally block. The else block is executed if no exceptions occur, and the finally block is always executed, regardless of whether an exception is raised.

Classes and Objects

In object-oriented programming, a class is a blueprint for creating objects. An object is an instance of a class.

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

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

# Create objects
dog1 = Dog(“Buddy”, “Golden Retriever”)
dog2 = Dog(“Max”, “Labrador”)

Attributes and Methods

Attributes are variables that belong to objects. Methods are functions that belong to objects.

class Dog:
# Attributes
name = “”
breed = “”

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

def eat(self):
print(“Eating food”)

Inheritance

Inheritance allows you to create new classes based on existing classes. The new class inherits the attributes and methods of the parent class.

class Animal:
def eat(self):
print(“Eating food”)

class Dog(Animal):
def bark(self):
print(“Woof!”)

# Create an object of the derived class
dog = Dog()
dog.eat() # Inherited from Animal
dog.bark()

Polymorphism

Polymorphism allows objects of different classes to be treated as if they were of the same type.

class Animal:
def make_sound(self):
pass

class Dog(Animal):
def make_sound(self):
print(“Woof!”)

class Cat(Animal):
def make_sound(self):
print(“Meow!”)

def animal_sound(animal):
animal.make_sound()

dog = Dog()
cat = Cat()

animal_sound(dog) # Output: Woof!
animal_sound(cat) # Output: Meow!

Encapsulation

Encapsulation is the practice of bundling data (attributes) and methods that operate on that data within a single unit (class). This helps to protect data from external access and modification.

class BankAccount:
def __init__(self, balance):
self.__balance = balance

def deposit(self, amount):
self.__balance += amount

def withdraw(self, amount):
if amount <= self.__balance:
self.__balance -= amount
else:
print(“Insufficient funds”)

def get_balance(self):
return self.__balance

In this example, the __balance attribute is private, meaning it cannot be accessed directly from outside the class. You can only access it through the public methods deposit, withdraw, and get_balance. This ensures data integrity and prevents unauthorized modifications.

Regular Expressions

Regular expressions are powerful tools for pattern matching in text. They provide a concise way to specify search patterns.

import re

text = “The quick brown fox jumps over the lazy dog.”
pattern = r”\bfox\b” # Matches the word “fox”

match = re.search(pattern, text)
if match:
print(“Found a match:”, match.group())

Functional Programming

Functional programming is a paradigm that emphasizes functions as the primary building blocks of programs. It promotes immutability, pure functions, and higher-order functions.

def square(x):
return x * x

numbers = [1, 2, 3, 4, 5]
squared_numbers = list(map(square, numbers))
print(squared_numbers) # Output: [1, 4, 9, 16, 25]

Decorators

Decorators are functions that modify the behavior of other functions. They are defined using the @ syntax.

def my_decorator(func):
def wrapper():
print(“Before function call”)
func()
print(“After function call”)
return wrapper

@my_decorator
def greet():
print(“Hello!”)

greet()

Generators

Generators are functions that return an iterator, allowing you to generate values on-the-fly. They use the yield keyword to return values.

def count_up(n):
for i in range(n):
yield i

for number in count_up(5):
print(number)

Libraries and Frameworks

Python has a vast ecosystem of libraries and frameworks that can be used to accomplish various tasks. Some popular ones include:

  • NumPy: For numerical computing and scientific computing.
  • Pandas: For data manipulation and analysis.
  • Matplotlib: For data visualization.
  • Scikit-learn: For machine learning.
  • TensorFlow: For deep learning.
  • Django: For web development.
  • Flask: For lightweight web development.

By exploring these additional topics, you can deepen your understanding of Python and its capabilities.

Python programming fundamentals, Python

By the end of this course, you will be able to understand and apply Python programming concepts to solve real-world problems. You will gain hands-on experience in writing Python scripts and developing projects using the language’s fundamental building blocks.