1. What is Python, and what are its main features?
Answer: Python is a high-level, interpreted, and dynamically typed programming language. It supports object-oriented, procedural, and functional programming paradigms. Python’s main features include
Easy-to-read syntax
Extensive standard libraries and third-party packages
Automatic memory management
Large community support
Cross-platform compatibility
2. What is PEP 8, and why is it important?
Answer: PEP 8 is the Python Enhancement Proposal that outlines the style guide for writing Python code. It includes recommendations on code layout, naming conventions, and coding standards to make Python code more readable and consistent. Following PEP 8 helps maintain a consistent coding style across projects, making collaboration easier.
3. What are Python’s built-in data types?
Answer: Python’s built-in data types include:
Numeric: int, float, complex
Sequence: str, list, tuple
Set: set, frozenset
Mapping: dict
Boolean: bool
NoneType: None
4. What is the difference between list, tuple, and set?
Answer:
List: Mutable, ordered collection of elements. Allows duplicate values and supports indexing.
Tuple: Immutable, ordered collection of elements. Allows duplicate values and supports indexing.
Set: Mutable, unordered collection of unique elements. Does not support duplicate values and is unindexed.
5. What is a dictionary in Python, and how is it different from a list?
Answer: A dictionary is an unordered collection of key-value pairs, defined using {} with key-value pairs separated by colons. Unlike lists, dictionaries are indexed by unique keys rather than sequential numbers. Dictionaries are useful for associative data, whereas lists are ideal for ordered sequences.
6. What are *args and **kwargs in functions?
Answer:
*args allows a function to accept a variable number of positional arguments as a tuple.
**kwargs allows a function to accept a variable number of keyword arguments as a dictionary.
Answer: A lambda function is an anonymous function expressed as a single line of code. It uses the lambda keyword and is commonly used for short, throwaway functions that perform simple operations. Syntax: lambda arguments: expression.
Example:
square = lambda x: x * x
print(square(5)) # Output: 25
8. Explain Python’s Global Interpreter Lock (GIL).
Answer: The Global Interpreter Lock (GIL) is a mutex in the CPython interpreter that allows only one thread to execute Python bytecode at a time, even on multi-core processors. This limits true parallelism in multi-threaded Python programs. However, GIL doesn’t affect multi-processing, and modules like multiprocessing can be used to achieve parallelism.
9. What is the difference between deepcopy and shallow copy?
Answer:
Shallow Copy: Creates a new object but inserts references to the original objects within it. Changes in nested elements affect both copies.
Deep Copy: Creates a new object and recursively copies all objects found within the original, creating an independent copy.
Example:
import copy
original = [[1, 2], [3, 4]]
shallow = copy.copy(original)
deep = copy.deepcopy(original)
10. Explain map(), filter(), and reduce() functions.
Answer:
map(function, iterable): Applies a function to all items in an iterable.
filter(function, iterable): Filters items in an iterable based on a condition.
reduce(function, iterable): Applies a rolling computation to sequential pairs of items (requires functools library).
The Python collections module provides a variety of specialized container types to optimize different storage and retrieval operations. Here’s a breakdown of the key classes:
1. Counter
A Counter is a dictionary subclass for counting hashable objects. It stores elements as dictionary keys and their counts as dictionary values. You can initialize a Counter with an iterable, a dictionary, or keyword arguments.
Example:
from collections import Counter
# With sequence of items
print(Counter(['B','B','A','B','C','A','B','B','A','C']))
# Output: Counter({'B': 5, 'A': 3, 'C': 2})
# with dictionary
print(Counter({'A': 3, 'B': 5, 'C': 2}))
# Output: Counter({'B': 5, 'A': 3, 'C': 2})
# with keyword arguments
print(Counter(A=3, B=5, C=2))
# Output: Counter({'B': 5, 'A': 3, 'C': 2})
2. OrderedDict
OrderedDict is a dictionary subclass that maintains the order of items as they are inserted. Deleting and reinserting a key moves it to the end.
Example:
from collections import OrderedDict
# Regular dictionary
d = {'a': 1, 'b': 2, 'c': 3, 'd': 4}
print("This is a Dict:")
for key, value in d.items():
print(key, value)
# Output:
# a 1
# b 2
# c 3
# d 4
# Ordered dictionary
od = OrderedDict(d)
print("\nThis is an Ordered Dict:")
for key, value in od.items():
print(key, value)
# Output:
# a 1
# b 2
# c 3
# d 4
3. DefaultDict
A DefaultDict provides default values for missing keys to avoid KeyError. It’s initialized with a default factory function.
Example:
from collections import defaultdict
# DefaultDict with int
d = defaultdict(int)
L = [1, 2, 3, 4, 2, 4, 1, 2]
for i in L:
d[i] += 1
print(d)
# Output: defaultdict(<class 'int'>, {1: 2, 2: 3, 3: 1, 4: 2})
# DefaultDict with list
d = defaultdict(list)
for i in range(5):
d[i].append(i)
print(d)
# Output: defaultdict(<class 'list'>, {0: [0], 1: [1], 2: [2], 3: [3], 4: [4]})
4. ChainMap
ChainMap combines multiple dictionaries into one. This is useful for managing multiple contexts in a single view.
These classes allow users to subclass and create modified dictionary, list, or string behaviors by acting as wrappers around built-in types.
UserDict Example:
from collections import UserDict
class MyDict(UserDict):
def __del__(self):
raise RuntimeError("Deletion not allowed")
def pop(self, s=None):
raise RuntimeError("Deletion not allowed")
def popitem(self, s=None):
raise RuntimeError("Deletion not allowed")
d = MyDict({'a': 1, 'b': 2, 'c': 3})
d.pop(1)
# Output: RuntimeError: Deletion not allowed
UserList
from collections import UserList
class MyList(UserList):
def remove(self, s=None):
raise RuntimeError("Deletion not allowed")
def pop(self, s=None):
raise RuntimeError("Deletion not allowed")
L = MyList([1, 2, 3, 4])
L.append(5)
print(L)
# Output: [1, 2, 3, 4, 5]
L.remove()
# Output: RuntimeError: Deletion not allowed
UserString
from collections import UserString
class MyString(UserString):
def append(self, s):
self.data += s
def remove(self, s):
self.data = self.data.replace(s, "")
s1 = MyString("Hello")
s1.append(" World")
print(s1)
# Output: Hello World
s1.remove("l")
print(s1)
# Output: Heo Word
NamedTuple in Python
In Python, NamedTuple from the collections module allows you to create simple, lightweight data structures that are similar to classes but don’t require a full class definition. NamedTuples combine the flexibility of dictionaries with the advantages of iteration and named access. This is particularly useful for creating objects with a predefined structure.
Python NamedTuple Syntax
To define a NamedTuple, use:
namedtuple(typename, field_names)
typename – The name of the NamedTuple.
field_names – A list of fields or attributes within the NamedTuple.
NamedTuple Example in Python
# Importing namedtuple from collections
from collections import namedtuple
# Defining a NamedTuple for Student
Student = namedtuple('Student', ['name', 'age', 'DOB'])
# Adding values to the NamedTuple
S = Student('Alex', '21', '01011999')
# Access using index
print("The student's age by index is:", S[1])
# Access using name
print("The student's name by field name is:", S.name)
Output:
The student's age by index is: 21
The student's name by field name is: Alex
Operations on NamedTuple
Operations supported by NamedTuples include:
1. Creating a NamedTuple in Python: You can create a NamedTuple class with namedtuple().
from collections import namedtuple
# Defining a Point NamedTuple
Point = namedtuple('Point', ['x', 'y'])
p = Point(x=3, y=4)
print(p.x, p.y)
Output:
3 4
2. Accessing NamedTuple Elements: NamedTuples provide different access methods.
3. Access by Index: Fields in a NamedTuple are ordered, allowing access by index.
from collections import namedtuple
# Defining Student NamedTuple
Student = namedtuple('Student', ['name', 'age', 'DOB'])
# Creating an instance
S = Student('Alex', '21', '01011999')
# Accessing age using index
print("The student's age by index is:", S[1])
Output:
The student's age by index is: 21
4. Access by Field Name: NamedTuple fields can be accessed by their field names.
from collections import namedtuple
# Creating a Student NamedTuple
Student = namedtuple('Student', ['name', 'age', 'DOB'])
# Adding values
S = Student('Alex', '21', '01011999')
# Accessing by field name
print("The student's name by field name is:", S.name)
Output:
The student's name by field name is: Alex
5. Access Using getattr(): Another way to retrieve values is with getattr().
from collections import namedtuple
# Defining Student NamedTuple
Student = namedtuple('Student', ['name', 'age', 'DOB'])
# Creating an instance
S = Student('Alex', '21', '01011999')
# Using getattr to access DOB
print("The student's DOB is:", getattr(S, 'DOB'))
Output:
The student's DOB is: 01011999
Conversion Operations for NamedTuple
NamedTuples offer conversion functions:
1. Conversion Using _make(): This function converts an iterable into a NamedTuple instance.
from collections import namedtuple
# Defining Student NamedTuple
Student = namedtuple('Student', ['name', 'age', 'DOB'])
# Creating an iterable
data = ['Chris', '20', '02111999']
# Converting the list to a NamedTuple
student_instance = Student._make(data)
print("NamedTuple from list:", student_instance)
Output:
NamedTuple from list: Student(name='Chris', age='20', DOB='02111999')
-
2. Conversion Using _asdict(): Converts a NamedTuple to an OrderedDict.
from collections import namedtuple
# Defining a Student NamedTuple
Student = namedtuple('Student', ['name', 'age', 'DOB'])
S = Student('Alex', '21', '01011999')
# Converting to OrderedDict
print("OrderedDict instance:", S._asdict())
3. __new__(): Creates a new instance by directly calling __new__ on the NamedTuple class.
from collections import namedtuple
# Defining Student NamedTuple
Student = namedtuple('Student', ['name', 'age', 'DOB'])
# Creating a new instance using __new__()
new_instance = Student.__new__(Student, 'Emily', '22', '05061999')
print("New instance:", new_instance)
Output:
New instance: Student(name='Emily', age='22', DOB='05061999')
Deque in Python
In Python, the deque (Doubly Ended Queue) is implemented using the collections module. Deques are more efficient than lists when performing append and pop operations from both ends, providing O(1) time complexity for these operations compared to O(n) in lists.
Types of Restricted Deque
1. Input Restricted Deque: Allows insertions at only one end, but deletions can happen from both ends. 2. Output Restricted Deque: Allows deletions at only one end, but insertions can happen at both ends.
Example: Creating a deque
from collections import deque
# Creating a deque with initial elements
queue = deque(['item1', 'item2', 'item3'])
print("Initial deque:", queue)
Output:
Initial deque: deque(['item1', 'item2', 'item3'])
Basic Operations on deque
1. Appending Items
append(): Adds an element to the right end of the deque.
appendleft(): Adds an element to the left end of the deque.
from collections import deque
# Initializing deque
deq = deque([10, 20, 30])
# Adding elements
deq.append(40)
deq.appendleft(5)
print("Deque after append and appendleft:", deq)
Output:
Deque after append and appendleft: deque([5, 10, 20, 30, 40])
2. Popping Items
pop(): Removes an element from the right end.
popleft(): Removes an element from the left end.
from collections import deque
# Initializing deque
deq = deque([10, 20, 30, 40])
# Removing elements
deq.pop()
deq.popleft()
print("Deque after pop and popleft:", deq)
Output:
Deque after pop and popleft: deque([20, 30])
3. Accessing and Modifying Items
index(ele, start, end): Finds the first occurrence of ele between the start and end indices.
insert(i, a): Inserts element a at position i.
remove(): Removes the first occurrence of a specified element.
count(): Counts the occurrences of an element.
from collections import deque
# Initializing deque
deq = deque([1, 2, 2, 3, 4, 3, 2])
# Using index and count
position = deq.index(3, 0, 6)
deq.insert(2, 5)
count_two = deq.count(2)
deq.remove(3)
print("Position of first 3:", position)
print("Deque after insert and remove:", deq)
print("Count of 2 in deque:", count_two)
Output:
Position of first 3: 3
Deque after insert and remove: deque([1, 2, 5, 2, 4, 3, 2])
Count of 2 in deque: 3
4. Finding Size of a deque
len(): Returns the current size of the deque.
from collections import deque
# Initializing deque
deq = deque([1, 2, 3, 4])
# Checking the size of deque
print("Size of deque:", len(deq))
Output:
Size of deque: 4
5. Accessing Front and Back Elements
deq[0]: Accesses the front element.
deq[-1]: Accesses the back element.
from collections import deque
# Initializing deque
deq = deque([10, 20, 30])
# Accessing front and back elements
print("Front element:", deq[0])
print("Back element:", deq[-1])
Output:
Front element: 10
Back element: 30
Complexity Analysis
Method
Time Complexity
Auxiliary Space
append()
O(1)
O(1)
appendleft()
O(1)
O(1)
pop()
O(1)
O(1)
popleft()
O(1)
O(1)
index()
O(N)
O(1)
insert()
O(N)
O(1)
remove()
O(N)
O(1)
count()
O(N)
O(1)
extend()
O(K)
O(1)
extendleft()
O(K)
O(1)
reverse()
O(N)
O(1)
rotate()
O(K)
O(1)
This covers the core functions and efficiency of deque in Python.
ChainMap in Python
In Python, the ChainMap container in the collections module allows you to combine multiple dictionaries into a single view. This is especially useful when managing multiple contexts or namespaces, as ChainMap prioritizes the order of the dictionaries, giving precedence to the first dictionary in case of duplicate keys.
keys(): Displays all keys across the dictionaries in the ChainMap.
values(): Displays values for each key in the ChainMap.
maps: Displays all key-value pairs across all dictionaries.
# Importing collections for ChainMap operations
import collections
# Initializing dictionaries
dict_a = {'p': 1, 'q': 2}
dict_b = {'q': 3, 'r': 4}
# Initializing ChainMap
combined_chain = collections.ChainMap(dict_a, dict_b)
# Printing the ChainMap contents
print("ChainMap contents:", combined_chain.maps)
# Displaying all keys
print("All keys in ChainMap:", list(combined_chain.keys()))
# Displaying all values
print("All values in ChainMap:", list(combined_chain.values()))
Output:
ChainMap contents: [{'p': 1, 'q': 2}, {'q': 3, 'r': 4}]
All keys in ChainMap: ['p', 'q', 'r']
All values in ChainMap: [1, 2, 4]
Manipulating Operations
new_child(): Adds a new dictionary at the start of the ChainMap.
reversed(): Reverses the order of dictionaries in the ChainMap.
Example:
# Importing collections for ChainMap operations
import collections
# Initializing dictionaries
dict_x = {'p': 5, 'q': 6}
dict_y = {'q': 7, 'r': 8}
dict_z = {'s': 9}
# Creating ChainMap
chain_map = collections.ChainMap(dict_x, dict_y)
# Displaying the ChainMap contents
print("Original ChainMap:", chain_map.maps)
# Using new_child() to add a new dictionary
new_chain_map = chain_map.new_child(dict_z)
# Displaying the updated ChainMap
print("Updated ChainMap with new child:", new_chain_map.maps)
# Accessing the value of 'q' before reversing
print("Value associated with 'q' before reversing:", new_chain_map['q'])
# Reversing the order of ChainMap dictionaries
new_chain_map.maps = reversed(new_chain_map.maps)
# Accessing the value of 'q' after reversing
print("Value associated with 'q' after reversing:", new_chain_map['q'])
Output:
Original ChainMap: [{'p': 5, 'q': 6}, {'q': 7, 'r': 8}]
Updated ChainMap with new child: [{'s': 9}, {'p': 5, 'q': 6}, {'q': 7, 'r': 8}]
Value associated with 'q' before reversing: 6
Value associated with 'q' after reversing: 7
Python Counter Objects elements()
The Counter class in Python’s collections module is a specialized type of container used to count the frequency of hashable objects. When you pass an iterable to Counter, it creates a hash table with elements as keys and their counts as values.
The elements() method of the Counter class provides an iterator over elements with positive counts, effectively repeating each element according to its count.
Parameters: None
Return Type: Returns an iterator over the elements with positive counts.
Example 1: Using elements() on a Counter object
# Importing Counter class from collections module
from collections import Counter
# Creating a Counter object from a string
counter_example = Counter("exampleexample")
# Printing the elements of the Counter object
for element in counter_example.elements():
print(element, end=" ")
Output:
e e e e x x a a m m p l
Example 2: Creating a Counter object from a list
from threading import Timer
def time_up():
print("\nTime's up! You took too long to respond.")
def ask_question():
print("What is your favorite color?")
timer = Timer(5, time_up) # Set a 5-second timer
timer.start()
answer = input()
timer.cancel() # Cancel the timer if input is received on time
return answer
response = ask_question()
print("Your favorite color is:", response)
An OrderedDict is a dictionary subclass that retains the order of key insertions. Unlike a regular dict, which does not guarantee order, OrderedDict preserves insertion sequence when iterating.
OrderedDict vs. dict in Python
An OrderedDict keeps items in the order they are added, while a regular dict may not. This behavior makes OrderedDict ideal when the order of entries is important.
Example: This code demonstrates the differences between a standard dictionary (dict) and an ordered dictionary (OrderedDict). First, it displays the entries in a regular dictionary, where insertion order isn’t assured.
from collections import OrderedDict
print("This is a Dict:\n")
d = {}
d['x'] = 1
d['y'] = 2
d['z'] = 3
d['w'] = 4
for key, value in d.items():
print(key, value)
print("\nThis is an Ordered Dict:\n")
od = OrderedDict()
od['x'] = 1
od['y'] = 2
od['z'] = 3
od['w'] = 4
for key, value in od.items():
print(key, value)
Output:
This is a Dict:
x 1
y 2
z 3
w 4
This is an Ordered Dict:
x 1
y 2
z 3
w 4
Key-Value Changes in OrderedDict
Changing the value associated with a key doesn’t affect its position within an OrderedDict. The example below illustrates changing a key’s value while maintaining order.
Example:
from collections import OrderedDict
print("Before:\n")
od = OrderedDict()
od['x'] = 1
od['y'] = 2
od['z'] = 3
od['w'] = 4
for key, value in od.items():
print(key, value)
print("\nAfter:\n")
od['z'] = 5
for key, value in od.items():
print(key, value)
Output:
Before:
x 1
y 2
z 3
w 4
After:
x 1
y 2
z 5
w 4
Equality Comparison
Two OrderedDict objects are considered equal only if their contents and insertion order match.
Example:
from collections import OrderedDict
# Two ordered dictionaries with different key order
od1 = OrderedDict([('x', 1), ('y', 2), ('z', 3)])
od2 = OrderedDict([('z', 3), ('y', 2), ('x', 1)])
# Comparing for equality
print(od1 == od2)
Output:
False
Reversing OrderedDict
Though OrderedDict lacks a reverse() method, Python’s reversed() function combined with list() and items() can reverse its contents.
Example:
from collections import OrderedDict
my_dict = OrderedDict([('x', 1), ('y', 2), ('z', 3)])
# Reverse the OrderedDict
reversed_dict = OrderedDict(reversed(list(my_dict.items())))
# Print reversed dictionary
for key, value in reversed_dict.items():
print(key, value)
Organizing files into folders and subfolders is common for efficiency and ease of management. Similarly, Python packages group related modules to provide organized, reusable components in a structured format, aiding code readability and maintenance. This article covers various categories of Python packages that support web frameworks, AI, GUI applications, web scraping, automation, and game development.
How to Create a Package in Python
Creating Python packages allows for modular code design. Follow these steps to make a simple package:
1. Create a Directory: Make a folder for your package, e.g., my_package. 2. Add Modules: Inside this directory, create individual Python files, each containing related functionality. 3. Init File: Add an __init__.py file (it can be empty) to signal that the directory is a package. 4. Subpackages: Add subdirectories with their own __init__.py files if you want sub-packages. 5. Importing Modules: Import modules with dot notation. For example, to access my_function in module1.py under my_package, use: from my_package.module1 import my_function. 6. Distribution: Use a setup.py file with setuptools to define package metadata for distribution.
Example Code Structure:
my_package/__init__.py
module1.py
module2.py
# module1.py
def greet(name):
print(f"Welcome, {name}!")
# Example usage
from my_package.module1 import greet
greet("Jane")
Python Packages for Web Frameworks
Web frameworks in Python provide tools to simplify and accelerate the process of building web applications, APIs, and services. Here are some popular choices:
1. Flask: A lightweight and flexible framework that is ideal for small-scale projects and APIs. Flask is unopinionated, meaning it gives developers the freedom to choose how they want to structure their code. It’s simple to use and allows for quick setups, making it popular for microservices and prototyping. 2. Django: Known for its rapid development capabilities, Django is a comprehensive framework that includes built-in features for database management, user authentication, URL routing, and an admin interface. It emphasizes the “Don’t Repeat Yourself” (DRY) principle, encouraging reusable code for scalable applications. 3. FastAPI: This high-performance framework is designed specifically for building APIs. It’s built on ASGI (Asynchronous Server Gateway Interface) and supports automatic documentation generation using OpenAPI and JSON Schema, as well as Python’s type hints for input validation. 4. Pyramid: Pyramid provides flexibility and encourages a modular approach, allowing developers to pick and integrate only the components they need. It’s suited for projects that require customization and adaptability, from small apps to large, complex applications. 5. Tornado: Tornado is built to handle high-concurrency applications through non-blocking I/O, making it excellent for real-time applications, web sockets, and long polling. It’s highly scalable and can manage a large number of open connections simultaneously. 6. Falcon: A minimalist and highly efficient framework focused on building RESTful APIs. Falcon is often chosen for microservices due to its low overhead and fast response times. 7. Bottle: Bottle is a single-file framework ideal for building small web applications and rapid prototyping. It’s easy to set up and requires minimal configuration, making it suitable for lightweight applications and simple APIs.
Python Packages for AI & Machine Learning
Python offers a range of packages to cover the different aspects of AI and machine learning, from statistical analysis to deep learning and computer vision:
Statistical Analysis
NumPy: Fundamental for numerical operations, it provides fast and efficient ways to handle large arrays and matrices, essential for scientific computing.
Pandas: Widely used for data manipulation and analysis, it introduces DataFrames, a tabular data structure that simplifies data preprocessing and analysis.
SciPy: Built on NumPy, SciPy offers additional functionality for scientific and engineering computations, such as optimization, integration, and signal processing.
Data Visualization
Matplotlib: The foundational plotting library, providing basic tools for creating static, interactive, and animated graphs.
Seaborn: Extends Matplotlib with additional features for creating statistical plots, such as heatmaps, pair plots, and more.
Plotly: Known for interactive and dynamic visualizations, Plotly is excellent for web-based data visualization.
Deep Learning
TensorFlow: One of the most popular deep learning libraries, offering tools for building and training neural networks, as well as deploying them in production.
torch (PyTorch): An alternative to TensorFlow, it’s widely appreciated for its dynamic computation graph, making it more intuitive for experimentation and research.
Keras: A high-level API that works on top of TensorFlow and simplifies the process of defining and training neural networks.
Natural Language Processing
NLTK: A comprehensive library for basic NLP tasks, such as tokenization, stemming, and more advanced functions like sentiment analysis.
spaCy: Designed for industrial use, it’s highly efficient and comes with pre-trained models for part-of-speech tagging, named entity recognition, and more.
TextBlob: A simpler high-level library, providing a more accessible approach to text processing, sentiment analysis, and translation.
Generative AI
Pillow: A library for image processing, essential for preparing images in computer vision tasks and generative models.
spaCy: In addition to NLP tasks, spaCy can be used with pre-trained language models for generative AI projects, especially in text generation.
Fastai: Built on top of PyTorch, it simplifies the development of deep learning models for generative tasks, such as text and image generation.
Computer Vision
OpenCV: A powerful library for image and video processing, used extensively for object detection, facial recognition, and other vision tasks.
scikit-image: Extends functionality for image transformations, filtering, and feature extraction, which is critical in computer vision preprocessing.
Dlib: Provides tools for facial recognition, object detection, and shape prediction, commonly used in identity verification and biometrics.
Python Packages for GUI Applications
Creating GUI applications in Python can range from basic desktop apps to interactive tools. Here’s a breakdown of popular GUI libraries:
1. Tkinter: This built-in library allows for creating simple GUI applications using a range of standard widgets (buttons, labels, text fields). It’s easy to get started and is included in most Python installations.
2. PyQt5: Based on the Qt framework, PyQt5 provides tools to create complex and highly customizable applications. It’s suitable for creating feature-rich applications that require advanced interface elements and themes.
3. Kivy: Known for its cross-platform support, Kivy allows you to build multi-touch applications that work on Android, iOS, Windows, macOS, and Linux with a single codebase.
4. PySide: Also based on the Qt framework, PySide offers similar capabilities to PyQt5 but under a different licensing model, making it a flexible choice for developers.
5. PySimpleGUI: This library simplifies GUI development, providing a straightforward way to add GUI elements without needing extensive knowledge of Tkinter or PyQt5.
6. NiceGUI: Ideal for creating micro web applications and dashboards, NiceGUI allows developers to integrate interactive visual elements, charts, and plots with minimal code.
7. PyGTK: PyGTK provides bindings for the GTK library, which is often used in GNOME desktop applications. It’s great for creating GUIs compatible with Linux environments.
Python Packages for Web Frameworks
These libraries facilitate scraping data from web pages and automating browser interactions:
1. Requests: A powerful HTTP library that simplifies making HTTP requests to interact with web APIs and retrieve data.
2. BeautifulSoup: Useful for parsing HTML and XML documents, allowing you to navigate and extract data based on the structure of the page.
3. Selenium: Known for automating web browsers, Selenium is often used for testing applications and scraping data from dynamic websites.
4. MechanicalSoup: Combines the functionalities of Requests and BeautifulSoup, allowing you to interact with web pages, submit forms, and navigate.
5. urllib3: This HTTP client library is great for making reliable HTTP requests, handling connection pooling, SSL verification, and retries.
6. Scrapy: A comprehensive scraping framework, Scrapy provides tools to navigate and extract structured data from websites, often used for large-scale scraping.
7. Requests-HTML: Merges the ease of Requests with HTML parsing capabilities, allowing you to query and manipulate HTML content directly.
Python Packages for Game Development
Python offers several packages to bring game ideas to life, from 2D to 3D games:
1. PyGame: This popular package provides a set of modules to handle graphics, sound, and input, ideal for developing 2D games.
2. Panda3D: A game engine for developing 3D games, Panda3D supports complex simulations and includes tools for rendering, audio, and asset management.
3. Pyglet: Useful for creating multimedia applications, Pyglet offers capabilities for graphics, sound, and input, making it versatile for both games and other applications.
4. Arcade: A beginner-friendly library for 2D game development, Arcade simplifies common game tasks and provides tools for creating engaging visual experiences.
5. PyOpenGL: A Python binding for OpenGL, allowing for more advanced 3D graphics and real-time rendering, suitable for both games and simulations.
6. Cocos2d: A framework specifically for 2D games, Cocos2d is widely used for simple, efficient game development and includes tools for handling sprites, sound, and animations.
Python modules are files that store functions, classes, and variables. Python has numerous built-in modules, each specialized for various tasks. In this guide, we’ll cover how to create and use modules in Python, including importing techniques, specific imports, aliasing, and exploring built-in modules.
What is a Python Module?
A Python module is a file containing definitions and statements. Modules help organize code logically, making it more understandable and reusable. Each module can define functions, classes, and variables, and it may include executable code.
Creating a Python Module
To create a module in Python, write the necessary code and save it with a .py extension. Here’s a simple example:
Example: operations.py
# Module: operations.py
def add(a, b):
return a + b
def subtract(a, b):
return a - b
Importing a Module in Python
To use the functions and classes defined in a module, we can import it using the import statement. Python searches for modules in a specific order and imports them if found in the search path.
Syntax:
import module_name
Example:
import operations
print(operations.add(15, 5))
Output:
20
Importing Specific Attributes from a Module
Python’s from statement allows you to import specific parts of a module rather than the whole module.
Example:
# Importing specific functions from math
from math import sqrt, factorial
print(sqrt(25))
print(factorial(5))
Output:
5.0
120
Importing All Names with *
Using the * symbol imports all names from a module into the current namespace. Use this approach cautiously to avoid conflicts.
Syntax:
from module_name import *
Example:
from math import *
print(sqrt(16))
print(factorial(4))
Output:
4.0
24
Locating Python Modules
When importing a module, Python looks through the following paths:
1. Current directory. 2. Directories listed in PYTHONPATH (an environment variable). 3. Installation-specific directories.
Python stores these paths in sys.path for easy access.
import sys
print(sys.path)
Example Output:
['/user/files', '/usr/lib/python3.8', ...]
Renaming a Module with Aliases
You can rename a module upon import using the as keyword.
Syntax:
import module_name as alias_name
Example:
import math as m
print(m.sqrt(25))
print(m.factorial(4))
Output:
5.0
24
Python Built-In Modules
Python provides several built-in modules, including math, random, and datetime. Let’s look at a few examples:
Example with math
import math
print(math.sqrt(36))
print(math.pi)
print(math.degrees(1.57))
print(math.radians(90))
print(math.sin(1.57))
print(math.factorial(5))
import random
print(random.randint(1, 10)) # Random integer between 1 and 10
print(random.uniform(0, 1)) # Random float between 0 and 1
print(random.choice(['apple', 'banana', 'cherry']))
Output:
7
0.5682323
banana
Example with datetime:
import datetime
from datetime import date
import time
print(time.time())
print(date.fromtimestamp(123456789))
In Python programming, errors can be broadly classified into two types: Syntax Errors and Exceptions. Errors halt the program execution, while exceptions arise from unexpected events that disrupt the usual program flow but may still be handled to allow the program to continue.
Common Types of Python Exceptions
Python has several built-in exceptions for handling errors that may occur during code execution. Here are some frequent ones:
SyntaxError: Raised when there is a syntax issue, like a missing colon or an unmatched parenthesis.
TypeError: Occurs when an operation is applied to an inappropriate data type, such as adding an integer to a string.
NameError: Triggered when a variable or function name is not found. IndexError: Occurs when an index is out of range in a list or other sequence.
KeyError: Raised when a key is not found in a dictionary.
ValueError: Occurs when a function receives an argument with the right type but an inappropriate value.
AttributeError: Triggered when an attribute or method is not found on an object.
IOError: Raised for input/output errors, such as issues reading or writing a file.
ZeroDivisionError: Occurs when dividing a number by zero.
ImportError: Triggered when a module fails to import.
Difference Between Syntax Errors and Exceptions
Syntax Errors: Caused by improper syntax and will terminate the program.
amount = 5000
if amount > 1000
print("Eligible to make a purchase")
Output:
SyntaxError: expected ':'
Exceptions: These arise from runtime errors in syntactically correct code, which can change the program’s normal flow.
value = 100
result = value / 0
print(result)
Output:
ZeroDivisionError: division by zero
Handling Exceptions with try and except
In Python, you use try and except blocks to catch and handle exceptions.
Example:
data = [1, 2, 3]
try:
print("Second element:", data[1])
print("Fourth element:", data[3])
except IndexError:
print("An error occurred: Index out of range")
Output:
Second element: 2
An error occurred: Index out of range
Catching Specific Exceptions
You can use multiple except clauses to handle different types of exceptions.
def calculate(a):
if a < 5:
result = a / (a - 4)
print("Result:", result)
try:
calculate(3)
calculate(5)
except ZeroDivisionError:
print("Division by zero error occurred and handled")
except NameError:
print("NameError occurred and handled")
Output:
Division by zero error occurred and handled
try with else Clause
The else clause runs only if the try block does not raise any exceptions.
name = 'Sam'
age = 25
print(f"Hello, My name is {name} and I'm {age} years old.")
Output:
-5.0
Division resulted in zero denominator
finally Clause in Python
The finally block is always executed after try and except blocks, regardless of whether an exception occurs.
num = int(input("Enter a value: "))
add = num + 5
print("The sum is %d" % add)
Output:
Cannot divide by zero
Execution complete
Raising Exceptions
The raise statement allows you to force a specific exception. You can specify an error message when raising an exception.
try:
raise NameError("This is a custom NameError")
except NameError:
print("A NameError occurred")
raise
Output:
A NameError occurred
Traceback (most recent call last):
...
NameError: This is a custom NameError
Advantages of Exception Handling
Improves program reliability: Helps in avoiding program crashes from unexpected errors.
Simplifies error handling: Keeps error management separate from main logic, making the code more readable.
Produces cleaner code: Reduces complex conditional checks for error detection.
Eases debugging: Exception traceback indicates the error location, simplifying debugging.
Disadvantages of Exception Handling
Performance overhead: Handling exceptions can be slower than using conditional checks.
Increases code complexity: Multiple exceptions can add complexity, especially with varied handling methods.
Potential security risks: Unhandled exceptions might reveal sensitive information or create vulnerabilities, so careful handling is essential.
User-defined Exceptions in Python with Examples
Custom exceptions in Python with practical examples is as follows.
class CustomError(Exception):
pass
raise CustomError("Example of Custom Exceptions in Python")
Output:
CustomError: Example of Custom Exceptions in Python
Python raises errors and exceptions when something goes wrong, potentially causing abrupt program termination. However, with try-except blocks, Python provides robust exception handling to prevent such interruptions. Common exceptions include IndexError, ImportError, IOError, ZeroDivisionError, TypeError, and FileNotFoundError.
Creating User-Defined Exceptions in Python
Custom exceptions should inherit from the Exception class, directly or indirectly. While it’s not required, custom exceptions often follow the naming convention of ending with “Error,” similar to Python’s standard exceptions.
Example:
# A Python program to create a user-defined exception
# MyError class inherits from Exception
class MyError(Exception):
# Constructor to initialize the error value
def __init__(self, value):
self.value = value
# __str__ method to return the error message
def __str__(self):
return repr(self.value)
try:
raise MyError(3 * 2)
# Handling the exception
except MyError as error:
print('A new exception occurred:', error.value)
Output:
Customizing Exception Classes
For more details about Exception class, try running:
python
Copy code
Customizing Exception Classes
For more details about Exception class, try running:
help(Exception)
Example 1: User-Defined Exception with Multiple Inheritance
Below is an example with a base class Error inherited by user-defined classes to handle specific custom exceptions.
# Defining a base class for exceptions
class Error(Exception):
"""Base class for other exceptions"""
pass
class ZeroDivisionErrorCustom(Error):
"""Raised when the input value is zero"""
pass
try:
num = int(input("Enter a number: "))
if num == 0:
raise ZeroDivisionErrorCustom
except ZeroDivisionErrorCustom:
print("Input value is zero, please try again!")
Output:
Enter a number: 0
Input value is zero, please try again!
Example 2: Deriving Errors from Superclass Exception
When a module requires specific exception handling, a base class for those exceptions can be defined. Additional subclasses allow for distinct error conditions.
# Define a base exception class
class Error(Exception):
pass
class TransitionError(Error):
# Raised when an invalid state transition occurs
def __init__(self, prev, next, msg):
self.prev = prev
self.next = next
self.msg = msg
try:
raise TransitionError(2, 6, "Transition Not Allowed")
except TransitionError as error:
print('Exception occurred:', error.msg)
Output:
Exception occurred: Transition Not Allowed
Using Standard Exceptions as a Base Class
The following example demonstrates using RuntimeError as a base class for custom exceptions, like NetworkError.
# NetworkError is derived from RuntimeError
class NetworkError(RuntimeError):
def __init__(self, message):
self.message = message
try:
raise NetworkError("Network connection lost")
except NetworkError as e:
print(e.message)
Output:
Network connection lost
Advantages of Exception Handling
Reliability: Manages unexpected errors, ensuring program stability.
Simplified Error Management: Separates error-handling code from main logic.
Cleaner Code: Avoids nested conditionals by handling errors systematically.
Debugging Ease: Tracebacks help in pinpointing error locations quickly.
Disadvantages of Exception Handling
Performance Overhead: Handling exceptions is slightly slower than conditional checks.
Complexity: Can increase code complexity, especially with multiple exception types.
Security Risks: Improperly managed exceptions may expose sensitive information, so they must be handled with caution.
Built-in Exceptions in Python
In Python, all instances must be derived from the BaseException class, and no two unrelated exception classes are considered equivalent, even if they share the same name. The Python interpreter or built-in functions can raise these exceptions.
You can view built-in exceptions, functions, and attributes using the locals() function:
>>> locals()['__builtins__']
Base Classes
The following exceptions serve primarily as base classes for other exceptions:
1. BaseException: This is the base class for all built-in exceptions. It is not intended for direct inheritance by user-defined classes, which should inherit from Exception. This class uses str() to create a string representation of the exception based on its arguments. An empty string is returned if there are no arguments.
Attributes:
args: A tuple of arguments passed to the exception constructor.
with_traceback(tb): Sets tb as the new traceback for the exception and returns the exception object.
3. BufferError: Raised when an operation related to buffers cannot be completed.
4. LookupError: Base class for exceptions raised when a key or index used on a mapping or sequence is invalid. Examples include:
KeyError
IndexError
Example:
try:
a = [1, 2, 3]
print(a[3])
except LookupError:
print("Index out of range.")
Output:
Index out of range.
Concrete Exceptions
Here are some of the more common exceptions:
AssertionError: Raised when an assert statement fails.
class MyClass:
pass
obj = MyClass()
print(obj.some_attribute)
Output:
AttributeError: 'MyClass' object has no attribute 'some_attribute'
EOFError: Raised when input() reaches the end of file condition.
while True:
data = input('Enter name: ')
print('Hello', data)
If an EOF is encountered, the interpreter will raise:
EOFError: EOF when reading a line
FloatingPointError: Raised for floating-point operation failures when Python is configured to raise this error.
GeneratorExit: Raised when a generator or coroutine is closed.
def generator():
try:
yield 1
except GeneratorExit:
print('Generator is exiting')
g = generator()
next(g)
g.close()
Output:
ImportError: No module named 'non_existent_module'
IndexError: Raised for sequence indexes out of range.
lst = [1, 2]
print(lst[3])
Output:
IndexError: list index out of range
KeyError: Raised when a mapping (dictionary) key is not found
dct = {'a': 1}
print(dct['b'])
Output:
KeyError: 'b'
KeyboardInterrupt: Raised when the user interrupts execution (e.g., by pressing Ctrl+C).
try:
input("Press Ctrl+C to interrupt.")
except KeyboardInterrupt:
print("Execution interrupted by user.")
MemoryError: Raised when an operation runs out of memory.
NameError: Raised when a local or global name is not found.
print(unknown_variable)
Output:
NameError: name 'unknown_variable' is not defined
NotImplementedError: Raised when an abstract method in a user-defined class is not implemented in derived classes.
OSError: Raised for operating system-related errors.
OverflowError: Raised for arithmetic operations that exceed limits.
RecursionError: Raised when the maximum recursion depth is exceeded.
ReferenceError: Raised when a weak reference proxy is used to access a referent that has been garbage-collected.
RuntimeError: Raised for errors that do not fall into any other category.
StopIteration: Raised to indicate the end of an iterator.
SyntaxError: Raised when a syntax error is encountered.
eval('x === y')
Output:
SyntaxError: invalid syntax
SystemError: Raised for internal interpreter errors.
SystemExit: Raised by the sys.exit() function.
TypeError: Raised when an operation is applied to an object of inappropriate type.
5 + 'a'
Output:
TypeError: unsupported operand type(s) for +: 'int' and 'str'
UnboundLocalError: A subclass of NameError, raised when a reference is made to a local variable that has not been assigned.
UnicodeError: Raised for Unicode-related encoding or decoding errors.
ValueError: Raised when a function receives an argument of correct type but invalid value.
int('a')
Output:
ValueError: invalid literal for int() with base 10: 'a'
ZeroDivisionError: Raised when division or modulo by zero occurs.
print(1 / 0)
Output:
ZeroDivisionError: division by zero
In Python, errors can be classified as syntax errors and exceptions. Syntax errors occur due to incorrect syntax, stopping the program’s execution immediately. Exceptions, however, are raised by specific events during execution, changing the program’s normal flow.
Some commonly encountered exceptions include:
IOError: Raised when a file operation (such as open) fails.
KeyboardInterrupt: Raised when the user interrupts program execution, typically by pressing Ctrl+C.
ValueError: Occurs when a function receives an argument of the right type but inappropriate value.
EOFError: Raised when the input() function hits an end-of-file condition.
ImportError: Raised when an import statement fails to find the specified module.
Try-Except in Python
The try and except statements help manage exceptions in Python. The try block contains the code that may throw an exception, and the except block handles the exception if it occurs.
try:
# Code to execute
except:
# Code to execute if an error occurs
How try Works
1. Python executes the code within the try block first. 2. If no exception is raised, the except block is skipped. 3. If an exception occurs, Python jumps to the except block. 4. If the exception is not handled, it will propagate to any outer try blocks, and, if unhandled, will stop execution. 5. Multiple except blocks can be used to handle different exceptions.
Example 1: Code runs without any exceptions, so only the try block executes.
def divide(x, y):
try:
result = x // y
print("The result is:", result)
except ZeroDivisionError:
print("Error: Division by zero is undefined.")
divide(10, 2)
Output:
The result is: 5
Catching Specific Exceptions with as
You can also catch and display the specific type of error that occurs by using Exception as.
def divide(x, y):
try:
result = x // y
print("The result is:", result)
except Exception as e:
print("An error occurred:", e)
divide(10, 'A')
divide(10, 0)
Output:
An error occurred: unsupported operand type(s) for //: 'int' and 'str'
An error occurred: integer division or modulo by zero
Using else with try–except
In Python, you can use an else clause with try–except blocks. The else block executes only if no exceptions are raised in the try block.
try:
# Code to execute
except:
# Code to execute if an error occurs
else:
# Code to execute if no exception occurs
Example:
def safe_divide(a, b):
try:
c = (a + b) // (a - b)
except ZeroDivisionError:
print("Error: Division by zero.")
else:
print("The result is:", c)
safe_divide(5, 3)
safe_divide(3, 3)
Output:
The result is: 4
Error: Division by zero.
finally Keyword in Python
The finally block in Python is always executed after the try and except blocks, whether or not an exception occurs. It is often used for cleanup actions.
try:
# Code to execute
except:
# Code to execute if an error occurs
else:
# Code to execute if no exception occurs
finally:
# Code that always executes
Example:
try:
k = 10 // 0 # Raises a ZeroDivisionError
print(k)
except ZeroDivisionError:
print("Error: Cannot divide by zero.")
finally:
print("This block always executes.")
Output:
Error: Cannot divide by zero.
This block always executes.
Object-Oriented Programming (OOP) is a fundamental concept in Python that enables developers to create modular, maintainable, and scalable applications. By mastering the core OOP principles—classes, objects, inheritance, encapsulation, polymorphism, and abstraction—programmers can fully leverage Python’s capabilities to design effective solutions for complex problems.
What is Object-Oriented Programming in Python?
In Python, Object-Oriented Programming (OOP) is a programming paradigm that uses objects and classes. This approach represents real-world entities through concepts like inheritance, polymorphism, encapsulation, and abstraction. The primary objective of OOP is to bind data and functions that operate on that data into a single unit, which restricts access to this data from other parts of the code.
OOP Concepts in Python
1. Class in Python 2. Objects in Python 3. Polymorphism in Python 4. Encapsulation in Python 5. Inheritance in Python 6. Data Abstraction in Python
Python Class
A class is a blueprint from which objects are created. It groups related attributes and methods. For example, to manage a collection of dogs with attributes like breed and age, creating a class can help organize this information effectively.
Key Points on Python Class:
Classes are created using the class keyword.
Attributes are variables within a class.
Attributes can be accessed using the dot (.) operator. For example: MyClass.my_attribute
Syntax:
class ClassName:
# Statements
pass
Example:
class Animal:
pass
Python Objects
An object is an instance of a class and represents entities with a state and behavior. For example, an integer, string, or list in Python is an object.
An object consists of:
State: Defined by its attributes.
Behavior: Defined by its methods.
Identity: Provides a unique identity for each object.
To understand these, let’s revisit the Animal class:
class Animal:
def __init__(self, species, age):
self.species = species
self.age = age
Here, species and age represent the state, while behaviors might include methods that describe the animal’s actions.
Creating an Object
lion = Animal("Lion", 5)
The Python self
In Python, every method must have self as the first parameter, which represents the instance of the class.
The Python __init__ Method
The __init__ method in Python is similar to constructors in other languages. It initializes an object’s attributes.
Example with Class and Instance Attributes:
class Car:
# Class attribute
category = "vehicle"
def __init__(self, make):
self.make = make
# Object instantiation
car1 = Car("Toyota")
car2 = Car("Honda")
# Accessing class attributes
print(f"Car1 is a {car1.category}")
print(f"Car2 is also a {car2.category}")
# Accessing instance attributes
print(f"Car1 make is {car1.make}")
print(f"Car2 make is {car2.make}")
Output:
Car1 is a vehicle
Car2 is also a vehicle
Car1 make is Toyota
Car2 make is Honda
Python Inheritance
Inheritance allows one class (child class) to inherit properties and methods from another (parent class). This promotes code reuse and a logical structure.
Types of Inheritance in Python:
1. Single Inheritance: One class inherits from a single parent class. 2. Multilevel Inheritance: One class is derived from another, which is also derived from another class. 3. Hierarchical Inheritance: Multiple classes inherit from one parent class. 4, Multiple Inheritance: One class inherits from multiple classes.
Example of Inheritance:
# Parent class
class Person:
def __init__(self, name, idnumber):
self.name = name
self.idnumber = idnumber
def display(self):
print(self.name)
print(self.idnumber)
# Child class
class Employee(Person):
def __init__(self, name, idnumber, salary, position):
super().__init__(name, idnumber)
self.salary = salary
self.position = position
def show_details(self):
print(f"Name: {self.name}")
print(f"ID Number: {self.idnumber}")
print(f"Position: {self.position}")
emp1 = Employee("Alice", 101, 50000, "Manager")
emp1.display()
emp1.show_details()
Output:
Alice
101
Name: Alice
ID Number: 101
Position: Manager
Python Polymorphism
Polymorphism allows methods to operate on objects of various types. Here’s an example using different types of birds:
class Bird:
def description(self):
print("This is a bird.")
class Sparrow(Bird):
def description(self):
print("Sparrows can fly.")
class Penguin(Bird):
def description(self):
print("Penguins cannot fly.")
bird1 = Sparrow()
bird2 = Penguin()
bird1.description()
bird2.description()
Output:
Sparrows can fly.
Penguins cannot fly.
Python Encapsulation
Encapsulation wraps data and methods into a single unit, restricting access to certain components. For instance, private attributes can only be modified within their class.
Data abstraction hides implementation details from the user and provides only the essential features. This can be achieved using abstract classes or private attributes.
Example of Data Abstraction
class Rectangle:
def __init__(self, length, width):
self.__length = length # Private attribute
self.__width = width # Private attribute
def area(self):
return self.__length * self.__width
def perimeter(self):
return 2 * (self.__length + self.__width)
# Creating an object
rect = Rectangle(5, 3)
# Accessing methods
print(f"Area: {rect.area()}")
print(f"Perimeter: {rect.perimeter()}")
# Uncommenting the following line will raise an AttributeError
# print(rect.__length)
A function in Python is a block of code designed to perform a specific task. By grouping repetitive tasks into functions, you can reuse the same code for different inputs, improving both efficiency and maintainability.
Benefits of Using Functions
Improves Code Readability: By abstracting complex tasks into named functions, the main code becomes cleaner and easier to read.
Increases Code Reusability: Once a function is written, it can be called multiple times without needing to rewrite the same code.
Syntax for Python Functions
def function_name(parameters):
"""Docstring (optional)"""
# function body
return value
Types of Functions in Python
There are two main types of functions in Python:
1. Built-in Functions: These are pre-defined functions provided by Python (e.g., print(), len(), input()). 2. User-defined Functions: Functions that you define yourself based on your needs.
Creating and Calling a Function
In Python, functions are created using the def keyword. After defining the function, it can be called by its name followed by parentheses.
Example of a Simple Function:
def greet():
print("Hello, Welcome to Python!")
greet() # Function call
Output:
Hello, Welcome to Python!
Example: Function to Add Two Numbers:
def add(a: int, b: int) -> int:
"""Returns the sum of two numbers."""
return a + b
# Function call with arguments
result = add(5, 10)
print(f"Sum of 5 and 10 is {result}")
Output:
Sum of 5 and 10 is 15
Python Function with Return
Functions can return values using the return keyword. This allows you to send back the result of the function to the caller.
Example:
def square(num):
return num * num
print(square(4)) # Function call
Output:
16
Types of Function Arguments
Python supports different types of arguments that can be passed to functions:
1. Default Arguments: Arguments that assume a default value if not provided in the function call.
3. Positional Arguments: Arguments are assigned based on their position in the function call.
Example:
def introduce(name, age):
print(f"Hi, I am {name}, and I am {age} years old.")
introduce("Alice", 25) # Correct order
introduce(25, "Alice") # Incorrect order
Output:
Hi, I am Alice, and I am 25 years old.
Hi, I am 25, and I am Alice years old.
4. Arbitrary Arguments(*args and **kwargs):
*args: Used to pass a variable number of non-keyword arguments.
**kwargs: Used to pass a variable number of keyword arguments.
Example with *args:
def print_args(*args):
for arg in args:
print(arg)
print_args("Hello", "Welcome", "to", "Python")
Output:
Hello
Welcome
to
Python
Docstrings
Docstrings provide a description of the function’s purpose and are considered good practice. They are written within triple quotes right after the function definition.
Example:
def even_odd(num):
"""Checks if a number is even or odd."""
if num % 2 == 0:
print("Even")
else:
print("Odd")
# Accessing docstring
print(even_odd.__doc__)
Output:
Checks if a number is even or odd.
Recursive Functions
A recursive function is one that calls itself. It is useful for problems that can be broken down into smaller instances of the same problem.
Example: Factorial Calculation:
def factorial(n):
if n == 0:
return 1
else:
return n * factorial(n - 1)
print(factorial(5)) # Output: 120
Output:
120
Passing by Reference and Value
In Python, when you pass an object to a function, you are passing a reference to the object, not a copy. So, changes made to the object inside the function will affect the original object.
Example (Passing a List by Reference):
def modify_list(lst):
lst[0] = 100
lst = [1, 2, 3]
modify_list(lst)
print(lst) # List is modified
Output:
[100, 2, 3]
Python def Keyword
The def keyword in Python is used to define functions, which are reusable blocks of code designed to perform specific tasks. It is used before a function name, allowing the creation of a user-defined function. The def keyword is essential for structuring Python code logically by grouping related statements under a function name.
Syntax of def in Python
def function_name():
# function statements
Purpose of def Keyword
In Python, def is not only for defining general functions but also for creating class methods and special member functions like __init__(). Using def helps with code reusability, letting developers encapsulate specific logic into functions instead of rewriting code repeatedly.
Applications of def in Python
Below are examples illustrating the various ways to use the def keyword in Python:
Defining a Simple Function with No Arguments
Example
def greet():
print("Hello, Python!")
greet()
Output:
Hello, Python!
Creating a Function for Subtraction
This function, subtract, calculates the difference between two numbers.
def subtract(x, y):
return x - y
result = subtract(15, 5)
print("The difference is:", result)
First Class functions in Python
In programming, first-class objects are treated uniformly, allowing them to be stored in data structures, passed as arguments, and used in control structures. A language that treats functions as first-class objects supports first-class functions. Python, as an example, treats functions as first-class citizens.
Properties of First-Class Functions
Functions are instances of the Object type.
Functions can be stored in variables.
Functions can be passed as parameters to other functions.
Functions can be returned from other functions.
Functions can be stored in data structures like lists or dictionaries.
This article explains how to assign a function to a variable in Python. By assigning a function to a variable, we can call that function using the variable name multiple times, enhancing code reusability.
Implementation
To assign a function to a variable, simply use the function name without parentheses (). If parentheses are used, it will execute the function immediately, returning None if no return value is provided.
Syntax:
def func():
# function body
variable = func # assigning function to variable
variable() # calling the function through variable
Example:
def greet():
print("Welcome to Python!")
# Assigning the function to a variable
message = greet
# Calling the function using the variable
message()
message()
Output:
Welcome to Python!
Welcome to Python!
Python User defined functions
A function is a set of instructions designed to take inputs, perform specific calculations, and return an output. The purpose of a function is to consolidate tasks that are commonly or repeatedly performed so that we can call the function instead of duplicating code. Python provides built-in functions, such as print(), but we can also define our own functions, known as user-defined functions.
Python User-defined Functions
All functions that we write are categorized as user-defined functions. Below are the steps for creating user-defined functions in Python:
Use the def keyword to define a function.
An indented block of statements follows the function name and arguments, forming the function body.
def function_name():
# statements
Example
# Defining a function
def show_message():
print("This is a custom function.")
# Calling the function
show_message()
Output:
This is a custom function.
Python Parameterized Function
A function can accept arguments or parameters as inputs within the parentheses following the function name.
Example:
def check_even_odd(num):
if num % 2 == 0:
print("Even")
else:
print("Odd")
# Calling the function
check_even_odd(4)
check_even_odd(7)
Output:
Even
Odd
Python Default Arguments
A default argument has a value that is assigned if no argument is provided when the function is called.
Example: Here, we call display_info() with a single argument.
# Function with a default argument
def display_info(name, age=25):
print("Name:", name)
print("Age:", age)
# Calling the function
display_info("Alice")
Output:
Name: Alice
Age: 25
Python Keyword Arguments
Keyword arguments allow the caller to specify argument names with values, so the order of arguments does not need to be memorized.
We can have variable numbers of arguments, both positional (*args) and keyworded (**kwargs).
Example:
def show_args(*args):
for arg in args:
print(arg)
def show_kwargs(**kwargs):
for key, value in kwargs.items():
print(f"{key} == {value}")
# Calling the functions
print("Result of *args:")
show_args("Hello", "This", "is", "Python")
print("\nResult of **kwargs:")
show_kwargs(name="John", age=30, country="USA")
Output:
Result of *args:
Hello
This
is
Python
Result of **kwargs:
name == John
age == 30
country == USA
Pass by Reference or Pass by Value in Python
In Python, every variable name is a reference. When a variable is passed to a function, a new reference to the object is created. We can use Python’s id() function to confirm this.
Value outside function: 42 id: 139865623584656
Value inside function: 42 id: 139865623584656
Python Function with Return Value
Sometimes a function needs to return a value to the caller, which can be achieved using the return statement.
Example:
def sum_values(a, b):
return a + b
def check_true(condition):
return bool(condition)
# Calling functions
result = sum_values(5, 3)
print("Result of sum function:", result)
result = check_true(3 > 1)
print("Result of check_true function:", result)
Output:
Result of sum function: 8
Result of check_true function: True
Defining a Python function at runtime
In Python, we can create a function at runtime and execute it using FunctionType(). This approach involves importing the types module, using compile() with exec mode, and then defining the function at runtime with FunctionType().
Example 1: Function to Print “Hello, Python!
# Importing the module
from types import FunctionType
# Defining the function during runtime
code_obj = compile('def hello_func(): return "Hello, Python!"', "<string>", "exec")
runtime_func = FunctionType(code_obj.co_consts[0], globals(), "hello_func")
# Calling the function
print(runtime_func())
Output:
Hello, Python!
Python Built in Functions
Python is versatile in handling system scripting, software development, and web development (server-side). It can manage workflows, connect to databases, modify files, handle big data, and perform advanced mathematical tasks.
The syntax in Python is straightforward and English-like, allowing developers to write concise code compared to other languages. Python’s interpreter system enables code execution immediately upon writing. Python also provides numerous built-in functions that make coding simpler.
In this guide, you will discover Python’s built-in functions, exploring their purposes and applications. Here’s a rundown of some of the most commonly used built-in functions in Python:
abs(): Returns the absolute value of a number.
abs(-5)
# Output: 5
aiter(): Creates an asynchronous iterator from an asynchronous iterable.
# Example: (used within an async function)
async for item in aiter(async_iterable):
print(item)
all(): Returns True if all elements in an iterable are True.
all([True, True, False])
# Output: False
any(): Returns True if any element in an iterable is True.
any([False, True, False])
# Output: True
anext(): Retrieves the next item from an asynchronous iterator.
# Example: (used within an async function)
await anext(async_iterator)
ascii(): Returns a string with only ASCII characters, escaping non-ASCII characters.
ascii("éclair")
# Output: "'\\xe9clair'"
bin(): Converts an integer to a binary string.
bin(10)
# Output: '0b1010'
bool(): Converts a value to a Boolean (True or False).
bool(0)
# Output: False
breakpoint(): Invokes a debugger at the call site.
breakpoint()
# (opens debugger)
bytearray(): Returns a mutable byte array from a sequence of bytes.
In Python, loops are essential structures used to execute a block of code repeatedly. There are two main types of loops: the for loop and the while loop. Both loops serve similar functions but differ in their syntax and how they check conditions. This article explains how these loops work with examples.
While Loop in Python
A while loop is used to repeatedly execute a block of code as long as a specified condition remains true. When the condition becomes false, the loop stops, and execution moves to the next line after the loop.
Using the else Clause with a While Loop: You can attach an else statement to a while loop. This block will execute after the loop completes, unless the loop is exited prematurely with a break or an exception is raised.
Example:
counter = 0
while counter < 3:
counter += 1
print("Hello World")
else:
print("Loop has finished")
Output:
Hello World
Hello World
Hello World
Loop has finished
Infinite While Loop: An infinite loop occurs when the condition always evaluates to true, resulting in continuous execution. Here’s an example:
counter = 0
while counter == 0:
print("This will run forever!")
Output:
Amount: $150.75
For Loop in Python
The for loop in Python is used to iterate over a sequence such as a list, tuple, or string. It is ideal for traversing through elements in these data structures.
Syntax:
for element in sequence:
statement(s)
Example:
n = 5
for i in range(n):
print(i)
Output:
0
1
2
3
4
Iterating Over Different Data Structures: The for loop can also be used to iterate over various data structures, like lists, tuples, and strings.
# List iteration
fruits = ["apple", "banana", "cherry"]
for fruit in fruits:
print(fruit)
# Tuple iteration
colors = ("red", "green", "blue")
for color in colors:
print(color)
# String iteration
word = "Python"
for letter in word:
print(letter)
# Dictionary iteration
student_ages = {'Alice': 23, 'Bob': 25, 'Charlie': 22}
for name, age in student_ages.items():
print(f"{name}: {age}")
Output:
apple
banana
cherry
red
green
blue
P
y
t
h
o
n
Alice: 23
Bob: 25
Charlie: 22
Using the Index in a For Loop: You can also iterate over the index of a sequence using range(len(sequence)). This allows access to each element’s index in the loop.
fruits = ["apple", "banana", "cherry"]
for index in range(len(fruits)):
print(f"Index {index}: {fruits[index]}")
Output:
Index 0: apple
Index 1: banana
Index 2: cherry
Using the else Clause with a For Loop: As with the while loop, the else block in a for loop executes when the loop completes normally (i.e., without interruption by break).
Example:
fruits = ["apple", "banana", "cherry"]
for fruit in fruits:
print(fruit)
else:
print("All fruits have been printed.")
Output:
apple
banana
cherry
All fruits have been printed.
Nested Loops in Python
In Python, you can place one loop inside another, which is referred to as a nested loop. A for loop can be nested inside another for loop, and similarly, a while loop can be nested inside another while loop.
Nested For Loop Syntax:
for outer_var in sequence:
for inner_var in sequence:
statement(s)
Nested While Loop Syntax:
while condition:
while condition:
statement(s)
statement(s)
Example of a nested for loop creating a pattern:
for i in range(1, 5):
for j in range(i):
print(i, end=' ')
print()
Output:
1
2 2
3 3 3
4 4 4 4
Loop Control Statements
Loop control statements modify the flow of execution within loops. Python has three primary control statements:
1. continue: Skips the current iteration and moves to the next iteration. 2. break: Exits the loop immediately. 3. pass: Does nothing and moves to the next iteration.
1. Using continue: This code skips the iteration when encountering a specific letter:
for letter in "programming":
if letter == "r":
continue
print(letter)
Output:
p
o
g
a
m
m
i
n
g
Output:
p
Using pass: The pass statement is used when you want an empty loop body or placeholder for future code.
for letter in "programming":
pass
print("Loop completed.")
Output:
Loop completed.
Using break: The break statement is used to exit a loop immediately when a specified condition is met. It terminates the loop’s execution and transfers control to the statement following the loop.
Example with break: This code stops the loop when a specific letter is encountered.
# Example of break
for letter in "python":
if letter == "h":
break # Exit the loop when the letter 'h' is encountered
print("Current letter:", letter)
Output:
Current letter: p
Current letter: y
Current letter: t
How the for Loop Works Internally
Python’s for loop iterates over iterable objects such as lists, sets, and dictionaries. Here’s how it works:
1. Python creates an iterator object from the iterable. 2. It repeatedly fetches the next item from the iterator using the next() function until it raises a StopIteration exception.
Example of manually iterating through a list using an iterator:
fruits = ["apple", "orange", "kiwi"]
iterator = iter(fruits)
while True:
try:
fruit = next(iterator)
print(fruit)
except StopIteration:
break
Conditional statements control the flow of your Python code by executing different actions based on specified conditions. They’re fundamental for building decision-making in programs, making them crucial for writing logical and efficient Python code.
Types of Conditional Statements in Python
1. if Statement
The if statement executes a block of code only if a specified condition is True. If the condition is False, the block will not execute.
Syntax of the if statement:
if condition:
# Code to execute if condition is true
Example:
age = 18
if age >= 18:
print("You are eligible to vote.")
Output:
You are eligible to vote.
2. if-else Statement
The if-else statement expands on the if statement by adding an alternate block of code if the condition is False.
Syntax of if-else:
if condition:
# Code if condition is true
else:
# Code if condition is false
Example:
score = 50
if score >= 60:
print("Passed")
else:
print("Failed")
Output:
Failed
3. Nested if-else Statement
A nested if-else is when an if-else block is placed inside another if-else block. This allows you to create more complex conditions.
Example:
number = 15
if number > 10:
if number < 20:
print("Number is between 10 and 20")
else:
print("Number is 20 or more")
else:
print("Number is 10 or less")
Output:
Number is between 10 and 20
4. if-elif-else Statement
The if-elif-else ladder provides multiple conditions to check sequentially. Once a condition is found True, the corresponding block is executed, and the rest are bypassed.
Example:
day = "Wednesday"
if day == "Monday":
print("Start of the work week")
elif day == "Wednesday":
print("Midweek")
elif day == "Friday":
print("End of the work week")
else:
print("It's the weekend!")
Output:
Midweek
5. Ternary Expression (Conditional Expression)
The ternary expression allows you to write a conditional statement in a single line. It’s ideal for simple conditions that can be concisely expressed.
Syntax:
value_if_true if condition else value_if_false
Example:
x, y = 5, 10
result = "x is greater" if x > y else "y is greater or equal"
print(result)
In Python, operators are symbols that perform specific computations or operations on variables and values, acting as essential tools in programming.
Operator: Special symbol used to perform operations, like +, -, *, /.
Operand: Value on which the operator acts.
Types of Operators in Python
Arithmetic
Operators
Comparison
Operators
Logical Operators
Bitwise Operators
Assignment Operators
Identity and Membership Operators
Arithmetic Operators in Python
These operators help perform basic arithmetic tasks such as addition, subtraction, multiplication, and division.
In Python 3.x, the division operator / returns a floating-point result, while // returns a floored integer result.
Operator
Description
Syntax
+
Adds two values
x + y
–
Subtracts second value from the first
x - y
*
Multiplies two values
x * y
/
Divides first value by the second (float)
x / y
//
Divides first value by second (floor)
x // y
%
Returns remainder
x % y
**
Raises first value to power of second
x ** y
Example: Performing Arithmetic Operations
a = 12
b = 5
sum_val = a + b
diff_val = a - b
product_val = a * b
quotient_val = a / b
floor_div = a // b
modulus_val = a % b
power_val = a ** b
print("Sum:", sum_val)
print("Difference:", diff_val)
print("Product:", product_val)
print("Quotient (float):", quotient_val)
print("Quotient (floor):", floor_div)
print("Modulus:", modulus_val)
print("Power:", power_val)
Comparison operators compare values and return True or False based on the condition.
Operator
Description
Syntax
>
True if left value is greater
x > y
<
True if left value is smaller
x < y
==
True if values are equal
x == y
!=
True if values are not equal
x != y
>=
True if left value is greater or equal
x >= y
<=
True if left value is smaller or equal
x <= y
Example: Using Comparison Operators
x = 15
y = 20
print("Is x greater than y?", x > y)
print("Is x less than y?", x < y)
print("Is x equal to y?", x == y)
print("Is x not equal to y?", x != y)
print("Is x greater than or equal to y?", x >= y)
print("Is x less than or equal to y?", x <= y)
Output:
Is x greater than y? False
Is x less than y? True
Is x equal to y? False
Is x not equal to y? True
Is x greater than or equal to y? False
Is x less than or equal to y? True
a = True
b = False
print("a and b:", a and b)
print("a or b:", a or b)
print("not a:", not a)
Output:
a and b: False
a or b: True
not a: False
Bitwise Operators
These operators perform bit-by-bit operations.
Operator
Description
Syntax
&
Bitwise AND
x & y
|
Bitwise OR
`x
~
Bitwise NOT
~x
^
Bitwise XOR
x ^ y
>>
Bitwise right shift
x >> y
<<
Bitwise left shift
x << y
Example: Applying Bitwise Operations
a = 5
b = 3
print("a & b:", a & b)
print("a | b:", a | b)
print("~a:", ~a)
print("a ^ b:", a ^ b)
print("a >> 1:", a >> 1)
print("a << 1:", a << 1)
Output:
a & b: 1
a | b: 7
~a: -6
a ^ b: 6
a >> 1: 2
a << 1: 10
Assignment Operators
These operators assign values to variables, often combining an arithmetic operation.
Operator
Description
Syntax
=
Simple assignment
x = y
+=
Add and assign
x += y
-=
Subtract and assign
x -= y
*=
Multiply and assign
x *= y
/=
Divide and assign (float)
x /= y
//=
Divide and assign (floor)
x //= y
%=
Modulus and assign
x %= y
**=
Exponent and assign
x **= y
&=
Bitwise AND and assign
x &= y
|=
Bitwise OR and assign
`x
^=
Bitwise XOR and assign
x ^= y
>>=
Right shift and assign
x >>= y
<<=
Left shift and assign
x <<= y
Example: Using Assignment Operators
x = 10
y = 3
x += y
print("After += :", x)
x -= y
print("After -= :", x)
x *= y
print("After *= :", x)
x //= y
print("After //= :", x)
Output:
After += : 13
After -= : 10
After *= : 30
After //= : 10
Identity Operators
Identity operators check if two variables point to the same object in memory.
Operator
Description
is
True if both are identical
is not
True if they are not
Example: Checking Object Identity
a = 100
b = 100
c = a
print("a is b:", a is b)
print("a is c:", a is c)
Output:
a is b: True
a is c: True
Membership Operators
Membership operators check if a value is in a sequence, like a list or a string.
Operator
Description
in
True if value is in sequence
not in
True if value is not in sequence
Example: Checking Membership in a List
fruits = ["apple", "banana", "cherry"]
fruit = "banana"
print("Is 'banana' in fruits?", fruit in fruits)
print("Is 'orange' not in fruits?", "orange" not in fruits)
Output:
Is 'banana' in fruits? True
Is 'orange' not in fruits? True
Difference between / vs. // operator in Python
In this article, we’ll explore the difference between the division operator / and the floor division operator // in Python.
Standard Division in Python (/ Operator)
The division operator / in Python performs classic division, yielding a floating-point result, even when dividing two integers. It calculates the precise value by including the decimal part.
Example
# Division operator
result = 9 / 4
print(result)
Output:
2.25
Floor Division in Python (// Operator)
The floor division operator //, on the other hand, performs division and rounds down to the nearest integer. This means that it removes the decimal part entirely, always resulting in an integer. Floor division can be helpful when you need integer results without rounding up, as it returns the largest whole number less than or equal to the result.
Using these operators depends on the need for precision in your calculation: / is used when you need the exact decimal, while // is for cases where only the integer part is required.
Python Star or Asterisk operator ( * )
Uses of the Asterisk (*) in Python:
1. Multiplication: The asterisk (*) can act as a multiplication operator when used between two numbers.
# Using the asterisk for multiplication
product = 8 * 6
print(product)
Output:
48
2. Exponentiation: Two asterisks (**) are used to raise a number to the power of another, representing exponentiation.
x = 4
y = 2
# Using double asterisks for exponentiation
power_result = x ** y
print(power_result)
Output:
16
3. List Multiplication: By placing an asterisk next to a list, we can repeat the elements in that list a specified number of times.
# Multiplying a list
words = ['hello'] * 4
print(words)
Output:
['hello', 'hello', 'hello', 'hello']
4. Unpacking a List for Function Arguments: Using an asterisk in front of a list while passing it to a function allows us to unpack its elements as separate positional arguments.
days = ['Monday', 'Tuesday', 'Wednesday', 'Thursday']
# Without unpacking
print(' '.join(days))
# Using asterisk to unpack
print(*days)
5. Passing Multiple Positional Arguments with *args: The asterisk allows a function to accept a variable number of positional arguments. Here, *args can be used to accept any number of arguments and handle them as a tuple.
# Function using *args
def add_numbers(*args):
return sum(args)
print(add_numbers(2, 8, 15, 5))
Output:
30
6. Passing Multiple Keyword Arguments with **kwargs: Two asterisks (**) allow a function to accept a variable number of keyword arguments, making them accessible as a dictionary.
# Function using **kwargs
def describe_food(**kwargs):
for item, description in kwargs.items():
print(f"{description} is a {item}")
describe_food(fruit='banana', vegetable='carrot', snack='chips')
Output:
banana is a fruit
carrot is a vegetable
chips is a snack
7. Unpacking a Dictionary for Function Arguments Using **: The double asterisk can also unpack a dictionary’s key-value pairs into function arguments, as shown here.
# Function using **kwargs with dictionary unpacking
def display_food_info(**kwargs):
for item, description in kwargs.items():
print(f"{description} is a {item}")
food_info = {'fruit': 'apple', 'vegetable': 'spinach', 'grain': 'rice'}
# Unpacking dictionary
display_food_info(**food_info)
Output
apple is a fruit
spinach is a vegetable
rice is a grain
Division Operators in Python
Python provides two main types of division operators for handling division between numbers:
2. Integer (Floor) Division (//) : The // operator performs integer division, also known as floor division. If one of the operands is a float, the result will be a float. When applied to a negative number, it returns the closest integer less than or equal to the quotient.
Python does not support division for boolean values. Attempting to divide boolean values will result in a TypeError. However, you can overload the division operator for a custom class to define specific behavior.
Example: Custom Division Operator Overload with Boolean Values
In the following code, a custom class CustomBool is created. The division operator (/) is overloaded to perform a logical operation between two boolean values.
class CustomBool:
def __init__(self, value):
self.value = bool(value)
def __truediv__(self, other):
# Logical AND operation between two boolean values
return CustomBool(self.value and other.value)
# Create instances
a = CustomBool(True)
b = CustomBool(False)
result = a / b # result.value will be False
print(result.value)
Output:
False
Modulo operator (%) in Python
In Python, the % symbol represents the modulo operator, which calculates the remainder of a division operation. This is not to be confused with the “percent” symbol. When you see a % b, it means that a is divided by b, and the result is the remainder of this division.
What is the Python Modulo Operator?
The Python modulo operator, %, provides the remainder from the division of two numbers and is part of the arithmetic operations family alongside +, -, /, *, **, and //. Unlike many programming languages, the modulo operator in Python works with both integers and floats.
Syntax:
result = a % b
Example:
# Example with integer operands
num1 = 16
num2 = 7
result = num1 % num2
print(num1, "mod", num2, "=", result, sep=" ")
Output:
16 mod 7 = 2
Modulo Operator with Floating-Point Numbers and Negative Values
The modulo operator can handle floating-point numbers, and when negative numbers are involved, the result takes on the sign of the divisor.
# Example with floating-point operands and a negative divisor
float_num1 = 22.5
float_num2 = -8.0
result = float_num1 % float_num2
print(float_num1, "mod", float_num2, "=", result, sep=" ")
Output:
22.5 mod -8.0 = -5.5
Practical Example Using the Modulo Operator
Suppose we want to compute the remainder when dividing each number from 1 to n by a given divisor k. This example demonstrates how to calculate and display these remainders:
# Define function to calculate remainders from 1 to n divided by k
def calculate_remainders(n, k):
for i in range(1, n + 1):
remainder = i % k
print(i, "mod", k, "=", remainder, sep=" ")
# Calling the function
n = 7
k = 4
calculate_remainders(n, k)
Output:
1 mod 4 = 1
2 mod 4 = 2
3 mod 4 = 3
4 mod 4 = 0
5 mod 4 = 1
6 mod 4 = 2
7 mod 4 = 3
Handling ZeroDivisionError in Python
If the divisor (right operand) is zero, Python raises a ZeroDivisionError since division by zero is undefined. The example below shows how to handle this exception gracefully.
# Example with exception handling for division by zero
num1 = 20
num2 = 0
try:
print(num1, 'mod', num2, '=', num1 % num2, sep=" ")
except ZeroDivisionError:
print("Error: Division by zero is not allowed. Please use a non-zero divisor.")
Output:
Error: Division by zero is not allowed. Please use a non-zero divisor.
Common Applications of the Modulo Operator in Python
1. Idnentifying Even or Odd Numbers
# Check if a number is even or odd
num = 27
if num % 2 == 0:
print("Even")
else:
print("Odd")
Output:
Odd
2. Cycling Through List Indices (e.g., Circular Buffer)
# Cycling through indices with modulo
indices = [0, 1, 2, 3]
current_position = 6
index = current_position % len(indices)
print(indices[index])
Output:
2
3. Checking Divisibility
# Check if a number is divisible by another
num = 45
if num % 9 == 0:
print("Divisible by 9")
Output:
Divisible by 9
4. Time Calculations (e.g., Converting Minutes to Hours and Minutes)
# Convert total minutes into hours and minutes
total_minutes = 150
hours = total_minutes // 60
minutes = total_minutes % 60
print(f"{hours} hours and {minutes} minutes")
Output:
2 hours and 30 minutes
Modulo Operator and Negative Numbers
When working with negative numbers, the result of a % b in Python has the same sign as b.
# Modulo with positive and negative values
result = 10 % -4
print(result) # Output: -2
result = -10 % 4
print(result) # Output: 2
Output:
-2
2
Modulo with Floating-Point Numbers
Python’s modulo operator also works with floats and returns a remainder that may also be a float.
# Example with floating-point values
result = 13.5 % 2.5
print(result)
Output:
0.5
Python OR Operator
The OR operator in Python evaluates multiple boolean expressions and returns True if at least one expression is True. If all expressions evaluate to False, then it returns False.
Example: OR Operator with Boolean Expressions
# Example with Boolean expressions
condition1 = 4 > 5
condition2 = 4 < 5
print('condition1:', condition1)
print('condition2:', condition2)
# OR operator
result = condition1 or condition2
print("OR operator result:", result)
Output:
condition1: False
condition2: True
OR operator result: True
Example: Using Python OR Operator in an if Statement
# OR operator in an if statement
def check_number(n):
if n % 4 == 0 or n % 6 == 0:
print(f"{n} is a multiple of 4 or 6")
else:
print(f"{n} is not a multiple of 4 or 6")
# Driver code
check_number(12)
check_number(5)
check_number(24)
Output:
12 is a multiple of 4 or 6
5 is not a multiple of 4 or 6
24 is a multiple of 4 or 6
Python OR Operator – Short-Circuiting
The Python OR operator stops evaluating as soon as it encounters a True condition. This is known as short-circuiting, where it doesn’t check further expressions once a True is found.
# Short circuiting with OR operator
def return_true():
print("Executing return_true()")
return True
def return_false():
print("Executing return_false()")
return False
# Case 1: First operand is True
case1 = return_true() or return_false()
print("Result of Case 1:", case1)
print()
# Case 2: Both operands are True
case2 = return_true() or return_true()
print("Result of Case 2:", case2)
print()
# Case 3: Both operands are False
case3 = return_false() or return_false()
print("Result of Case 3:", case3)
print()
# Case 4: First operand is False, second is True
case4 = return_false() or return_true()
print("Result of Case 4:", case4)
Output
Executing return_true()
Result of Case 1: True
Executing return_true()
Result of Case 2: True
Executing return_false()
Executing return_false()
Result of Case 3: False
Executing return_false()
Executing return_true()
Result of Case 4: True
Walrus Operator in Python 3.8
The Walrus Operator in Python
The Walrus Operator (:=), introduced in Python 3.8, allows you to assign a value to a variable as part of an expression. This operator is particularly useful in cases where a value needs to be used repeatedly within the same expression, such as within loops or conditional statements, without having to calculate it multiple times.
Basic Syntax and Usage
The syntax for the Walrus Operator is variable := expression. This allows you to both evaluate and assign a value to variable within the context of an expression.
Example: Using the Walrus Operator in a while Loop
Merging and Updating Dictionary Operators in Python
Traditional Method: Using update()
The update() method allows you to merge one dictionary into another. It modifies the first dictionary in-place by adding or updating keys from the second dictionary. However, it does not create a new dictionary, nor does it return any value.
Example: Merging Dictionaries with update()
# Define two dictionaries
dict1 = {'x': 100, 'y': 200, 'z': 300}
dict2 = {'y': 250, 'z': 350, 'w': 400}
# Update dict1 with dict2 and return None
result = dict1.update(dict2)
print("Return value from update():", result)
# dict1 is modified
print("Updated dict1:", dict1)
print("Unchanged dict2:", dict2)
In Python, the ** operator can be used to unpack dictionaries, making it possible to merge multiple dictionaries in a single expression. This approach creates a new dictionary, leaving the original dictionaries unaltered.
Example: Merging Dictionaries Using ** Unpacking
# Define two dictionaries
dict1 = {'p': 15, 'q': 25, 'r': 35}
dict2 = {'s': 45, 'r': 55, 'q': 65}
# Create a new dictionary by unpacking dict1 and dict2
merged_dict = {**dict1, **dict2}
print("Original dict1:", dict1)
print("Original dict2:", dict2)
print("Merged dictionary:", merged_dict)
New Method in Python 3.9+: Using | and |= Operators
Python 3.9 introduces the dictionary merge (|) and update (|=) operators. The merge operator (|) creates a new dictionary by combining the contents of the two dictionaries, while the update operator (|=) modifies the dictionary on the left in place by adding or updating keys from the dictionary on the right.
Example: Merging with | and Updating with |=
# Define two dictionaries
dict1 = {'m': 5, 'n': 10, 'o': 15}
dict2 = {'p': 20, 'o': 25, 'n': 30}
# Merge using |
merged_dict1 = dict1 | dict2
print("Merging dict1 with dict2 (dict1 | dict2):")
print(merged_dict1)
merged_dict2 = dict2 | dict1
print("\nMerging dict2 with dict1 (dict2 | dict1):")
print(merged_dict2)
# Update dict1 with dict2 using |=
dict1 |= dict2
print("\nUpdating dict1 with dict2 using |=:")
print("Updated dict1:", dict1)
print("Unchanged dict2:", dict2)
In programming, checking multiple conditions is common. Instead of writing separate conditions with logical operators, Python allows you to combine comparisons into a single expression through chaining. This feature simplifies expressions like:
if a < b and b < c:
{...}
In Python, you can simplify this with comparison chaining:
if a < b < c:
{...}
Python supports chaining comparisons using operators like >, <, ==, >=, <=, !=, is, is not, in, and not in.
Comparison chaining in Python follows typical mathematical notation, making it intuitive. For example, x < y <= z is equivalent to x < y and y <= z, but with a key difference: each expression is evaluated only once. So, if x < y is false, y and z won’t be evaluated, improving performance.
This chaining doesn’t imply comparisons between non-adjacent elements; for instance, a < b > c is valid and checks that a < b and b > c but does not relate a and c.
Example of Comparison Operator Chaining
# Demonstrating chaining of comparison operators
x = 7
print(1 < x < 15) # Checks if x is between 1 and 15
print(15 < x < 20) # Checks if x is between 15 and 20
print(x < 15 < x * 2 < 200) # Checks multiple conditions on x
print(10 >= x <= 10) # Checks if x is equal to or less than 10
print(7 == x > 4) # Checks if x is both 7 and greater than 4
Output:
True
False
True
True
True
Importance of Parentheses in Logical Expressions
When combining and and or with comparison chaining, parentheses are essential for clarifying precedence. Without them, expressions may not evaluate as expected, since and has higher precedence than or.
p = 3
q = 7
r = 12
# Example with no parentheses
if p < q or q < r and r < p:
print("This might not be printed as expected")
# Using parentheses to clarify precedence
if (p < q or q < r) and r < p:
print("This will be printed as expected")
Output:
This might not be printed as expected
Python Membership Operators
Membership operators check if a value is present in a sequence (e.g., strings, lists, dictionaries).
Membership Operators
Operator
Description
Syntax
in
Returns True if the value exists in a sequence, else False
value in sequence
not in
Returns True if the value does not exist in a sequence, else False
value not in sequence
Python in Operator
The in operator tests whether a character, element, or substring exists within a sequence, returning True if it’s found, and False otherwise.
Example :
# Check if 'z' exists in a string (case-sensitive check)
'z' in 'PythonProgramming'
# Output: False
Python is not Operator
The is not operator returns True if variables refer to different memory locations.
Example:
# Variables
num_a, num_b = 15, 15
list_a = [1, 2]
list_b = [1, 2]
list_c = list_a
# Testing identity
print(num_a is not num_b) # False
print(list_a is not list_b) # True
print(list_a is not list_c) # False