Python Notes
# use of setter and getters in python?
In Python, getters and setters are used to control access to the
attributes of a class. While Python allows direct access to object
attributes, using getters and setters provides encapsulation,
validation, and control over attribute behavior.
What Are Getters and Setters?
• Getters: Methods that allow you to access the value of a private
attribute.
• Setters: Methods that allow you to set or update the value of a
private attribute with control or validation.
Why Use Getters and Setters?
• To hide internal data.
• To add validation logic when attributes are set.
• To make code more maintainable and flexible.
class Person:
def __init__(self, name):
self._name = name # _name is a 'protected' attribute
def get_name(self):
return self._name
def set_name(self, value):
if isinstance(value, str) and [Link]():
self._name = value
else:
raise ValueError("Name must be a non-empty string")
p = Person("Alice")
print(p.get_name()) # Get name
p.set_name("Bob") # Set name
print(p.get_name())
Feature Without Decorators With @property Decorators
Getter obj.get_attr() [Link]
Setter obj.set_attr(value) [Link] = value
Cleaner
Less Pythonic More Pythonic
Code
Manual method Looks like direct attribute
Readability
calls access
# explain the concept of list comprehension in python
Syntax:
[expression for item in iterable if condition]
1. Create a list of numbers from 1 to 5
nums = [x for x in range(1, 6)]
print(nums) # Output: [1, 2, 3, 4, 5]
2. Square of each number in a list
squares = [x*x for x in range(1, 6)]
print(squares) # Output: [1, 4, 9, 16, 25]
3. Only even numbers
evens = [x for x in range(1, 11) if x % 2 == 0]
print(evens) # Output: [2, 4, 6, 8, 10]
4. Using if-else in list comprehension
labels = ['even' if x % 2 == 0 else 'odd' for x in range(1, 6)]
print(labels) # Output: ['odd', 'even', 'odd', 'even', 'odd']
5. Flatten a nested list (2D list to 1D)
matrix = [[1, 2], [3, 4], [5, 6]]
flattened = [num for row in matrix for num in row]
print(flattened) # Output: [1, 2, 3, 4, 5, 6]
Feature Benefit
Shorter code Less lines than a loop
Faster Slight performance boost
Cleaner More readable when used properly
# define constructor and differentiate btw instance
and class variable in python ?
What is a Constructor in Python?
A constructor is a special method used to initialize objects of
a class.
In Python, the constructor method is:
def __init__(self):
# initialization code
• It’s called automatically when a new object is created.
• self refers to the current instance of the class.
Example:
class Student:
def __init__(self, name, age):
[Link] = name # instance variable
[Link] = age # instance variable
s1 = Student("Alice", 20)
print([Link]) # Output: Alice
Feature Instance Variable Class Variable
Inside constructor Inside class, but outside
Defined In
(__init__) methods
A specific object The class itself (shared by
Belongs To
(instance) all)
Accessed [Link] or
[Link]
With [Link]
Different for each Same for all objects unless
Uniqueness object overridden
Modified Each object One place — affects all
By separately objects
Concept Constructor Instance Variable Class Variable
What is Special init Object-specific Class-wide shared
it? method attribute data
Declared [Link] = Outside methods in
__init__()
in value class
Accessed Auto on object [Link] or
[Link]
by creation [Link]
No (unique Yes (shared
Shared? N/A
per object) across all)
# differentiate btw compiler and interpretor
Feature Compiler Interpreter
Translates entire code into
Translates and executes
Definition machine code before
code line by line
execution
Translation Whole program is compiled Code is translated and
Process at once executed line-by-line
Execution Slower due to line-by-
Fast after compilation
Speed line execution
Error Finds all errors at once Finds and reports errors
Detection during compilation line by line
Memory More memory-efficient Uses more memory
Usage after compilation during execution
No executable file; runs
Output Produces an executable file directly from source
code
Examples C, C++, Java Python, Ruby, PHP
More portable as long
Can be platform-
Portability as interpreter is
dependent
available
No. Feature Compiler Interpreter
Translates entire Translates and
1 Definition source code into executes source code
machine code at once line by line
Translates the whole Translates and
Execution
2 program before executes code line by
Process
execution line
Speed of Faster execution (after Slower execution (line-
3
Execution compilation) by-line translation)
Detects all errors at
Error Detects errors line by
4 once during
Detection line during execution
compilation
Requires more
Efficient in memory
Memory memory as it holds
5 usage after
Usage source code in
compilation
memory
Produces an Does not produce an
6 Output executable file (e.g., output file; executes
.exe, .out) code directly
Generates machine
Intermediate No intermediate code
7 code or intermediate
Code generated
code
Once compiled, no
Needs the source code
8 Dependency need for source code
every time it executes
during execution
Language C, C++, Java (compiles
9 Python, Ruby, PHP
Examples to bytecode in Java)
Can be platform- More portable as long
10 Portability dependent unless it's as the interpreter is
bytecode (Java) installed
May require Runs on any platform
Platform
11 recompilation for where the interpreter
Dependency
different platforms is available
After compilation, Requires more
Memory
12 memory usage is memory during
Consumption
generally low execution
Easier to debug as
More challenging since
errors are detected
13 Debugging errors are detected
immediately during
after compilation
execution
Executes faster after Executes slower, line-
14 Execution Type
compiling by-line
Ideal for large
Suitable for scripting,
Usage programs and
15 interactive sessions,
Scenario applications that need
and debugging
fast execution
# explain the concept of indexing and slicing in python
Indexing in Python
Indexing allows you to access individual elements of an ordered
collection like lists, tuples, strings, etc.
• Index refers to the position of an element in the sequence.
• Python uses zero-based indexing, meaning the first element is
at index 0.
Example of Indexing:
# List Example
my_list = [10, 20, 30, 40, 50]
print(my_list[0]) # Output: 10 (First element)
print(my_list[3]) # Output: 40 (Fourth element)
# String Example
my_string = "Hello"
print(my_string[1]) # Output: 'e' (Second character)
Negative Indexing:
• Negative indices allow you to access elements from the end of
the collection.
• -1 refers to the last element, -2 refers to the second last, and so
on.
# List Example
my_list = [10, 20, 30, 40, 50]
print(my_list[-1]) # Output: 50 (Last element)
print(my_list[-2]) # Output: 40 (Second last element)
Slicing in Python
Slicing allows you to extract a subsequence (part of the sequence)
from a collection like a list, tuple, or string.
Syntax of Slicing:
sequence[start:end:step]
• start (inclusive): The index where slicing begins (default is 0).
• end (exclusive): The index where slicing ends (default is the end
of the sequence).
• step (optional): The interval between elements (default is 1).
Examples of Slicing:
# List Example
my_list = [10, 20, 30, 40, 50]
# Slicing from index 1 to 3 (not including index 3)
print(my_list[1:3]) # Output: [20, 30]
# Slicing from index 2 to end
print(my_list[2:]) # Output: [30, 40, 50]
# Slicing from the beginning to index 3 (not including index 3)
print(my_list[:3]) # Output: [10, 20, 30]
# Slicing with a step of 2
print(my_list[::2]) # Output: [10, 30, 50]
String Slicing:
# String Example
my_string = "Hello, World!"
# Extracting from index 7 to 12 (not including index 12)
print(my_string[7:12]) # Output: World
# Slicing with a step of 2 (Every other character)
print(my_string[::2]) # Output: Hoo ol!
Negative Indexing in Slicing:
You can use negative indices in slicing to start from the end of the
sequence.
# List Example
my_list = [10, 20, 30, 40, 50]
# Slicing from the second last element to the first
print(my_list[-2:]) # Output: [40, 50]
# Slicing from the second last element to the second element from
the beginning
print(my_list[-2:-1]) # Output: [40]
Summary:
• Indexing allows you to access a single element using its index in
the sequence.
• Slicing allows you to extract a sublist/substring using a range of
indices with an optional step.
Feature Indexing Slicing
Accessing a single Extracting a subsequence (part
Purpose
element of the list)
Syntax sequence[index] sequence[start:end:step]
Single element A new sequence (list, string,
Return Type
(value) etc.)
Supports
Negative Yes Yes
Indices
For mutable
Returns a new list/tuple/string,
Mutability sequences like lists
doesn't modify the original
(not strings)
my_list = [10, 20, 30, 40, 50, 60, 70]
# Indexing:
print(my_list[2]) # Output: 30 (Access third element)
# Slicing:
print(my_list[1:5]) # Output: [20, 30, 40, 50] (Elements from index 1
to 4)
print(my_list[::2]) # Output: [10, 30, 50, 70] (Every second element)