Python Programming
Python Programming
Python is a popular programming language. It was created by Guido van Rossum, and
released in 1991.
It is used for:
Need of Python:
Python can be used on a server to create web applications.
Python can be used alongside software to create workflows.
Python can connect to database systems. It can also read and modify files.
Python can be used to handle big data and perform complex mathematics.
Python can be used for rapid prototyping, or for production-ready software
development.
Importance of Python:
Python works on different platforms (Windows, Mac, Linux, Raspberry Pi, etc).
Python has a simple syntax similar to the English language.
Python has syntax that allows developers to write programs with fewer lines than
some other programming languages.
Python runs on an interpreter system, meaning that code can be executed as
soon as it is written. This means that prototyping can be very quick.
Python can be treated in a procedural way, an object-oriented way or a functional
way.
Advantages of Python:
Ease of Learning and Use:
Python's simple, readable syntax, resembling natural language, makes it relatively
easy for beginners to learn and understand.
Extensive Libraries and Frameworks: A vast ecosystem of libraries (e.g., NumPy,
Pandas, Django, Flask) and frameworks simplifies development for various domains
like data science, web development, and machine learning.
Versatility and Wide Applicability:
Python can be used for a broad range of applications, including web development,
data analysis, artificial intelligence, scientific computing, automation, and scripting.
Large and Active Community:
Python boasts a large and supportive community, providing ample resources,
documentation, and assistance for developers.
Improved Productivity:
Its concise syntax and extensive libraries allow developers to write less code and
achieve more, leading to increased productivity.
Portability:
Python code can run on various platforms (Windows, macOS, Linux) with minimal or
no modifications.
Disadvantages of Python:
Speed Limitations:
As an interpreted language, Python can be slower than compiled languages like C++
or Java for computationally intensive tasks, especially those requiring high
performance.
Higher Memory Consumption:
Python's automatic memory management and dynamic typing can lead to higher
memory consumption compared to languages with more explicit memory control.
Weak in Mobile Computing:
Python is not widely used for mobile application development due to performance and
memory considerations, although frameworks like Kivy exist.
Global Interpreter Lock (GIL):
The GIL restricts true parallel execution of multiple threads within a single Python
process, potentially limiting performance on multi-core processors for CPU-bound
tasks.
Runtime Errors:
Python's dynamic typing can lead to runtime errors that might not be caught during
compilation, requiring thorough testing.
Database Access Layer:
While Python can connect to databases, its native database access layer can be
considered less mature or feature-rich compared to technologies like JDBC or ODBC
for complex enterprise-level interactions.
REPL shell in Python with example
A REPL (Read-Evaluate-Print Loop) is an interactive shell environment that executes a
single line of code at a time and immediately shows the result
. It is a powerful tool for testing small snippets of code, debugging, and exploring
Python's features and libraries.
3. You will see the Python prompt, which looks like this: >>> .
To exit the REPL: Type exit() or quit() and press Enter . On Windows, you can also
press Ctrl+Z followed by Enter , and on macOS or Linux, use Ctrl+D .
text
>>> 2 + 2
4.
Explanation:
Loop: The prompt >>> reappears, waiting for your next command.
text
>>> greeting = "Hello, REPL!"
>>> greeting
'Hello, REPL!'
Explanation:
You can declare and assign variables, and they are immediately stored in memory for
the current session.
Typing the variable's name and pressing Enter prints its value.
text
>>> def multiply(a, b):
... return a * b
...
>>> multiply(5, 6)
30
Explanation:
For multi-line statements like functions or loops, the prompt changes to ... to indicate
that Python is expecting more code.
text
>>> import math
>>> math.sqrt(16)
4.0
>>> math.pi
3.141592653589793
Explanation:
Modules can be imported and their functions and attributes can be used immediately.
text
>>> my_list = [10, 20, 30, 40]
>>> my_list.append(50)
>>> my_list
[10, 20, 30, 40, 50]
Explanation:
You can interactively modify data structures like lists and dictionaries and see the
changes reflected immediately.
REPL features:
Command History: You can press the up and down arrow keys to cycle through
previously executed commands.
Tab Completion: If you type the beginning of a variable, function, or module name and
press the Tab key, the REPL will try to complete the name for you. Pressing Tab again
will show all possible options.
help() function: To get instant documentation for any function, type help() followed by
the item you want to inspect. For example, help(str) .
Steps
1. Save your file: Create a file with a .py extension (e.g., hello.py ).
2. Open a terminal:
sh
cd C:\Users\YourName\Documents\python_projects
4. Execute the script: Run the script by typing python followed by the filename. On some
systems, especially Linux and macOS, you may need to use python3 .
sh
python hello.py
python
print("Hello, world!")
2. Click the "Run" button: Click the small green "play" arrow in the top-right corner of the
editor. This will execute the script in the integrated terminal.
3. Use the command palette: Alternatively, you can open the Command Palette
(Ctrl+Shift+P) and select Run Python File in Terminal .
1. Open a terminal and type python (or python3 ) and press Enter.
python
>>> print("Hello from the REPL!")
Hello from the REPL!
>>> 2 + 5
7
Variables in python:
In Python, a variable is a symbolic name that acts as a container for storing data.
Variables allow you to label data with a descriptive name, which makes your code
easier to read, maintain, and reuse.
Example
python
# An integer variable
age = 30
# A string variable
name = "Jane Doe"
# A floating-point variable
price = 19.99
# A boolean variable
is_active = True
# You can print variables to see their values
print(name)
print(age)
python
x=5 # x is an integer
x = "Hello" # Now x is a string
python
a = [1, 2, 3]
b=a # b references the same list object as a
b.append(4)
print(a) # Output: [1, 2, 3, 4]
No explicit declaration: You do not need to use special keywords to declare a variable.
The act of assigning a value creates the variable.
It can only contain alpha-numeric characters and underscores ( A-z , 0-9 , and _ ).
Good practices:
dict : An unordered collection of key-value pairs ( student = {"name": "Rohit", "age": 36} ).
Variable scope :
The scope of a variable determines where in your program it can be accessed.
Local variables are defined inside a function and are only accessible within that
function.
Global variables are defined outside of any function and can be accessed from
anywhere in the program.
Assignments in python:
In Python, an assignment is the process of creating a variable and giving it a value. The
equal sign ( = ) is the main assignment operator, which binds the variable name on its
left to the data object on its right.
Basic assignment:
The most common form of assignment is giving a single variable a single value.
Example:
python
# A new variable `name` is assigned the string "Alice"
name = "Alice"
print(name) # Output: Alice
Multiple assignments:
Python offers several shortcuts for assigning values to multiple variables
simultaneously.
Example:
python
x = y = z = 100
print(x, y, z) # Output: 100 100 100
Example:
python
a, b, c = 1, 2, 3
print(a, b, c) # Output: 1 2 3
Augmented assignments:
These are a shorthand for performing an operation on a variable and then assigning the
result back to that variable.
Example:
python
counter = 1
counter += 1
print(counter) # Output: 2
total = 100
total -= 25
print(total) # Output: 75
Use code with caution.
Case Sensitivity: Variable names are case-sensitive, so myVar and myvar are treated
as two different variables.
Object References: In Python, a variable is not a container for data but a reference to a
data object stored in memory. When you assign y = x , both variables reference
the same object. Reassigning x to a new value will not change y , but modifying a
mutable object (like a list) through one variable will be reflected when accessing it with
the other.
Keywords:
Keywords in Python are reserved words that hold special meaning to the
Python interpreter. These words are fundamental to the syntax and structure
of the language, and therefore, they cannot be used as identifiers (names for
variables, functions, classes, or other user-defined entities).
Input:
The input() function prompts the user for text and returns it as a string.
Syntax: input(prompt)
The prompt is an optional string that is displayed to the user before they enter their
input.
# The input() function displays a message and waits for user input
name = input("Please enter your name: ")
print("Hello,", name)
Output:
The print() function displays information to the console.
sep (optional): A string to separate the values. The default is a space ( ' ' ).
end (optional): A string to append at the end of the output. The default is a newline
character ( '\n' ).
x = 10
y = 20
print("The sum of x and y is:", x + y)
Example 2: Customizing output
Use the optional sep and end arguments to format the output.
A combined example:
Here is a complete program that uses both input and output to interact with the user.
Indentation :
Indentation in Python refers to the leading whitespace (spaces or tabs) at the
beginning of a line of code. Unlike many other programming languages that
use curly braces or other delimiters to define code blocks, Python uses
indentation as a fundamental part of its syntax to indicate the structure and
scope of code blocks.
Python uses various types of operators to perform operations on values and variables.
These include:
Comparison (or relational) operators to compare values and return True or False ,
including == , != , > , < , >= and <= .
Identity operators ( is , is not ) to check if variables refer to the same object in memory.
Arithmetic expressions
Arithmetic expressions perform mathematical calculations and evaluate to a numerical
value.
Addition ( + ): 5 + 3 results in 8 .
Multiplication ( * ): y * 2 results in 10 if y is 5 .
Equal to ( == ): x == 10 is True if x is 10 .
Greater than or equal to ( >= ): age >= 18 might be used to check if a person is old
enough.
Logical expressions
Logical expressions combine two or more relational or boolean expressions using
logical operators ( and , or , not ) and produce a boolean result.
and : x < 5 and x < 10 is True only if both conditions are met.
not : not(x < 5 and x < 10) inverts the result of the expression inside the parentheses.
Comprehension expressions
This family of expressions offers a concise way to build data structures like lists, sets,
and dictionaries from an iterable.
Integral expressions: These produce integer results, even if the operands are not all
integers, due to automatic type conversion.
Bitwise expressions: Used for low-level manipulation of data at the bit level.
if condition:
# Code to execute
if...else statement: Provides an alternative block for when the condition is False .
if condition:
# Code if True
else:
# Code if False
if...elif...else statement: Checks multiple conditions in order, executing the first one that
is True .
if condition1:
# Code if condition1 is True
elif condition2:
# Code if condition2 is True
else:
# Code if none are True
Nested if statements: Allows for placing if statements within other conditional blocks
for more complex logic.
print("Loop finished.")
Explanation:
An infinite while True loop is initiated.
Inside the loop, the count is printed and incremented.
An if statement checks if count is greater than or equal to 5.
If the condition in the if statement is met, a message is printed, and the break statement
is executed, causing the while loop to terminate.
After the loop terminates, the "Loop finished." message is printed.
Key points about break in while loops:
It provides a way to exit a loop based on dynamic conditions that might not be part of
the initial while condition.
It is often used within an if statement to conditionally terminate the loop.
When break is executed, the else block associated with a while loop (if present) will not be
executed.
continue
The continue statement is used exclusively inside loops ( for or while ) to skip the rest of
the code in the current iteration and immediately move to the next iteration.
How it works:
When the Python interpreter encounters continue , it stops executing the rest of the
loop's current code block.
It then jumps back to the top of the loop to begin the next iteration.
Example:
This code prints every number from 0 to 4, except for 2.
Output:
0
1
3
4
pass
How it works:
The interpreter executes pass and then proceeds to the next line of code. It does not
alter the flow of the loop or conditional block.
It is most useful for defining a code block that you intend to fill in later to avoid
a SyntaxError .
Example:
This code includes a pass statement inside the if condition. The program executes
the pass and then continues to the next line, so all numbers are printed.
Output:
0
1
2
3
4
Difference between Continue & Pass Statement:
Effect on loop Jumps to the beginning of No effect on the loop's flow; execution
the next iteration. simply moves to the next statement.
Primary Use Efficiently skipping items in Temporarily filling empty code blocks to
Case a loop that meet a certain avoid SyntaxError , especially during
condition.
code development.
Example:
Key features:
o Efficient operations: Fast for adding to the end with append() , but insertions and
deletions from the middle can be slower.
Tuples
Key features:
o Immutable: Cannot be modified after creation, making them useful for data that should
not change.
o Performance: Generally use less memory and are faster than lists.
Dictionaries
Syntax: Created with curly braces {} and colons to separate keys from values.
Example:
o Efficient retrieval: Optimized for quickly retrieving a value based on its key.
Sets
Example:
my_set = {1, 2, 3, 2}
print(my_set) # Output: {1, 2, 3} (duplicate '2' is removed)
my_set.add(4)
print(my_set) # Output: {1, 2, 3, 4}
Key features:
o Efficient membership testing: Very fast to check if an element is present in the set.
Stacks
Operations: push (add to the top) and pop (remove from the top).
Implementation: Can be implemented using a Python list, with append() for push
and pop() for pop.
Queues
Operations: enqueue (add to the end) and dequeue (remove from the front).
Linked lists
Principle: A sequence of nodes, where each node contains data and a reference to the
next node.
Implementation: Not a built-in Python type; it's typically implemented with a custom
class for the node and another for the list.
Trees
Principle: A hierarchical, non-linear data structure with a root, nodes, and branches.
For a collection of unique items where order doesn't matter: Use a set.
start : The index where the slice begins (inclusive). If omitted, it defaults to the beginning
of the sequence (index 0).
stop : The index where the slice ends (exclusive). The element at this index is not
included. If omitted, it defaults to the end of the sequence.
step : The interval between elements. If omitted, it defaults to 1, selecting every element.
A negative step reverses the order of the slice.
Example data : Let's use a list of numbers for the following examples:
my_list = [10, 20, 30, 40, 50, 60, 70, 80, 90]
1. Slice with start and stop Extracts a subsequence from the start index up to (but not
including) the stop index.
3.Omit stop Gets all elements from the start index to the end of the sequence.
5. Reverse a sequence
Use negative indices, Negative indices count from the end of the sequence. For
example, -1 refers to the last element.
Example
Functions in python:
In Python, a function is a named, reusable block of code that performs a specific task.
Using functions makes your code more organized, readable, and modular, and it
prevents you from having to repeat the same code.
Built-in functions: These are standard functions that are always available, such
as print() , len() , and range() .
User-defined functions: These are functions that you create yourself to suit your specific
needs.
def greet(name):
print(f"Hello, {name}!")
Function arguments:
Arguments are the values passed into a function when it is called. Python supports
several ways to handle arguments.
Positional arguments
The arguments are assigned to parameters based on their position in the function call.
def add_numbers(a, b):
return a + b
Keyword arguments
You can pass arguments by explicitly naming the corresponding parameter, which
allows you to put them in any order.
introduce(city="Paris", name="Jean")
# Output: Jean is from Paris.
Default arguments
You can set a default value for a parameter. If no argument is passed for that
parameter, the default value is used.
def say_hello(name="World"):
print(f"Hello, {name}!")
**kwargs allows a function to accept a variable number of keyword arguments, which are
received as a dictionary.
Example
When a return statement is executed, the function immediately terminates and sends
the specified value back to the point where it was called.
Any code after the return statement within the function is "dead code" and will not be
executed.
A function can have multiple return statements, but only one will be executed
depending on the logic and control flow.
Example: A simple fruitful function
The following function calculate_area is fruitful because it computes a value and returns
it.
# Output:
# The area of the rectangle is: 40
Fruitful functions offer flexibility by allowing you to return more than one value, which
Python handles by packing the values into a tuple.
Returns a Yes, using the return <value> statement. No, it performs an action and does not return a usable
value value.
Return value The explicit value specified after It implicitly returns None , a special Python value
the return keyword, which can be of any data representing the absence of a value.
type.
Purpose To provide a result that can be used for further To perform an action or cause a side effect, such as
computations. printing to the console or modifying a data structure.
Syntax Always contains a return May or may not contain a return statement. If it does,
<value> statement. it's a bare return .
The greet function below is a void function because it only prints a message and does
not return a value.
def greet(name):
"""
Prints a greeting to the console.
"""
print(f"Hello, {name}!")
# Output:
# Hello, Alice!
# Hello, Bob!
# The value of 'result' is: None
Built-in modules: A vast standard library of pre-defined modules, like math and os , is
bundled with every Python installation.
User-defined modules: Custom modules that you create to organize your own
functions and classes for specific projects.
Example: calculator.py
Group and order imports: Separate your imports into three groups: standard library,
third-party libraries, and local project modules. Within each group, sort alphabetically.
Use if __name__ == "__main__": : If your module contains runnable code for testing, place
it inside this block. This code will only execute when the file is run directly, not when it's
imported by another script.
L: Local—The innermost scope. It holds names defined within the current function,
including its arguments.
E: Enclosing—For nested functions, this is the scope of the outer (enclosing) function. A
name not found in the inner function's local scope is searched for here.
G: Global—The module-level scope. This contains all names defined in the main body
of a script or module.
Contains: The names defined inside that function, including its parameters.
Lifetime: Lasts only for the duration of the function's execution. When the function
returns, the local namespace is destroyed.
Global namespace
This namespace is created when a module is imported or a script starts. It contains
names defined at the top level of the module and exists until the interpreter stops.
Enclosing namespace
This namespace is associated with outer functions when functions are nested. It
contains names in the outer function that are available to inner functions and lasts as
long as the outer function is running.
Built-in namespace
Created when the Python interpreter begins, this namespace holds all of Python's
standard built-in functions and objects. It persists for the entire duration of the
interpreter's execution.
import module_name
This statement imports the entire module, and to access any of its contents, you must
use the module's name as a prefix.
Syntax:
import module_name
Example:
To use the sqrt() function from the math module, you would write math.sqrt() .
import math
Cons:
Can be more verbose if you use many different functions from the same module.
This statement imports specific items (functions, classes, or variables) from a module
directly into your current namespace. You can then use the item's name without a
prefix.
Syntax:
from module_name import item1, item2
Example:
Pros:
More concise: The code can be shorter and less repetitive, especially when calling a
specific item multiple times.
Clearer intent: It explicitly states which parts of a module you intend to use.
Cons:
Risk of naming conflicts: Importing multiple names directly can cause clashes if your
code or other imported modules use the same name.
Less readable (for many imports): If you import many items, it can become less obvious
where each item originated.
This variation imports all public items from a module into the current namespace.
Syntax:
from module_name import *
Example:
Key differences:
Feature import module_name from module_name import item
Code Access All contents of the module are available, but must Only the specified items are available and can be
be accessed with a prefix ( module_name.item ). accessed directly without a prefix.
Namespace Adds a single name ( module_name ) to the Adds multiple specific names ( item1 , item2 ) to
Impact current namespace. the current namespace.
Readability High; the origin of each function or variable is Can be high if only a few items are imported, but
always explicit. decreases if many items are pulled into the
namespace.
Best For General use, or when importing many items from Importing only one or two frequently used items
a module to prevent name conflicts. from a module
Python packages:
A Python package is a directory that contains a collection of related modules and a
special __init__.py file, which tells Python to treat the directory as a package. Packages
are a fundamental way to organize, reuse, and share code in a hierarchical structure,
especially in large projects.
Virtual environments create an isolated environment for a project, so you can manage a
project's dependencies without affecting other projects or the global Python installation.
You must activate the virtual environment to ensure that any packages you install are
added to it and not your global Python installation.
source my_project_env/bin/activate
On Windows:
my_project_env\Scripts\activate
You can now import the installed package into your Python script and use its
functionality.
import requests
response = requests.get('https://example.com')
print(response.status_code)
The Python ecosystem offers packages for almost any task. Some of the most widely
used ones include:
Pandas: Provides powerful, flexible data structures for data analysis and manipulation.
Scikit-learn: Offers simple and efficient tools for machine learning, including
classification, regression, and clustering.
TensorFlow / PyTorch: Deep learning frameworks used for building complex neural
networks.
Web development:
Flask: A lightweight and minimalist web framework for smaller applications and APIs.
Requests: An elegant and simple HTTP library for making web requests.
Data visualization:
Web scraping:
BeautifulSoup: A library for pulling data out of HTML and XML files.
Other applications:
PyGame: A set of modules for writing video games and multimedia applications.
NLTK: The Natural Language Toolkit for working with human language data.
Installation: When you install a package, pip automatically finds it on PyPI, downloads
it, and installs it in your Python environment. For example, to install the requests library
for making web requests, you would run pip install requests .
Dependency resolution: If a package you want to install has its own dependencies, pip
automatically identifies and installs those as well, ensuring everything works together.
Install a package:
Uninstall a package:
pip list
Save project dependencies: To create a requirements.txt file that lists all packages in
your current environment, which can be shared with others, use the freeze command.
Python Class:
Creating a Class
Here, class keyword indicates that we are creating a class followed by name
of the class (Dog in this case).
class Dog:
species = "Canine" # Class attribute
Explanation:
class Dog: Defines a class named Dog.
species: A class attribute shared by all instances of the class.
__init__ method: Initializes the name and age attributes when a new
object is created.
Python Objects
Constructor ( __init__ )
Key characteristics
The constructor method is always named __init__ (with double underscores).
The first argument is always self , which is a reference to the newly created object
instance.
Python does not support multiple constructors in the same way as languages like Java,
but you can simulate this functionality using default arguments.
Example
class Car:
# This is the constructor
def __init__(self, make, model):
self.make = make
self.model = model
def display_info(self):
print(f"Make: {self.make}, Model: {self.model}")
Key characteristics
The child class's method takes precedence over the parent's version when called on an
instance of the child class.
You can call the parent's version of the method from within the overridden method using
the super() function.
Example
class Animal:
def speak(self):
print("The animal makes a sound.")
class Dog(Animal):
# Overrides the speak() method from the parent class
def speak(self):
print("The dog barks.")
class Cat(Animal):
# Overrides and extends the parent's speak() method
def speak(self):
super().speak() # Calls the parent method
print("The cat meows.")
dog = Dog()
dog.speak() # Output: The dog barks.
cat = Cat()
cat.speak() # Output:
# The animal makes a sound.
# The cat meows.
Example
class Employee:
def __init__(self, name, salary):
self.name = name
self.salary = salary
class Manager(Employee):
def __init__(self, name, salary, department):
# Calls the parent class's constructor to initialize name and salary
super().__init__(name, salary)
# Initializes the new attribute for the Manager class
self.department = department
def display_details(self):
print(f"Name: {self.name}, Salary: {self.salary}, Department: {self.department}")
In Python, both errors and exceptions disrupt the normal execution flow of a
program, but they differ in their nature and how they are typically handled.
Errors:
Errors represent fundamental problems in a program that prevent it from
running at all or indicate severe issues that cannot be easily recovered from
during runtime.
Syntax Errors: These occur when the code violates Python's grammatical rules, such
as missing colons, incorrect indentation, or misspelled keywords. The Python interpreter
detects these before execution, preventing the program from starting.
Python
# Example of a Syntax Error
if True
print("Hello") # Missing colon after True
Logical Errors (often leading to Exceptions): These are flaws in the program's logic
that cause it to produce incorrect results or behave unexpectedly. While not always
preventing execution, they can lead to situations that raise exceptions during runtime.
Exceptions:
Exceptions are events that occur during the execution of a program that
disrupt its normal flow. Unlike syntax errors, exceptions are typically detected
at runtime and can often be handled to prevent program termination.
Runtime Errors:
Exceptions are a type of runtime error. They signal that something unexpected
happened during the program's execution, such as:
ZeroDivisionError: Attempting to divide a number by zero.
FileNotFoundError: Trying to access a file that does not exist.
TypeError: Performing an operation on an object of an inappropriate type.
IndexError: Accessing an index outside the bounds of a sequence (e.g., list, tuple).
Handling Exceptions:
The except block specifies how to handle a particular exception if it occurs within
the try block.
else and finally blocks can also be used for additional control flow in exception handling.
def validate_age(age):
if age < 0:
raise ValueError("Age cannot be negative.")
return age
Occurrence Errors are fundamental issues that prevent a Exceptions are events that interrupt the normal flow
program from even starting its execution. They of the program during runtime (execution).
occur during the parsing stage, also known as
compile-time.
Severity Errors are generally more severe and often indicate Exceptions are less severe and represent
a problem that cannot be recovered from gracefully conditions that a program can anticipate and handle
by the program itself. to continue running.
Handling Errors, particularly syntax errors, cannot be caught Exceptions can be caught and managed
and handled by try...except blocks. They must be using try...except blocks, allowing the program
fixed in the code before it can be run. to handle the issue and prevent a crash.
Common SyntaxError: Caused by code that does not follow ZeroDivisionError: Raised when a number is
types the correct Python syntax, such as missing colons divided by zero.
or parentheses. NameError: Raised when a variable or function
IndentationError: Occurs due to incorrect or name is used but has not been defined.
inconsistent indentation, which is a key part of TypeError: Occurs when an operation is performed
Python's syntax. on an object of an inappropriate type, e.g., adding a
number and a string.
FileNotFoundError: Raised when the program
attempts to open a file that does not exist.
Resolution Errors require the programmer to fix the underlying Exceptions are typically handled with code logic,
structural problem in the source code. providing an alternative path for the program to
follow when an unusual event occurs.
Example if True (missing a colon) will cause 10 / 0 will raise a ZeroDivisionError at runtime.
a SyntaxError and stop the program from running. A try...except block can catch this and print a
user-friendly message instead of crashing.
Python's GIL ensures that only one thread can execute Python bytecode at a time,
limiting true parallelism on multi-core systems.
The GIL is not a limitation for I/O-bound tasks (like reading a file or network requests)
because the GIL is released while a thread waits for I/O operations to complete. This
allows other threads to run concurrently.
Thread class: The main object for creating and managing threads. You pass the target
function and its arguments to its constructor.
.join() : Blocks the main program until the specified thread completes. This is useful for
waiting on a background task to finish before exiting.
Lock : A synchronization primitive that prevents race conditions by allowing only one
thread at a time to access a shared resource. Use with lock: to safely manage the
acquisition and release of the lock.
Queue : The queue module provides a thread-safe queue that is ideal for safely passing
data between threads.
Treat your program's core logic as distinct from the turtle library's graphical output. Your
testing should focus on whether your functions correctly calculate coordinates, angles,
and other states, not on what the final drawing looks like.
Example:
Instead of a single function draw_square() , split the responsibilities:
A separate draw_shape() function that takes this list and executes the turtle commands.
This makes get_square_moves() easy to test with standard unit testing tools.
Mocking is the practice of replacing objects in your tests with "mock" objects that mimic
their behavior. This is crucial for turtle because you do not want to launch a graphics
window every time you run a test. The unittest.mock module (or the pytest-mock plugin
for pytest ) is the standard tool for this. The core idea is to replace
the turtle.Turtle and turtle.Screen objects with mocks and then check if the correct
methods were called with the expected arguments.
mock_turtle.forward()
mock_turtle.right()
For interactive games or applications, your code often binds functions to keyboard
presses using screen.onkeypress() . You can test this behavior by mocking
the turtle.Screen and confirming that the correct functions are bound to the events.
3. Simulate a keypress event by calling the function that onkeypress would normally
invoke.
4. Assert that your turtle's state (e.g., coordinates) has changed as expected.
For complex graphical outputs where the final visual result is important, you can perform
visual regression testing.
How it works:
1. Capture baseline: On the first run, your test captures a screenshot of the turtle window
and saves it as the "baseline." Libraries like PyAutoGUI can automate this process.
This approach is more complex to set up and maintain due to minor visual differences
between platforms, but it offers the most comprehensive way to test the final visual
output.
Unit testing:
Unit testing is a software development practice in which individual components of an
application are tested to ensure they work as intended. In Python, developers can use
frameworks like the built-in unittest or the popular third-party pytest to write, organize,
and run these tests.
Assertions: The framework provides a rich set of built-in methods for verifying
conditions, such as assertEqual() , assertTrue() , and assertRaises() .
Fixtures: It supports setup and teardown methods ( setUp() and tearDown() ) for
preparing and cleaning up the testing environment for each test.
Best for: Developers who prefer a classic, object-oriented testing style or for projects
where external dependencies are not desired.
pytest
pytest is a widely-used third-party framework known for its simple, flexible, and concise
syntax.
Assertions: It uses Python's standard assert keyword, which is less verbose and
easier to read than unittest 's assertion methods.
Fixtures: It has a powerful and flexible fixture system that simplifies the setup and
teardown process. Fixtures can be shared across multiple test files.
Best for: Most Python projects, from small to large, due to its minimal boilerplate and
rich plugin ecosystem.
class Calculator:
def add(self, a, b):
return a + b
def subtract(self, a, b):
return a - b
import unittest
from calculator import Calculator
class TestCalculator(unittest.TestCase):
def setUp(self):
# Set up a new Calculator instance before each test
self.calc = Calculator()
def test_add(self):
self.assertEqual(self.calc.add(2, 3), 5)
self.assertEqual(self.calc.add(-1, 1), 0)
def test_subtract(self):
self.assertEqual(self.calc.subtract(5, 2), 3)
self.assertEqual(self.calc.subtract(10, 20), -10)
def test_divide_by_zero(self):
with self.assertRaises(ValueError):
self.calc.divide(10, 0)
if __name__ == '__main__':
unittest.main()
import pytest
from calculator import Calculator
def test_add():
calc = Calculator()
assert calc.add(2, 3) == 5
assert calc.add(-1, 1) == 0
def test_subtract():
calc = Calculator()
assert calc.subtract(5, 2) == 3
assert calc.subtract(10, 20) == -10
def test_divide_by_zero():
calc = Calculator()
with pytest.raises(ValueError):
calc.divide(10, 0)
.
Use descriptive names: Choose clear and meaningful names for your test functions and
classes, such as test_add_two_positive_numbers() .
Isolate tests: Each test should be independent and not rely on the outcome or state of
other tests. Use test fixtures ( setUp in unittest or pytest fixtures) to create a clean state.
Test for edge cases: Go beyond typical inputs and include boundary conditions, invalid
data, and potential error cases to ensure robustness.
Aim for high coverage: Use a coverage tool like coverage.py to identify untested parts of
your codebase, but don't blindly aim for 100% coverage.
Avoid complex logic in tests: Tests should be simple and straightforward. If your test
logic becomes too complicated, it may indicate a problem with the code being tested