Prompt Engineering
Prompt Engineering
Engineering
1. What is a Prompt?
A prompt is a set of instructions or a specific input given to a machine learning model (like
ChatGPT) to generate a desired output.
2. What is a Response?
A response is the output generated by the AI system when a prompt is provided. The
response is the result of the model processing the input.
3. Introduction to ChatGPT
ChatGPT is an AI model developed by OpenAI, based on the GPT architecture. It is designed
to understand and generate human-like text based on the input provided.
1. Lists
Lists are mutable sequences in Python, meaning they can be modified after creation. Lists
can hold elements of any data type, and they maintain the order of insertion.
• List comprehensions: A concise way to create lists by applying an expression to each item
in a sequence.
2. Tuples
Tuples are immutable sequences in Python, meaning once they are created, their contents
cannot be modified. Tuples are often used for fixed collections of items.
• Creating and using tuples: Tuples can store multiple items, and they are defined using
parentheses.
• Unpacking tuples: Python allows you to extract the values from a tuple into separate
variables.
3. Sets
Sets are unordered collections of unique elements. They are used to store multiple items but
only one occurrence of each item.
• Creating and using sets: Sets can be created using curly braces or the set() constructor.
• Set operations: Sets support various operations, including union, intersection, and
difference.
4. Dictionaries
Dictionaries are collections of key-value pairs, where each key is mapped to a value. They
allow for fast lookup, insertion, and deletion.
• Creating and using dictionaries: Keys must be unique, and dictionaries are created using
curly braces or the dict() constructor.
• Dictionary methods and comprehensions: Python provides several methods for working
with dictionaries, and comprehensions allow for concise creation of dictionaries.
1. Collections Module
The collections module in Python provides additional data structures that enhance the
built-in types.
• defaultdict: A dictionary subclass that provides default values for non-existent keys.
• OrderedDict: A dictionary subclass that remembers the order in which items were added.
• deque: A list-like container optimized for fast appends and pops from both ends.
Module 4: File Handling and Data
Processing
File Operations
• Opening files: The open() function is used to open files in different modes (read, write,
append, etc.).
• Reading from files: You can read the contents of a file using various methods such as
read(), readline(), and readlines().
• Closing files: After completing operations, it is important to close the file using the close()
function to free up resources.
Python supports various file modes for opening files, such as:
• w: Write mode, which overwrites the file if it exists or creates a new file.
• a: Append mode, which adds data to the end of the file if it exists.
• CSV (Comma-Separated Values): The csv module in Python provides functionality to read
from and write to CSV files. CSV files store tabular data in plain text, with each line
representing a row and columns separated by commas.
• JSON (JavaScript Object Notation): The json module in Python is used to work with JSON
data, which is a lightweight format for storing and exchanging data. JSON files are text files
that contain structured data, typically used for APIs and configuration files.
Module 5: Object-Oriented
Programming (OOP)
OOP Basics
• Defining classes: A class is defined using the class keyword followed by the class name.
• Creating objects: Objects are created by instantiating a class, and each object can have
unique values for instance variables.
• Instance variables and methods: Instance variables hold data specific to an object, while
methods define the behavior associated with the class.
• Using class variables: These variables are accessible by all instances of the class and can be
modified at the class level.
• Using class methods: Class methods take the class itself as their first argument, which is
typically named cls.
3. Inheritance
Inheritance allows one class (the child class) to inherit the attributes and methods of
another class (the parent class). It promotes code reusability and helps in maintaining a
cleaner class hierarchy.
• Single inheritance: Involves inheriting from one parent class.
• Multiple inheritance: Involves inheriting from more than one parent class, allowing a class
to combine functionalities from multiple sources.
• Method overriding: Involves redefining a method in the child class that was defined in the
parent class, allowing for custom behavior in the child class.
Encapsulation refers to restricting access to certain details of an object and only exposing
essential features.
• Name mangling: Python uses name mangling to internally change the names of private
variables, making them harder to access from outside the class.
• Defining modules: A module is simply a Python file that contains functions, classes, and
variables. It can be created by saving Python code in a .py file.
• Using modules: Modules can be imported into other Python scripts using the import
statement. Once imported, the functions and variables defined in the module can be
accessed.
2. Packages
A package is a collection of modules organized into directories. Packages allow for a more
hierarchical structuring of the code, especially in large projects.
• Organizing code into packages: Packages are directories that contain a special __init__.py
file, which can be empty or used to initialize the package. Sub-packages can also be created
by nesting directories inside packages.
• Importing from packages: Specific modules or functions from a package can be imported
using the from...import syntax.
1. Exception Types
Exceptions are runtime errors that can occur during the execution of a program. Python
provides several built-in exceptions to handle different error scenarios.
• Else: The else block runs if no exceptions are raised in the try block.
• Finally: The finally block runs whether an exception occurs or not, typically used to clean
up resources like closing files.
3. Custom Exceptions
In Python, you can create your own exceptions by subclassing the built-in Exception class.
Custom exceptions are useful when you need to handle errors that are specific to your
application logic.
• Raising custom exceptions: You can raise your custom exception using the raise keyword.
Debugging
1. Debugging Techniques
Debugging is the process of identifying and fixing errors in the code. Python provides
several tools and techniques to aid in debugging.
• Using print statements and logging: Inserting print statements at critical points in the code
is a simple way to track the program's execution. For more advanced logging, the logging
module is used to record various levels of messages (info, warning, error, etc.).
• Using debuggers (pdb): Python’s built-in debugger, pdb, allows step-by-step execution,
inspecting variables, and setting breakpoints to analyze code behavior.
1. Decorators
Decorators are a powerful tool in Python that allow you to modify the behavior of functions
or classes. They are often used for cross-cutting concerns like logging, timing, or access
control.
• Class decorators: Class decorators are similar to function decorators but are used to
modify the behavior of classes.
2. Context Managers
Context managers are used to manage resources in Python, such as opening and closing files
or managing database connections. They ensure that resources are properly acquired and
released.
• Using with statement: The with statement simplifies the management of resources by
ensuring that the resource is automatically cleaned up when the block of code is exited.
• Creating custom context managers: You can create custom context managers by defining
the __enter__ and __exit__ methods in a class. The __enter__ method is executed when the
block is entered, and __exit__ is executed when the block is exited, regardless of whether an
exception occurred.
Module 9: Iterators and Generators
1. Iterators
An iterator in Python is an object that contains a countable number of values and can be
iterated over, meaning that you can traverse through all the values. It implements the
iterator protocol, which consists of two methods: __iter__() and __next__().
- __iter__(self): This method is called when an iterator object is initialized. It should return
the iterator object itself.
- __next__(self): This method returns the next value from the iterator. If there are no more
items to return, it raises the StopIteration exception.
Example: Custom iterator class
class MyIterator:
def __iter__(self):
self.value = 0
return self
def __next__(self):
if self.value < 5:
self.value += 1
return self.value
else:
raise StopIteration
2. Generators
Generators are a simpler way to create iterators. They allow you to declare a function that
behaves like an iterator, i.e., it can be used in a for loop.
The yield statement is used in Python to replace return in a generator function. A generator
function is defined like a normal function, but instead of returning a value, it yields an
expression back to the caller. Each time the generator’s __next__() method is called, the
function resumes execution from where it left off.
Example:
def simple_generator():
yield 1
yield 2
yield 3
Generator Expressions
Generator expressions provide an even shorter syntax to define generators. They are
similar to list comprehensions, but instead of creating a list, they return a generator object.
Example:
gen = (x * x for x in range(5))
for val in gen:
print(val)
Advantages of Generators:
1. Memory Efficient: Generators are memory efficient as they only generate values one at a
time, which is beneficial when working with large datasets.
2. Represent Infinite Sequences: Unlike lists, generators can represent infinite sequences as
they don’t store all values in memory.
3. Better Performance: Generators often perform better than lists when dealing with large
volumes of data because they compute values on the fly.
- NumPy Arrays: Arrays in NumPy are grid-like data structures that can hold elements of the
same type. Arrays can be one-dimensional (1D), two-dimensional (2D), or multi-
dimensional (ND).
Example:
import numpy as np
arr = np.array([1, 2, 3, 4])
- Matrix Operations: NumPy allows easy matrix operations, including addition, subtraction,
multiplication, and dot product of matrices.
Example:
A = np.array([[1, 2], [3, 4]])
B = np.array([[5, 6], [7, 8]])
matrix_sum = A + B
matrix_product = np.dot(A, B)
- Broadcasting: NumPy also supports broadcasting, where smaller arrays are 'broadcast'
across the larger array to perform arithmetic operations.
Advantages of NumPy:
1. Efficient storage and computation of large data arrays.
2. Performs element-wise operations and broadcasting, which are faster and memory
efficient.
2. Pandas
Pandas is a powerful library for data manipulation and analysis. It provides data structures
like Series (1D) and DataFrames (2D) to work with structured data.
- Data Manipulation: Pandas allows filtering, sorting, and aggregating data. You can easily
manipulate DataFrames using indexing, slicing, and various built-in functions.
Example:
filtered_df = df[df['Age'] > 25]
- Handling Missing Data: Pandas provides tools to handle missing data by filling or dropping
null values.
- Reading Data: Pandas provides easy functions to read data from CSV, Excel, and other file
formats.
Example:
df_csv = pd.read_csv('data.csv')
df_excel = pd.read_excel('data.xlsx')
- Writing Data: Similarly, Pandas allows writing DataFrames back to CSV, Excel, or other
formats.
Example:
df.to_csv('output.csv')
df.to_excel('output.xlsx')
Advantages of Pandas:
1. Easy handling and manipulation of structured data.
2. Integration with many file formats like CSV, Excel, JSON, and SQL databases.
3. Built-in handling of missing data.
- Basic Plotting: Matplotlib’s pyplot module provides a MATLAB-like interface for creating
plots. It supports various types of plots like line plots, scatter plots, bar charts, histograms,
and more.
Example:
import matplotlib.pyplot as plt
x = [1, 2, 3, 4]
y = [10, 20, 25, 30]
plt.plot(x, y)
plt.show()
- Customization: Matplotlib allows customization of plot properties such as axis labels, title,
legends, colors, and line styles.
Example:
plt.title('Sample Plot')
plt.xlabel('X-axis')
plt.ylabel('Y-axis')
- Subplots: Matplotlib also allows creating multiple plots (subplots) in a single figure.
Example:
plt.subplot(1, 2, 1)
plt.plot(x, y)
plt.subplot(1, 2, 2)
plt.bar(x, y)
Advantages of Matplotlib:
1. Flexible and powerful for a wide variety of plots.
2. Highly customizable, allowing control over all aspects of plot appearance.
3. Large community and extensive documentation.
2. Seaborn
Seaborn is a Python data visualization library based on Matplotlib. It provides a high-level
interface for drawing attractive and informative statistical graphics.
- Seaborn is designed to work well with Pandas DataFrames, making it easy to visualize
statistical data. It supports a range of plots like distribution plots, categorical plots, and
regression plots.
Example:
import seaborn as sns
import matplotlib.pyplot as plt
tips = sns.load_dataset('tips')
sns.scatterplot(x='total_bill', y='tip', data=tips)
plt.show()
- Styling: Seaborn comes with built-in themes and color palettes for improving the
aesthetics of plots.
Example:
sns.set_theme(style="whitegrid")
Advantages of Seaborn:
1. Built-in support for statistical data visualization.
2. Easy integration with Pandas DataFrames.
3. Simplifies complex visualizations and improves the appearance of plots by default.
- Writing Tests: Test cases in unittest are written by subclassing unittest.TestCase. Methods
that start with the name 'test' are considered test methods.
Example:
import unittest
class TestMath(unittest.TestCase):
def test_addition(self):
self.assertEqual(1 + 1, 2)
if __name__ == '__main__':
unittest.main()
- Running Tests: Tests can be run using the command-line interface or by calling
unittest.main() in the script.
Advantages of Unittest:
- Test Fixtures: Fixtures are setup and cleanup routines that run before and after tests.
Common methods are setUp() and tearDown(), used to prepare the environment before
tests and clean up after tests.
Example:
def setUp(self):
self.test_data = [1, 2, 3]
def tearDown(self):
del self.test_data
- Test Suites: A test suite is a collection of test cases, test suites, or both. It allows you to
group tests to be run together.
Example:
suite = unittest.TestSuite()
suite.addTest(TestMath('test_addition'))
2. Pytest
Pytest is a popular third-party testing framework that makes writing and running tests
easier. It supports fixtures, parameterized testing, and advanced features.
- Writing Tests: Pytest uses a simple function-based approach for writing tests. You do not
need to subclass any class or follow any strict naming convention.
Example:
def test_addition():
assert 1 + 1 == 2
- Fixtures in Pytest: Fixtures in Pytest are used to manage setup and teardown. They are
defined using the @pytest.fixture decorator.
Example:
import pytest
@pytest.fixture
def sample_data():
return [1, 2, 3]
Example:
assert a + b == result
Advantages of Pytest:
2. Supports parameterized testing, useful for running the same test with multiple data sets.