Python
Python
A computer language is a medium used to interact with computers and solve problems by writing
programs. There are several types:
1. Machine Languages: Binary code (0s and 1s) directly executed by the CPU.
2. Assembly Languages: Low-level language closely related to one or a family of machine
languages, and which uses mnemonics to ease writing. Assembly language is specific to a
computer's architecture, meaning each family of machines(e.g x86 Family: Intel 8086,
Pentium, AMD processors (PCs, laptops); ARM Family: ARM Cortex series (mobile,
embedded systems); MIPS Family: Embedded devices, routers (networking, embedded
systems); SPARC Family: Sun Microsystems servers (enterprise servers); IBM System/360
Family: IBM mainframes (business, scientific computing).) has its own assembly language.
Key Differences
• Readability: Binary language is nearly impossible for humans to read, while assembly
language uses more understandable mnemonics.
• Translation: Binary is executed directly by the CPU; assembly needs to be converted
into binary by an assembler.
• Ease of Use: Assembly language is easier for programmers to use and debug compared
to binary.
• Abstraction Level: Binary language is at the lowest level of abstraction, directly dealing
with hardware. Assembly language offers a slightly higher level of abstraction with more
human-readable instructions.
3. Programming Languages:
• General Purpose Languages: Broadly applicable across domains and lacks
specialized features for a particular domain(e.g., Python, Java, C).
• Markup Languages: Used for annotating documents in a syntactically
distinguishable way from the text (e.g., HTML, XML).
• Stylesheet Languages: Express document presentation (e.g., CSS).
• Configuration Languages: Write configuration files (e.g., JSON).
• Query Languages: Make queries in databases (e.g., SQL).
• Scripting Languages: Write scripts (e.g., JavaScript, Ruby).
Low Level vs High Level Languages
Compiled language
A compiled language is one where the source code is translated into machine code by a compiler,
resulting in faster execution and thorough error checking before running the program. This machine
code is directly executed by the computer's CPU. E.g. C, C++, Pascal, Objective-C, Swift, Go,
Rust, and Fortran
1. Pre-processing (main.c)
• Purpose: To prepare the source code for compilation.
• Strips out comments from the code, which are not needed for execution.
• Expands macros and includes header files as specified by #include directives.
• Includes or excludes parts of the code based on preprocessor directives (e.g., #ifdef,
#ifndef, #if).
2. Compiling (main.i)
• Purpose: To translate pre-processed code into assembly instructions.
• The compiler converts the pre-processed code into assembly language, which is specific to
the target processor architecture.
3. Assembling (main.s)
• Purpose: To convert assembly instructions into object code.
• The assembler translates the assembly language code into machine code (object code),
which is a binary representation that the computer's CPU can execute.
4. Linking (a.out or main.exe)
• Purpose: To produce the final executable file.
• The linker combines object code files, resolves references between them, and inserts code
from libraries or other modules as needed. It arranges the code and data into a cohesive
executable file.
Interpreted Language
An interpreted language is a type of programming language where instructions are executed directly
without the need for compilation into machine code. Here are the key characteristics of interpreted
languages:
Example:
In Python, for instance, you can write a script (script.py) directly in the Python interpreter or
run it using python script.py, where the Python interpreter reads and executes each line of
code without needing a separate compilation step.
Mixed language: shows behavior from both (compiled as well as interpreted) uses compiler as
well as interpreter e.g. Java, Python
Compiler vs Interpreter
------------------------------------------------------------------------------------------------
Python
• Python was conceived by Guido van Rossum in the late 1980s as a successor to the ABC
language. Development started in December 1989, and van Rossum led the project until July
2018. A team of five then took over in January 2019.
• python application does NOT require any entry point function
• python is one of the scripting languages. The code starts execution from from top (line 1) to
bottom
• Python is a general-purpose, high-level, and compiled as well as interpreted language with
the following characteristics:
• Dynamically Typed: Variables do not require explicit type definitions.
• Garbage Collected: Automatically manages memory by cleaning up unused objects.
Advantages of Python:
• Ease of Use: Simple and user-friendly, suitable for both beginners and experienced
programmers, offering more structure than shell scripts or batch files.
• Error Checking: Provides more error checking compared to C and includes high-level data
types like flexible arrays and dictionaries.
• Broad Applicability: More versatile than Awk or Perl, capable of handling a wider range of
problems with ease.
• Modularization: Supports splitting programs into reusable modules.
• Interpreted Language: Saves time during development as there's no need for compilation
and linking.
• Compact and Readable: Allows for concise and readable code, with programs often shorter
than equivalent code in C, C++, or Java due to:
• High-level data types for complex operations.
• Indentation for grouping statements instead of brackets.
• No need for variable or argument declarations.
versions
0.9.0: deprecated. Having features like classes with inheritance, exception handling, functions
1.x: deprecated. functional programming tools like lambda, map, filter, reduce
2. Python Compiler: Python interpreter compiles the source code (hello.py) into bytecode.
a. Lexical Analysis: The interpreter reads the source code and breaks it down into tokens.
Tokens are the smallest units of the code, such as keywords, identifiers, literals, and
operators.
Tokens for the Example:
• def
• greet
• (
• name
• )
• :
• return
• f"Hello, {name}!"
• print
• (
• greet
• (
• "Alice"
• )
• )
b. Parsing: The tokens are then organized into a syntax tree (also known as an abstract
syntax tree or AST). The syntax tree represents the structure of the program based on the
syntax rules of Python. The parser checks the syntax tree for correctness. If the code is
syntactically incorrect, Python will produce an error message.
AST Representation:
• Function Definition (greet)
• Parameters (name)
• Body
• Return Statement
• Formatted String (f"Hello, {name}!")
• Print Statement
• Function Call (greet)
• Argument ("Alice")
c. Semantic Analysis
• Type Checking and Validation: Python performs semantic checks, such as verifying
that name is a valid parameter and that print is a callable function.
d. Compilation: The Python interpreter compiles the syntax tree into bytecode. Bytecode
is a low-level representation of your source code that is easier for the interpreter to execute.
3. Bytecode (.pyc)
• OS Independent: The compiled bytecode is saved in a .pyc file. This bytecode is
platform-independent, meaning it can be executed on any operating system that has a
compatible Python interpreter.
• The size of each byte code is 1 byte or 8 bits and hence the these are called as byte codes
4. Python Interpreter
• Execution: The bytecode is executed by the Python Virtual Machine (PVM). interprets the
bytecode and translates it into machine code that can be executed by the CPU.
• OS Dependent: While the bytecode itself is OS-independent, the PVM translates it into OS-
specific machine code.
print("hello 1")
printf("hello 2") #will generate NameError. he first line will get executed and code will stop on the
line
------------------------------------------------------------------------------------------------------------------------
Python Implementations:
• PyPy: Faster alternative due to JIT compilation. Written in Python
• Jython: For Java platform integration.
• IronPython: For .NET framework integration.
• CPython: The default, standard version of Python you get from the official site, widely
used, and written in C. Fastest
Categories of Keywords:
1. Value Keywords: True, False, None
2. Operator Keywords: and, or, not, in, is
3. Control Flow Keywords: if, elif, else
4. Iteration Keywords: for, while, break, continue, else
5. Structure Keywords: def, class, with, as, pass, lambda
6. Returning Keywords: return, yield
7. Import Keywords: import, from, as
8. Exception-Handling Keywords: try, except, raise, finally, else, assert
9. Asynchronous Programming Keywords: async, await
10.Variable Handling Keywords: del, global, nonlocal
Valid Identifiers
1. variable1
2. first_name
3. MAX_LIMIT
4. total_amount
5. _private_var
6. userAge
Invalid Identifiers
1. 1variable - Starts with a digit
2. first-name - Contains a hyphen
3. price! - Contains a special symbol !
4. @home - Contains a special symbol @
5. total amount - Contains a space
6. class - Uses a reserved keyword
Conventions
• for variables: lower case e.g. name, address, first_name
• for functions: lower case with underscore e.g. is_eligible_for_voting
• or class: lower case with first letter uppercase e.g. Person, Mobile
Variables
• A named location used to store data in memory.
• Think of variables as containers that hold data that can be changed later.
Characteristics
• Dynamically-Typed: In languages like Python, you do not need to declare the data type of a
variable explicitly. The data type is inferred when a value is assigned to the variable.
value = 20 # Integer
name = "Steve" # String
pi = 3.14 # Float
is_valid = True # Boolean
Key Points
1. No Explicit Type Declaration: The type is inferred from the value assigned.
2. Reassignable: Variables can be reassigned to different types of values.
3. Container Concept: Variables act as containers that can hold different types of data.
# Examples of variable assignments
value = 20 # Initially an integer
value = "twenty" # Now a string
Constants
In Python, constants are indicated by naming variables in all capital letters with underscores
between words. This is a convention to signal that the value should not change, though Python does
not enforce this and allows reassignment.
Python Statements
• Statement: An instruction that a Python interpreter can execute.
Types of Statements
1. Assignment Statement: Assigns a value to a variable.
num = 200
2. Single-Line Statement: Ends with a newline character.
a = 1
print(a)
3. Multi-Line Statement:
• Extend over multiple lines using the line continuation character (\).
• Implied inside parentheses (), brackets [], and braces {}.
total = (1 + 2 +
3 + 4)
• or
total = 1 + 2 + 3 + \
4 + 5 + 6 + \
7 + 8 + 9
Data Types
• Data Types: Define the nature of data and operations that can be performed. An attribute
associated with a piece of data that tells a computer system how to interpret its value.
1. Numeric Types:
• Integer: Whole numbers
• Float: Decimal numbers
• Complex: Numbers with real and imaginary parts
2. Boolean Type:
• Boolean: Represents True or False
3. Sequence Types:
• String: Sequence of characters
• List: Ordered, mutable collection
• Tuple: Ordered, immutable collection
4. Set Type:
• Set: Unordered, mutable collection of unique items
5. Dictionary Type:
• Dictionary: Collection of key-value pairs
# Numeric Types
a = 10 # Integer
pi = 3.14 # Float
z = 2 + 3j # Complex
# Boolean Type
is_valid = True # Boolean
# Sequence Types
name = "Alice" # String
fruits = ["apple", "banana", "cherry"] # List
point = (1, 2) # Tuple
# Set Type
unique_numbers = {1, 2, 3, 4, 5} # Set
# Dictionary Type
person = {"name": "John", "age": 30} # Dictionary
Literals
In Python, literals are fixed values that are used directly in code. They represent data without
needing any computation. It is raw data given in a variable or constan
1. Integer Literals: These are whole numbers without a decimal point. They are immutable
• Example: 10, -5, 0
2. Floating-Point Literals: These represent real numbers with a decimal point.
• Example: 3.14, -0.001, 2.0
3. String Literals: These are sequences of characters enclosed in quotes.
• Single quotes: 'hello'
• Double quotes: "world"
• Triple quotes for multi-line strings: '''multi-line string''' or
"""multi-line string"""
4. Boolean Literals: These represent truth values.
• True and False
Example
def add_numbers(a: int, b: int) -> int:
return a + b
Type Conversion
Type conversion in Python involves changing the data type of a value to another type.
1. Implicit Type Conversion: Automatic and Avoids any Data Loss
result = 10 + 35.50 # converts 10 (int) to 10.0 (float) before addition
String to List:
str_val = "hello"
list_val = list(str_val)
print(list_val) # Output: ['h', 'e', 'l', 'l', 'o']
print(type(list_val)) # Output: <class 'list'>
List to Tuple:
list_val = [1, 2, 3]
tuple_val = tuple(list_val)
print(tuple_val) # Output: (1, 2, 3)
Tuple to Set:
tuple_val = (1, 2, 3, 3)
set_val = set(tuple_val)
print(set_val) # Output: {1, 2, 3}
print(type(set_val)) # Output: <class 'set'>
int_val = 97
char_val = chr(int_val)
print(char_val) # Output: 'a'
print(type(char_val)) # Output: <class 'str'>
• You can include various expressions inside the curly braces {}:
a = 5
b = 10
result = f"The sum of {a} and {b} is {a + b}."
print(result) # Output: The sum of 5 and 10 is 15.
• Left-Align
name = "Alice"
formatted_name = f"{name:<10}" # 10-character wide, left-aligned
print(f"'{formatted_name}'") # Output: 'Alice '
• Right-Align
name = "Alice"
formatted_name = f"{name:>10}" # 10-character wide, right-aligned
print(f"'{formatted_name}'") # Output: ' Alice'
• Center
name = "Alice"
formatted_name = f"{name:^10}" # 10-character wide, centered
print(f"'{formatted_name}'") # Output: ' Alice '
• Formatting Date
Using Expressions
a = 5
b = 10
c = 15
result = f"The average of {a}, {b}, and {c} is {(a + b + c) / 3:.2f}"
print(result) # Output: The average of 5, 10, and 15 is 10.00
Nested f-strings
name = "Alice"
age = 30
info = f"Name: {name}, Age: {age}"
formatted_info = f"User Info: {info}"
print(formatted_info) # Output: User Info: Name: Alice, Age: 30
# Keyword arguments
message = "Name: {name}, Age: {age}".format(name="Alice", age=30)
print(message) # Output: Name: Alice, Age: 30
value = 123.456789
formatted = "Value: {:.2f}".format(value)
print(formatted) # Output: Value: 123.46
text = "Python"
formatted = "{:<10}".format(text) # 10-character wide, left-aligned
print(f"'{formatted}'") # Output: 'Python '
Multiline f-strings
name = "Alice"
age = 30
message = f"""
Hello, {name}!
You are {age} years old.
"""
print(message)
# Output:
# Hello, Alice!
# You are 30 years old.
Type()
It is a built-in function that returns the type of the specified object or creates new types dynamically
x = 42
print(type(x)) # Output: <class 'int'>
x = 3.14
print(type(x)) # Output: <class 'float'>
x = "Hello"
print(type(x)) # Output: <class 'str'>
x = [1, 2, 3]
print(type(x)) # Output: <class 'list'>
x = (1, 2, 3)
print(type(x)) # Output: <class 'tuple'>
x = {'key': 'value'}
print(type(x)) # Output: <class 'dict'>
x = {1, 2, 3}
print(type(x)) # Output: <class 'set'>
x = True
print(type(x)) # Output: <class 'bool'>
2. Using type() to Create a Class: type() can be used to create a new class dynamically:
# Define a class dynamically
MyClass = type('MyClass', (object,), {'greet': lambda self: 'Hello'})
obj = MyClass()
print(obj.greet()) # Output: Hello
Functions
Purpose of Functions
• Modularity: Functions allow you to break a program into smaller, manageable pieces, each
performing a specific task.
• Reusability: Once defined, a function can be reused throughout your code, avoiding
duplication.
• Organization: Functions help organize code logically, making it easier to understand and
maintain.
• function uses c calling conventions
Syntax
def function_name(parameters):
"""
docstring
"""
# statement(s)
return value
• pass: do not do anything, pass the control to the next line, used to create empty
function/class
Examples
1. Empty function
def empty_function():
pass
2. Parameterless Function
def greet():
print("Hello, World!")
greet() # Output: Hello, World!
3. Parameterized Function
def greet(name):
print(f"Hello, {name}!")
greet("Alice") # Output: Hello, Alice!
8. Nested Function
• the inner function can be called only within the function in which it is declared
def outer():
print(“inside outer”)
def inner():
print(”inside inner”)
inner()
outer()
inner() # error as it has local scope
9. Lambda / Anonymous Functions: Lambda functions are small anonymous functions
defined using the lambda keyword. They can take any number of arguments but have only
one expression.
Syntax lambda arguments: expression
Characteristics
• It can only contain expressions and can’t include statements in its body
• It is written as a single line of execution
• It does not support type annotations. It can be immediately invoked
• Lambda must accept at least one parameter
• the body statement must return a value
square = lambda x: x * x
print(square(5)) # Output: 25
Scope of Variables
Scope of a variable is the portion of a program where the variable is recognized
The lifetime of a variable is the period throughout which the variable exists in the memory
• Local Scope: Parameters and variables defined inside a function are not visible from outside
the function. Hence, they have a local scope.
• The lifetime of variables inside a function is as long as the function executes
• They are destroyed once we return from the function. Hence, a function does not remember
the value of a variable from its previous calls.
def foo():
local_var = "local"
foo()
print(local_var) # error
• Global Scope: Variables defined outside of all functions are global and can be accessed
from within functions, but they must be declared as global if modified inside the function.
• Rules of global Keyword
• When we create a variable inside a function, it is local by default
• When we define a variable outside of a function, it is global by default. You don't
have to use global keyword
• We use global keyword to read and write a global variable inside a function
• Use of global keyword outside a function has no effect
g_var = "global"
def foo():
print(g_var)
foo()
x = 10 # Global variable
def modify():
global x
x = 20
modify()
print(x) # Output: 20
Function Types
1. Built-in functions
• Functions that readily come with Python are called built-in functions. E.g., str(), int(), float()
• If we use functions written by others in the form of library, it can be termed as library
functions
2. User defined functions
• Functions that we define ourselves to do certain specific task are referred as user-defined
functions
• Programmers working on large project can divide the workload by making different
functions
Function parameters
1. Positional parameters: do not have parameter name whlie making the function name the values
will get assigned to the parameters from left to right
def function_3(num1, num2, num3):
print(f"num1 = {num1}, num2 = {num2}
function_3(10, 20, 30) # num1 = 10, num2 = 20, num3 = 30
2. Named parameters: the function call will contain the parameter name along with the parameter
value
def function_3(num1, num2, num3):
print(f"num1 = {num1}, num2 = {num2}
function_3(num3=30, num2=20, num1=10) # num1 = 10, num2 = 20, num3 = 30
3. Optional parameters: a function can set a default value for a parameter caller does not need to
pass a value for such parameters. The parameter having default value becomes optional (caller may
or may not pass the value forsuch parameter)
def function_1(p1, p2=50): # p2 has a default value = 50
print(f"{p1}, {2}")
function_1(10) # p1 = 10, p2 = 50
function_1(10, 20) # p1 = 10, p2 = 20
Variable length argument function
• when the function gets called
◦ the positional parameters get collected in a tuple (args)
◦ the named parameters get collected in a dictionary (kwargs)
def va_function(*args, **kwargs):
print(f"args = {args}, type = {type(args)}")
print(f"kwargs = {kwargs}, type = {type(kwargs)}")
va_function(10, 20) # args = (10, 20)
va_function(p1=10, p2=20) # kwargs = {'p1': 10, 'p2': 20}
va_function(10, 20, p1=30, p2=40) # args = (10, 20), kwargs = {'p1': 30, 'p2': 40}
Function alias
Another name given to an existing function. Similar to function pointer in C
def function_2():
print("inside function_2")
my_function = function_2 # function alias
function_2() # inside function_2
my_function() # inside function_2
Collections in Python
Python provides several built-in collection types that allow you to store, organize, and manipulate
groups of related data. Each collection type has its unique properties and use cases.
1. Lists
- ordered collection of similar to dis-similar values
- to create a list use []
- the values in list are not allocated contiguously
- internally the list is implemented as linked list
- is mutable collection
numbers = [10, 20, 30, 40, 50]
print(f"length of numbers = {len(numbers)}") # get the size or length of numbers
Return
Method Syntax Parameters Description Example
Value
lst = [1, 2];
Adds an item to the end lst.append(3);
append() list.append(item) item: Element to add None
of the list. print(lst)
Extends the list by lst = [1, 2];
iterable: List or
extend() list.extend(iterable) None appending elements lst.extend([3, 4]);
iterable print(lst)
from an iterable.
Inserts an item at a lst = [1, 2];
list.insert(index, index: Position,
insert() None specified position in the lst.insert(1, 1.5);
item) item: Element
list. print(lst)
Removes the first lst = [1, 2, 3];
item: Element to
remove() list.remove(item) None occurrence of an item lst.remove(2);
remove print(lst)
from the list.
Removes and returns
the item at the specified
index: Position Element at lst = [1, 2, 3];
pop() list.pop([index]) position. If no index is
(optional) index lst.pop(); print(lst)
specified, it removes
and returns the last item.
Removes all items from lst = [1, 2, 3];
clear() list.clear() None None
the list. lst.clear(); print(lst)
item: Element to find,
Returns the index of the lst = [1, 2, 3];
list.index(item, start: Start position Index of
index() first occurrence of an
start, end) (optional), end: End item print(lst.index(2))
item.
position (optional)
Returns the number of lst = [1, 2, 2, 3];
item: Element to Count of
count() list.count(item) times an item appears in print(lst.count(2))
count item
the list.
key: Function to
list.sort(key=None, Sorts the items of the lst = [3, 1, 2];
sort() customize sort None
reverse=False) list in place (default is lst.sort(); print(lst)
(optional), reverse:
ascending order).
Sort order (optional)
lst = [1, 2, 3];
reverse() list.reverse() None Reverses the elements lst.reverse();
None
of the list in place. print(lst)
lst = [1, 2, 3]; new_lst
copy() list.copy() Shallow copy Returns a shallow copy = lst.copy();
None
of list of the list. print(new_lst)
set_data = {4, 5, 6}
my_list = list(set_data) # Convert set to list
2. Negative Indexing: Access elements from the end of the list using negative integers, starting
from -1.
element = my_list[-1] # Accesses the last element (50)
element = my_list[-2] # Accesses the second-to-last element (40)
3. Slicing: Access a range of elements using a slice, which includes a start index, end index, and
optionally a step.
sub_list = my_list[1:4] #Accesses elements from index 1 to 3 ([20, 30, 40])
sub_list = my_list[:3] #Accesses elements from the start to index 2 ([10, 20, 30])
sub_list = my_list[2:] #Accesses elements from index 2 to the end ([30, 40, 50])
4. Step in Slicing
sub_list = my_list[::2] # Accesses every second element ([10, 30, 50])
List Operations
# Concatenation
lst1 = [1, 2]
lst2 = [3, 4]
combined = lst1 + lst2 # Output: [1, 2, 3, 4]
# Repetition
repeated = lst1 * 3 # Output: [1, 2, 1, 2, 1, 2]
# Membership
is_member = 2 in lst1 # Output: True
Else Statements
1. In an if-elif-else Chain
The else block runs if none of the if or elif conditions are True.
x = 10
if x > 20:
print("x is greater than 20")
elif x > 5:
print("x is greater than 5 but less than or equal to 20")
else:
print("x is 5 or less")
i = 0
while i < 5:
print(i)
if i == 3:
break
i += 1
else:
print("Loop completed without break") # This will not be printed
4. In a try-except-finally Block
The finally block always runs, regardless of whether an exception occurred. However, you can
use else to specify code that runs only if no exceptions are raised.
try:
result = 10 / 2
except ZeroDivisionError:
print("You can't divide by zero!")
else:
print("Division successful:", result)
finally:
print("This block always runs")
2. Tuples
- ordered collection of similar and dissimilar values
- tuple is immutable (once created, it can not be changed)
- () is used to create a tuple
1. Creating Tuples
• Basic Tuple Creation:
my_tuple = (1, 2, 3)
# countries_tuple = ("india")
single_element_tuple = (1,)
• Empty Tuple:
empty_tuple = ()
• Negative Indexing:
print(my_tuple[-1]) # 30 (last element)
print(my_tuple[-2]) # 20 (second-to-last element)
• Slicing:
print(my_tuple[1:3]) # (20, 30)
print(my_tuple[:2]) # (10, 20)
print(my_tuple[1:]) # (20, 30)
3. Tuple Operations
• Concatenation:
tuple1 = (1, 2)
tuple2 = (3, 4)
concatenated_tuple = tuple1 + tuple2 # (1, 2, 3, 4)
• Repetition:
repeated_tuple = (1, 2) * 3 # (1, 2, 1, 2, 1, 2)
• Membership Testing:
print(2 in (1, 2, 3)) # True
print(4 in (1, 2, 3)) # False
• Length:
length = len((1, 2, 3)) # 3
4. Immutability
• Tuples are immutable: You cannot change, add, or remove elements once a tuple is created.
my_tuple = (1, 2, 3)
# my_tuple[1] = 4 # This will raise a TypeError
• Modifying Mutable Elements Inside Tuples: If a tuple contains mutable objects (e.g.,
lists), you can modify the mutable objects, but you cannot reassign the tuple itself.
tuple_with_list = ([1, 2], [3, 4])
tuple_with_list[0].append(3) # ([1, 2, 3], [3, 4])
n1, n2 = 10, 20
7. Tuple Methods
8. Tuple Comprehension
Unlike lists, tuples do not support list comprehensions. However, you can use a generator
expression and convert it to a tuple.
tuple_comprehension = tuple(x**2 for x in range(5)) # (0, 1, 4, 9, 16)
9. Applications of Tuples
• Dictionary Keys: Tuples can be used as keys in dictionaries because they are immutable
and hashable.
my_dict = {('a', 'b'): 1, ('c', 'd'): 2}
3. Set
- collection of unique values
- mutable collection
- unordered collection: does not honor the insertion order
- uses hashing behind the scene
- indexing does not work with set
• Empty Set: Note that an empty set must be created using the set() constructor; {}
creates an empty dictionary.
empty_set = set()
4. Set Methods
Method Description Syntax Parameters Returns Example
my_set = {1, 2, 3}
Adds an element element (the
add() set.add(element) None my_set.add(4)
to the set. element to add)
print(my_set) # {1, 2, 3, 4}
Removes a
Remove() specified element. element (the my_set = {1, 2, 3}
(raises KeyError if set.remove(element) element to None my_set.remove(2)
element not found) remove) print(my_set) # {1, 3}
Removes a
element (the my_set = {1, 2, 3}
specified element
discard() set.discard(element) element to None my_set.discard(2)
without raising
discard) print(my_set) # {1, 3}
an error.
my_set = {1, 2, 3}
Removes and
element = my_set.pop()
pop() returns an set.pop() - element
print(element)
arbitrary element.
print(my_set)
Removes all my_set = {1, 2, 3}
clear() elements from the set.clear() - None my_set.clear()
set. print(my_set) # set()
my_set = {1, 2, 3}
Returns a shallow
copy() set.copy() - set new_set = my_set.copy()
copy of the set.
print(new_set) # {1, 2, 3}
5. Set Operations
• Union: Combines elements from both sets.
set1 = {1, 2, 3}
set2 = {3, 4, 5}
union_set = set1 | set2 # {1, 2, 3, 4, 5}
# or set1.union(set2)
# or set1.intersection(set2)
• Difference: Returns elements in the first set but not in the second set.
difference_set = set1 - set2 # {1, 2}
# or set1.difference(set2)
6. Set Comprehension
squared_set = {x**2 for x in range(5)} # {0, 1, 4, 9, 16}
frozenset
def function1():
s1 = frozenset([10, 20, 30, 40, 50, 10, 20, 30, 40, 50])
print(f"s1 = {s1}, type = {type(s1)}") # type = <class 'frozenset'>
# can not be updated
# s1.add(10)
function1()
4. Dictionary
- unordered collection of key-value pairs
- mutable
- key:
- must be of data type string
- must be unique
- case sensitive
- value: can be of any data type
1. Creating Dictionaries
• Basic Dictionary Creation:
my_dict = {'key1': 'value1', 'key2': 'value2'}
2. Accessing Values
• By Key: if the key does not exist, the application will raise an exception KeyError
value = my_dict['key1'] # 'value1'
• Using get() Method: if the key does not exist, you will get None as value
value = my_dict.get('key1') # 'value1'
# You can also provide a default value if the key is not found
value = my_dict.get('key3', 'default_value') # 'default_value'
4. Dictionary Methods
6. Dictionary Comprehension
squares = {x: x**2 for x in range(6)} # {0: 0, 1: 1, 2: 4, 3: 9, 4: 16, 5: 25}
7. Use Cases
• Data Mapping: Useful for mapping relationships, such as names to phone numbers.
• Counting Occurrences: Count occurrences of items with keys.
• Configuration Settings: Store configuration settings where keys are setting names.
8. Dictionary Operations
• Checking if Key Exists:
if 'key1' in my_dict:
print("Key exists")
Functional programming
It is a programming paradigm that treats computation as the evaluation of mathematical functions
and avoids changing state and mutable data.
1. Basic Concepts of Functional Programming
• First-Class Functions: Functions are treated as first-class citizens, meaning they can be
assigned to variables, passed as arguments, and returned from other functions.
• Pure Functions: Functions that have no side effects and return the same output given the
same input.
• Higher-Order Functions: Functions that take other functions as arguments or return
functions as their results.
• Immutable Data: Data that cannot be modified after it is created.
• Recursion: Functions that call themselves as part of their execution.
2. First-Class Functions
• Assigning Functions to Variables:
def add(a, b):
return a + b
sum_function = add
print(sum_function(2, 3)) # Output: 5
result = apply_function(add, 2, 3)
print(result) # Output: 5
3. Higher-Order Functions
• Map: Applies a function to all items in an input list.
numbers = [1, 2, 3, 4]
squared = list(map(lambda x: x**2, numbers))
print(squared) # Output: [1, 4, 9, 16]
• Filter: Constructs a list from those elements of the input list for which the function returns
true.
numbers = [1, 2, 3, 4]
even = list(filter(lambda x: x % 2 == 0, numbers))
print(even) # Output: [2, 4]
4. Recursion
• Recursive Functions: Functions that call themselves.
def factorial(n):
if n == 0:
return 1
else:
return n * factorial(n-1)
list of dictionary
object creation
- when an object gets created, two memory blocks will be allocated
- one for the object (to store the data)
- second one for the reference (used to refer the object)
- every object gets created on new memory location. Every object has a unique memory address
method
- function declared inside the class
- types
1. __init__ (Initializer): Initializes an instance of the class.
• gets called automatically for every object. must not be called explicitly
• called as a constructor in other languages
• types
• default: no parameter other than self (parameterless initializer)
class Person:
def __init__(self):
print("inside __init__")
• custom(parameterized initializer): with at least one parameter other than self
class MyClass:
def __init__(self, value):
self.value = value
2. __del__ (de-initializer): used to de-initialize the object called as a destructor in other languages
def print_info(self):
print(f"model = {self.model}")
print(f"price = {self.price}")
def __del__(self):
print("-- inside __del__ method --")
def is_affordable(self):
if self.price <= 20000:
print(f"{self.model} is affordable")
else:
print(f"{self.model} is NOT affordable")
3. Getters (inspector)
4. Setters(mutator)
Purpose:
def get_age(self):
return self.__age
-getattr
The getattr function is used to retrieve the value of an attribute from an object. It takes three
arguments: getattr(object, name, default)
• object: The object from which the attribute is to be retrieved.
• name: A string representing the name of the attribute.
• default: An optional value to return if the attribute does not exist.
-setattr
The setattr function is used to set the value of an attribute on an object. It takes three
arguments: setattr(object, name, value)
• object: The object on which the attribute is to be set.
• name: A string representing the name of the attribute.
• value: The value to set for the attribute.
setattr(obj, "name", "Bob")
name = getattr(person, "name", “Unknown”)
class Mobile:
def print_info(self): # method
print(f"model = {getattr(self, 'model')}")
print(f"price = {getattr(self, 'price')}")
def set_attributes(self, model, price):
setattr(self, "model", model)
setattr(self, "price", price)
m = Mobile()
m.set_attributes("iphone", 100000)
m.print_info()
print(m.__dict__) # Output: {'model': 'iphone', 'price': 100000}
• You can use __dict__ to get a dictionary of all the attributes of an object.
# Modifying the __dict__ attribute
person.__dict__['name'] = "Bob"
person.__dict__['age'] = 40
2. Accessing Specifiers
Access Level Description Naming Convention Example Access
Public Accessible from outside the class No prefix object.attribute
Accessible only within the class, uses
Private name mangling- (python/internal) __prefix object._ClassName__attribute
feature, (compiler) version specific
Accessible from outside the class but
intended for internal use.
Protected _prefix object._attribute
Accessible in the same class and all of
its child classes
Internal Special methods for object-oriented
__prefix__suffix__ object.__method__
Methods features
3. Association (has-a)
represents associations of multiple classes
types
• aggregation
- also known as has-a relationship
- loose coupling / weak relationship: one entity can live without another entity
- e.g Student has-a address
• composition
- also known as composed-of / part-of
- tight coupling / strong relationship: one entity can not live without the other entity
- e.g. Car composed-of engine
class Employee:
def __init__(self, emp_id, name):
self.__emp_id = emp_id
self.__name = name
def print_employee_info(self):
print(f"id: {self.__emp_id}, name: {self.__name}")
class Company:
def __init__(self, name, address):
self.__name = name
self.__address = address
self.__employees = [] # Company has many Employee
def recruit(self, name, emp_id):
employee = Employee(emp_id, name)
self.__employees.append(employee)
def print_info(self):
print(f"name = {self.__name}")
print(f"address = {self.__address}")
print(f"-- employees --")
for employee in self.__employees:
employee.print_employee_info()
# recruit employees
company.recruit("employee 1", 1)
company.recruit("employee 2", 2)
- Python 3.x, every class is derived from the object class directly or indirectly
-object Class: Known as the root class or system class, it provides foundational methods like
memory management.
1. Single Inheritance: there is only one base class and only one derived class
class Person: # base class
def __init__(self, name):
self.name = name
def print_person_info(self):
print(f"name = {self.name}")
class Player(Person): # dervied class
def __init__(self, name, team):
Person.__init__(self, name)
self.team = teamclass Parent:
def parent_method(self):
print('This is a parent method')
-----------------------------------------------------------------
2. Multi-level Inheritance: there are multiple levels
- a level will have one base and one derived class
- a may have one direct and mulitple indirect base class(es)
class Person:
def __init__(self, name):
self._name = name
class Employee(Person):
def __init__(self, name, company):
Person.__init__(self, name)
self._company = company
class Manager(Employee):
def __init__(self, name, company, department):
Employee.__init__(self, name, company)
self._department = department
class Faculty:
def __init__(self, name, subject):
self._name = name
self._subject = subject
class LabAssistant:
def __init__(self, name, lab):
self._name = name
self._lab = lab
class FacultyLabAssistant(Faculty, LabAssistant):
def __init__(self, name, subject, lab):
Faculty.__init__(self, name, subject)
LabAssistant.__init__(self, name, lab)
-------------------------------------------------------------------------------
5. Polymorphism
- multiple forms of one thing
- types
- method overriding (run-time or dynamic polymorphism)
- Redefining a method in the child class that is already defined in the parent class.
- the method gets called from type of object
- compile-time polymorphism : NOT SUPPORTED n Python
- also known as function overloading
- multiple functions with same name but
- different number of parameters
- different type of parameters
- different order of parameters
Method Overriding
class Parent:
def show(self):
print('Parent method')
class Child(Parent):
def show(self):
print('Child method')
obj = Child()
obj.show() # Output: Child method
class Child(Parent):
def show(self):
super().show()
print('Child method')
obj = Child()
obj.show()
# Output:
# Parent method
# Child method
class MyClass:
def __init__(self, value):
self.value = value
def __str__(self):
return f'MyClass with value {self.value}'
obj = MyClass(10)
print(obj) # Output: MyClass with value 10
Operation Overloading
- provide different meaning to existing operators
magical methods
- mathematical operators (binary operator)
__add__: for addition
__sub__: for subtraction
__mul__: for multiplication
__truediv__: for true division (produces float result)
__floordiv__: for floor division (produces int result)
__mod__: for modulus
__pow__: for raised to (power) operation
- comparison operators (binary operator)
__gt__: for >
__lt__: for <
__ge__: for >=
__le__: for <=
__eq__: for ==
__ne__: for !=
- __getitem__: Indexing ( [] )
- __contains__: Membership testing (in)
class MyNumber:
def __init__(self, number: int):
self.__number = number
def __add__(self, other):
return self.__number + other.__number
def __pow__(self, power, modulo=None):
result = self.__number ** power
return MyNumber(result)
class Person:
def __init__(self, name, age):
self.__name = name
self.__age = age
def __gt__(self, other):
return self.__age > other.__age
class B(A):
pass
class C(A):
pass
print(D.mro())
print(D.__mro__)
def print_info(self):
print(f"model = {self.model}")
print(f"price = {self.price}")
def __del__(self):
print("-- inside __del__ method --")
def is_affordable(self):
if self.price <= 20000:
print(f"{self.model} is affordable")
else:
print(f"{self.model} is NOT affordable")
def __len__(self):
return len(self.value)
8. Encapsulation
• Private Attributes: Indicated by prefixing the attribute name with double underscores.
python
Copy code
class MyClass:
def __init__(self, value):
self.__value = value
def get_value(self):
return self.__value
obj = MyClass(10)
print(obj.get_value()) # Output: 10
# print(obj.__value) # Raises AttributeError
9. Polymorphism
• Method Polymorphism: Methods in different classes having the same name.
python
Copy code
class Cat:
def sound(self):
return 'Meow'
class Dog:
def sound(self):
return 'Bark'
def make_sound(animal):
print(animal.sound())
cat = Cat()
dog = Dog()
class Animal(ABC):
@abstractmethod
def sound(self):
pass
class Cat(Animal):
def sound(self):
return 'Meow'
cat = Cat()
print(cat.sound()) # Output: Meow
Key Concepts
1. Exception: An event that disrupts the normal flow of the program. Examples include
division by zero, accessing a variable that doesn't exist, etc.
2. Exception Handling: The process of responding to exceptions when they occur during
program execution (runtime).
- This is achieved using the try, except, else, and finally blocks.
Multiple Exceptions
try:
result = 10 / 0
except ZeroDivisionError:
print("Cannot divide by zero!")
except ValueError:
print("Invalid value!")
Catching All Exceptions: except clause catches all exceptions but is generally discouraged
because it can catch unexpected exceptions, including system-exiting exceptions.
try:
risky_code()
except:
print("An error occurred!")
else Clause
The else block runs if no exceptions were raised in the try block.
try:
result = 10 / 2
except ZeroDivisionError:
print("Cannot divide by zero!")
else:
print(f"Result is {result}")
finally Clause
The finally block always executes, regardless of whether an exception was raised or not. It is
typically used for cleanup actions.
try:
result = 10 / 2
except ZeroDivisionError:
print("Cannot divide by zero!")
finally:
print("Execution completed")
Custom Exceptions
You can define custom exceptions by creating a new class that inherits from the Exception class.
class CustomError(Exception):
def __init__(self, message):
self.message = message
try:
raise CustomError("This is a custom error")
except CustomError as e:
print(f"Caught custom exception: {e.message}")
Key Concepts
1. File Operations: Python allows for various file operations such as opening, reading,
writing, and closing files.
2. File Modes: Different modes dictate how the file is accessed (e.g., read, write, append).
File Modes
Mode Description
r Read mode. Default mode. Opens a file for reading.
Write mode. Opens a file for writing (If the file already exists, its contents are truncated (deleted), and
w
a new file is created with the same name. If the file does not exist, a new one is created.).
a Append mode. Opens a file for appending (creates a new file if it doesn't exist).
b Binary mode. Opens a file in binary mode.
t Text mode. Default mode. Opens a file in text mode.
x Exclusive creation mode. Creates a new file, fails if the file already exists.
Writing to a File
You can write to a file using the write() method. If the file does not exist, it will be created.
with open("example.txt", "w") as file:
file.write("Hello, world!")
Closing a File
Although using the with statement automatically closes the file, you can also manually close a file
using the close() method.
file = open("example.txt", "r")
# Perform file operations
file.close()
File Management
Checking if a File Exists
The os module provides methods to check if a file exists.
import os
if os.path.exists("example.txt"):
print("File exists.")
else:
print("File does not exist.")
Deleting a File
You can delete a file using the remove() method from the os module.
import os
os.remove("example.txt")
import csv
Strings
• collection of characters
• Immutable. However, you can create new strings based on operations
performed on existing strings.
Creating Strings
String Operations
# Concatenation
greeting = "Hello, " + "World!"
print(greeting) # Output: Hello, World!
# Repetition
repeated_greeting = "Hello! " * 3
print(repeated_greeting) # Output: Hello! Hello! Hello!
# Length
length_of_greeting = len(greeting)
print(length_of_greeting) # Output: 13
# Indexing
first_char = greeting[0]
print(first_char) # Output: H
# Slicing
substring = greeting[0:5]
print(substring) # Output: Hello
Formatting Strings
String Interpolation
name = "Alice"
age = 30
formatted_str = "Name: %s, Age: %d" % (name, age)
print(formatted_str) # Output: Name: Alice, Age: 30
s = b.decode("utf-8")
print(s) # Output: Hello, World!
Raw Strings
Use r prefix to treat backslashes as literal characters.
raw_str = r"This is a raw string with a newline \n"
print(raw_str) # Output: This is a raw string with a newline \n
String Methods
Para
Return
Method Description Syntax meter Example Output
s
s
xpands tabs tabsize 'hello\
Expandtabs in the string str.expandtabs(tabsize) (optio tworld'.expandtabs(4 'hello world'
() to spaces. nal) )
Converts the
string to
lowercase None New
casefold() str.casefold() 'Hello'.casefold() 'hello'
suitable for string
caseless
comparison.
width,
Centers the
fillcha
string in a New
center() str.center(width[, fillchar]) r 'hello'.center(10, '-') '--hello---'
field of a string
(optio
given width.
nal)
Format Types
Format Type Description Example Output
"{:<10}".format("Hell
:< Left-align within the specified width Hello
o")
"{:>10}".format("Hell
:> Right-align within the specified width Hello
o")
"{:^10}".format("Hell
:^ Center-align within the specified width Hello
o")
:0 Pad with zeros "{:05}".format(42) 00042
"{:10}".format("Hello
Width Minimum width of the output Hello
")
Number of digits after the decimal point for floating- "{:.2f}".format(3.141 3.14
Precision 59)
point numbers
d Decimal integer "{:d}".format(255) 255
x Hexadecimal (lowercase) "{:x}".format(255) ff
X Hexadecimal (uppercase) "{:X}".format(255) FF
b Binary "{:b}".format(255) 11111111
Format Type Description Example Output
o Octal "{:o}".format(255) 377
"{:.2f}".format(3.141
f Fixed-point number 3.14
59)
"{:.2e}".format(3.141
e Exponential notation (lowercase) 3.14e+00
59)
"{:.2E}".format(3.141
E Exponential notation (uppercase) 3.14E+00
59)
"{:s}".format("Hello"
s String (default) Hello
)
% Percentage "{:.2%}".format(0.25) 25.00%
module
importing a module
# mymodule.py
class Person:
pass
def my_function():
pass
# page1.py
# import all the entities from mymodule
import mymodule
p1 = mymodule.Person()
# page2.py
# import only Person class from mymodule
from mymodule import Person
p1 = Person()
# page3.py
# MyPerson will be created as an alias of Person
from mymodule import Person as MyPerson
p1 = MyPerson()
# page4.py
# mm will be created as an alias for mymodule
import mymodule as mm
p1 = mm.Person()
package
- collection of modules
- folder with a file named __init__.py
e.g.
# mypackage (package)
# - files (sub-package)
# - myfile (module)
# - Person (class)
# - Car (class)
2. *Basic Syntax*
- Variables and Data Types (int, float, str, bool)
- Basic Operators (Arithmetic, Comparison, Logical)
- Control Flow (if, else, elif)
- Loops (for, while, nested loops)
- Comments and Docstrings
3. *Data Structures*
- Lists (creation, indexing, slicing, methods)
- Tuples (immutability, packing/unpacking)
- Dictionaries (key-value pairs, methods)
- Sets (unique elements, set operations)
4. *Functions*
- Defining and Calling Functions
- Function Arguments (default, keyword, arbitrary)
- Return Values
- Lambda Functions
- Scope and Lifetime of Variables
6. *File Handling*
- Reading and Writing Text Files
- Working with CSV Files
- JSON Serialization and Deserialization
- File Methods and Context Managers
2. *Exception Handling*
- Try, Except, Finally Blocks
- Raising Exceptions
- Custom Exception Classes
- Handling Multiple Exceptions
5. *Decorators*
- Function Decorators
- Class Decorators
- Using functools.wraps
- Practical Use Cases (logging, access control)
6. *Regular Expressions*
- Basic Patterns and Syntax
- Matching and Searching
- Substitution and Splitting
- Using the re Module
2. *Concurrency*
- Multithreading (threading module)
- Multiprocessing (multiprocessing module)
- Asynchronous Programming (asyncio, await, async)
- Concurrent Futures
3. *Networking*
- Sockets (TCP/IP, UDP)
- HTTP Requests (using requests library)
- Web Scraping (BeautifulSoup, Scrapy)
- Building Simple Web Servers
4. *Database Interaction*
- SQL Basics (CRUD operations)
- SQLite (using sqlite3 module)
- Object-Relational Mapping (ORM) with SQLAlchemy
- NoSQL Databases (MongoDB with PyMongo)
5. *Testing*
- Unit Testing (unittest, pytest)
- Integration Testing
- Mocking and Patching
- Test-Driven Development (TDD)
6. *Memory Management*
- Understanding Memory Allocation
- Garbage Collection
- Memory Profiling (using memory_profiler, tracemalloc)
- Optimizing Memory Usage
3. *Security*
- Cryptography (using cryptography library)
- Secure Coding Practices
- Authentication and Authorization
- Secure Communication (SSL/TLS)
4. *Deployment*
- Docker (containerization)
- Continuous Integration/Continuous Deployment (CI/CD)
- Cloud Deployment (AWS, Azure, GCP)
- Serverless Computing
5. *Metaprogramming*
- Introspection (using dir(), getattr(), setattr())
- Code Generation (using exec(), eval())
- Decorators and Metaclasses
- Dynamic Code Execution
6. *Advanced Topics*
- Design Patterns (Singleton, Factory, Observer)
- Domain-Specific Languages (DSLs)
- Advanced Algorithms and Data Structures
- Contributing to Open Source Projects