

| Category | Keywords |
|---|---|
| Control Flow | if, else, elif, while, for, break, continue, pass |
| Boolean Logic | True, False, and, or, not, is, in |
| Functions | def, return, lambda, yield, global, nonlocal |
| Classes & OOP | class, del, self, super |
| Exception Handling | try, except, finally, raise, assert |
| Variable Control | None, as, with, from, import |
| Miscellaneous | await, async, match, case (Python 3.10+) |
| Declarations | True, False, None |
import keyword
print(keyword.kwlist)
['False', 'None', 'True', 'and', 'as', 'assert', 'async', 'await', 'break', 'class', 'continue', 'def', 'del', 'elif', 'else', 'except', 'finally', 'for', 'from', 'global', 'if', 'import', 'in', 'is', 'lambda', 'nonlocal', 'not', 'or', 'pass', 'raise', 'return', 'try', 'while', 'with', 'yield']
A function is a block of reusable code that performs a specific task. Functions help in:
Built-in Functions: Already available in Python.
🔹 Examples: print(), len(), type(), sum(), range()
User-defined Functions: Functions you write yourself using def keyword.
Lambda Functions: Short anonymous functions created using lambda keyword.
def greet():
print("Hello, World!")
greet() # Output: Hello, World!
def greet(name):
print(f"Hello, {name}!")
greet("Purushotham") # Output: Hello, Purushotham!
def add(x, y):
return x + y
result = add(5, 3)
print(result) # Output: 8
def greet(name="User"):
print(f"Hello, {name}!")
greet() # Output: Hello, User!
greet("AI Bro") # Output: Hello, AI Bro!
*args → multiple positional arguments (tuple)**kwargs → multiple keyword arguments (dict)def show_items(*args):
for item in args:
print(item)
show_items("Pen", "Notebook", "Pencil")
Short, anonymous function (used for simple operations)
square = lambda x: x**2
print(square(5)) # Output: 25
# Documentation (also called a docstring) is a special string that explains what a function does, its parameters, return values, and usage.
def add(a, b):
"""
Adds two numbers.
Parameters:
a (int or float): First number
b (int or float): Second number
Returns:
int or float: The sum of a and b
"""
return a + b
# Accessing Documentation
print(add.__doc__)
Adds two numbers.
Parameters:
a (int or float): First number
b (int or float): Second number
Returns:
int or float: The sum of a and b
help(add)
Help on function add in module __main__:
add(a, b)
Adds two numbers.
Parameters:
a (int or float): First number
b (int or float): Second number
Returns:
int or float: The sum of a and b
help(str)
Help on class str in module builtins:
class str(object)
| str(object='') -> str
| str(bytes_or_buffer[, encoding[, errors]]) -> str
|
| Create a new string object from the given object. If encoding or
| errors is specified, then the object must expose a data buffer
| that will be decoded using the given encoding and error handler.
| Otherwise, returns the result of object.__str__() (if defined)
| or repr(object).
| encoding defaults to sys.getdefaultencoding().
| errors defaults to 'strict'.
|
| Methods defined here:
|
| __add__(self, value, /)
| Return self+value.
|
| __contains__(self, key, /)
| Return key in self.
|
| __eq__(self, value, /)
| Return self==value.
|
| __format__(self, format_spec, /)
| Return a formatted version of the string as described by format_spec.
|
| __ge__(self, value, /)
| Return self>=value.
|
| __getattribute__(self, name, /)
| Return getattr(self, name).
|
| __getitem__(self, key, /)
| Return self[key].
|
| __getnewargs__(...)
|
| __gt__(self, value, /)
| Return self>value.
|
| __hash__(self, /)
| Return hash(self).
|
| __iter__(self, /)
| Implement iter(self).
|
| __le__(self, value, /)
| Return self<=value.
|
| __len__(self, /)
| Return len(self).
|
| __lt__(self, value, /)
| Return self<value.
|
| __mod__(self, value, /)
| Return self%value.
|
| __mul__(self, value, /)
| Return self*value.
|
| __ne__(self, value, /)
| Return self!=value.
|
| __repr__(self, /)
| Return repr(self).
|
| __rmod__(self, value, /)
| Return value%self.
|
| __rmul__(self, value, /)
| Return value*self.
|
| __sizeof__(self, /)
| Return the size of the string in memory, in bytes.
|
| __str__(self, /)
| Return str(self).
|
| capitalize(self, /)
| Return a capitalized version of the string.
|
| More specifically, make the first character have upper case and the rest lower
| case.
|
| casefold(self, /)
| Return a version of the string suitable for caseless comparisons.
|
| center(self, width, fillchar=' ', /)
| Return a centered string of length width.
|
| Padding is done using the specified fill character (default is a space).
|
| count(...)
| S.count(sub[, start[, end]]) -> int
|
| Return the number of non-overlapping occurrences of substring sub in
| string S[start:end]. Optional arguments start and end are
| interpreted as in slice notation.
|
| encode(self, /, encoding='utf-8', errors='strict')
| Encode the string using the codec registered for encoding.
|
| encoding
| The encoding in which to encode the string.
| errors
| The error handling scheme to use for encoding errors.
| The default is 'strict' meaning that encoding errors raise a
| UnicodeEncodeError. Other possible values are 'ignore', 'replace' and
| 'xmlcharrefreplace' as well as any other name registered with
| codecs.register_error that can handle UnicodeEncodeErrors.
|
| endswith(...)
| S.endswith(suffix[, start[, end]]) -> bool
|
| Return True if S ends with the specified suffix, False otherwise.
| With optional start, test S beginning at that position.
| With optional end, stop comparing S at that position.
| suffix can also be a tuple of strings to try.
|
| expandtabs(self, /, tabsize=8)
| Return a copy where all tab characters are expanded using spaces.
|
| If tabsize is not given, a tab size of 8 characters is assumed.
|
| find(...)
| S.find(sub[, start[, end]]) -> int
|
| Return the lowest index in S where substring sub is found,
| such that sub is contained within S[start:end]. Optional
| arguments start and end are interpreted as in slice notation.
|
| Return -1 on failure.
|
| format(...)
| S.format(*args, **kwargs) -> str
|
| Return a formatted version of S, using substitutions from args and kwargs.
| The substitutions are identified by braces ('{' and '}').
|
| format_map(...)
| S.format_map(mapping) -> str
|
| Return a formatted version of S, using substitutions from mapping.
| The substitutions are identified by braces ('{' and '}').
|
| index(...)
| S.index(sub[, start[, end]]) -> int
|
| Return the lowest index in S where substring sub is found,
| such that sub is contained within S[start:end]. Optional
| arguments start and end are interpreted as in slice notation.
|
| Raises ValueError when the substring is not found.
|
| isalnum(self, /)
| Return True if the string is an alpha-numeric string, False otherwise.
|
| A string is alpha-numeric if all characters in the string are alpha-numeric and
| there is at least one character in the string.
|
| isalpha(self, /)
| Return True if the string is an alphabetic string, False otherwise.
|
| A string is alphabetic if all characters in the string are alphabetic and there
| is at least one character in the string.
|
| isascii(self, /)
| Return True if all characters in the string are ASCII, False otherwise.
|
| ASCII characters have code points in the range U+0000-U+007F.
| Empty string is ASCII too.
|
| isdecimal(self, /)
| Return True if the string is a decimal string, False otherwise.
|
| A string is a decimal string if all characters in the string are decimal and
| there is at least one character in the string.
|
| isdigit(self, /)
| Return True if the string is a digit string, False otherwise.
|
| A string is a digit string if all characters in the string are digits and there
| is at least one character in the string.
|
| isidentifier(self, /)
| Return True if the string is a valid Python identifier, False otherwise.
|
| Call keyword.iskeyword(s) to test whether string s is a reserved identifier,
| such as "def" or "class".
|
| islower(self, /)
| Return True if the string is a lowercase string, False otherwise.
|
| A string is lowercase if all cased characters in the string are lowercase and
| there is at least one cased character in the string.
|
| isnumeric(self, /)
| Return True if the string is a numeric string, False otherwise.
|
| A string is numeric if all characters in the string are numeric and there is at
| least one character in the string.
|
| isprintable(self, /)
| Return True if the string is printable, False otherwise.
|
| A string is printable if all of its characters are considered printable in
| repr() or if it is empty.
|
| isspace(self, /)
| Return True if the string is a whitespace string, False otherwise.
|
| A string is whitespace if all characters in the string are whitespace and there
| is at least one character in the string.
|
| istitle(self, /)
| Return True if the string is a title-cased string, False otherwise.
|
| In a title-cased string, upper- and title-case characters may only
| follow uncased characters and lowercase characters only cased ones.
|
| isupper(self, /)
| Return True if the string is an uppercase string, False otherwise.
|
| A string is uppercase if all cased characters in the string are uppercase and
| there is at least one cased character in the string.
|
| join(self, iterable, /)
| Concatenate any number of strings.
|
| The string whose method is called is inserted in between each given string.
| The result is returned as a new string.
|
| Example: '.'.join(['ab', 'pq', 'rs']) -> 'ab.pq.rs'
|
| ljust(self, width, fillchar=' ', /)
| Return a left-justified string of length width.
|
| Padding is done using the specified fill character (default is a space).
|
| lower(self, /)
| Return a copy of the string converted to lowercase.
|
| lstrip(self, chars=None, /)
| Return a copy of the string with leading whitespace removed.
|
| If chars is given and not None, remove characters in chars instead.
|
| partition(self, sep, /)
| Partition the string into three parts using the given separator.
|
| This will search for the separator in the string. If the separator is found,
| returns a 3-tuple containing the part before the separator, the separator
| itself, and the part after it.
|
| If the separator is not found, returns a 3-tuple containing the original string
| and two empty strings.
|
| removeprefix(self, prefix, /)
| Return a str with the given prefix string removed if present.
|
| If the string starts with the prefix string, return string[len(prefix):].
| Otherwise, return a copy of the original string.
|
| removesuffix(self, suffix, /)
| Return a str with the given suffix string removed if present.
|
| If the string ends with the suffix string and that suffix is not empty,
| return string[:-len(suffix)]. Otherwise, return a copy of the original
| string.
|
| replace(self, old, new, count=-1, /)
| Return a copy with all occurrences of substring old replaced by new.
|
| count
| Maximum number of occurrences to replace.
| -1 (the default value) means replace all occurrences.
|
| If the optional argument count is given, only the first count occurrences are
| replaced.
|
| rfind(...)
| S.rfind(sub[, start[, end]]) -> int
|
| Return the highest index in S where substring sub is found,
| such that sub is contained within S[start:end]. Optional
| arguments start and end are interpreted as in slice notation.
|
| Return -1 on failure.
|
| rindex(...)
| S.rindex(sub[, start[, end]]) -> int
|
| Return the highest index in S where substring sub is found,
| such that sub is contained within S[start:end]. Optional
| arguments start and end are interpreted as in slice notation.
|
| Raises ValueError when the substring is not found.
|
| rjust(self, width, fillchar=' ', /)
| Return a right-justified string of length width.
|
| Padding is done using the specified fill character (default is a space).
|
| rpartition(self, sep, /)
| Partition the string into three parts using the given separator.
|
| This will search for the separator in the string, starting at the end. If
| the separator is found, returns a 3-tuple containing the part before the
| separator, the separator itself, and the part after it.
|
| If the separator is not found, returns a 3-tuple containing two empty strings
| and the original string.
|
| rsplit(self, /, sep=None, maxsplit=-1)
| Return a list of the substrings in the string, using sep as the separator string.
|
| sep
| The separator used to split the string.
|
| When set to None (the default value), will split on any whitespace
| character (including \n \r \t \f and spaces) and will discard
| empty strings from the result.
| maxsplit
| Maximum number of splits.
| -1 (the default value) means no limit.
|
| Splitting starts at the end of the string and works to the front.
|
| rstrip(self, chars=None, /)
| Return a copy of the string with trailing whitespace removed.
|
| If chars is given and not None, remove characters in chars instead.
|
| split(self, /, sep=None, maxsplit=-1)
| Return a list of the substrings in the string, using sep as the separator string.
|
| sep
| The separator used to split the string.
|
| When set to None (the default value), will split on any whitespace
| character (including \n \r \t \f and spaces) and will discard
| empty strings from the result.
| maxsplit
| Maximum number of splits.
| -1 (the default value) means no limit.
|
| Splitting starts at the front of the string and works to the end.
|
| Note, str.split() is mainly useful for data that has been intentionally
| delimited. With natural text that includes punctuation, consider using
| the regular expression module.
|
| splitlines(self, /, keepends=False)
| Return a list of the lines in the string, breaking at line boundaries.
|
| Line breaks are not included in the resulting list unless keepends is given and
| true.
|
| startswith(...)
| S.startswith(prefix[, start[, end]]) -> bool
|
| Return True if S starts with the specified prefix, False otherwise.
| With optional start, test S beginning at that position.
| With optional end, stop comparing S at that position.
| prefix can also be a tuple of strings to try.
|
| strip(self, chars=None, /)
| Return a copy of the string with leading and trailing whitespace removed.
|
| If chars is given and not None, remove characters in chars instead.
|
| swapcase(self, /)
| Convert uppercase characters to lowercase and lowercase characters to uppercase.
|
| title(self, /)
| Return a version of the string where each word is titlecased.
|
| More specifically, words start with uppercased characters and all remaining
| cased characters have lower case.
|
| translate(self, table, /)
| Replace each character in the string using the given translation table.
|
| table
| Translation table, which must be a mapping of Unicode ordinals to
| Unicode ordinals, strings, or None.
|
| The table must implement lookup/indexing via __getitem__, for instance a
| dictionary or list. If this operation raises LookupError, the character is
| left untouched. Characters mapped to None are deleted.
|
| upper(self, /)
| Return a copy of the string converted to uppercase.
|
| zfill(self, width, /)
| Pad a numeric string with zeros on the left, to fill a field of the given width.
|
| The string is never truncated.
|
| ----------------------------------------------------------------------
| Static methods defined here:
|
| __new__(*args, **kwargs)
| Create and return a new object. See help(type) for accurate signature.
|
| maketrans(...)
| Return a translation table usable for str.translate().
|
| If there is only one argument, it must be a dictionary mapping Unicode
| ordinals (integers) or characters to Unicode ordinals, strings or None.
| Character keys will be then converted to ordinals.
| If there are two arguments, they must be strings of equal length, and
| in the resulting dictionary, each character in x will be mapped to the
| character at the same position in y. If there is a third argument, it
| must be a string, whose characters will be mapped to None in the result.
help(list)
Help on class list in module builtins: class list(object) | list(iterable=(), /) | | Built-in mutable sequence. | | If no argument is given, the constructor creates a new empty list. | The argument must be an iterable if specified. | | Methods defined here: | | __add__(self, value, /) | Return self+value. | | __contains__(self, key, /) | Return key in self. | | __delitem__(self, key, /) | Delete self[key]. | | __eq__(self, value, /) | Return self==value. | | __ge__(self, value, /) | Return self>=value. | | __getattribute__(self, name, /) | Return getattr(self, name). | | __getitem__(...) | x.__getitem__(y) <==> x[y] | | __gt__(self, value, /) | Return self>value. | | __iadd__(self, value, /) | Implement self+=value. | | __imul__(self, value, /) | Implement self*=value. | | __init__(self, /, *args, **kwargs) | Initialize self. See help(type(self)) for accurate signature. | | __iter__(self, /) | Implement iter(self). | | __le__(self, value, /) | Return self<=value. | | __len__(self, /) | Return len(self). | | __lt__(self, value, /) | Return self<value. | | __mul__(self, value, /) | Return self*value. | | __ne__(self, value, /) | Return self!=value. | | __repr__(self, /) | Return repr(self). | | __reversed__(self, /) | Return a reverse iterator over the list. | | __rmul__(self, value, /) | Return value*self. | | __setitem__(self, key, value, /) | Set self[key] to value. | | __sizeof__(self, /) | Return the size of the list in memory, in bytes. | | append(self, object, /) | Append object to the end of the list. | | clear(self, /) | Remove all items from list. | | copy(self, /) | Return a shallow copy of the list. | | count(self, value, /) | Return number of occurrences of value. | | extend(self, iterable, /) | Extend list by appending elements from the iterable. | | index(self, value, start=0, stop=9223372036854775807, /) | Return first index of value. | | Raises ValueError if the value is not present. | | insert(self, index, object, /) | Insert object before index. | | pop(self, index=-1, /) | Remove and return item at index (default last). | | Raises IndexError if list is empty or index is out of range. | | remove(self, value, /) | Remove first occurrence of value. | | Raises ValueError if the value is not present. | | reverse(self, /) | Reverse *IN PLACE*. | | sort(self, /, *, key=None, reverse=False) | Sort the list in ascending order and return None. | | The sort is in-place (i.e. the list itself is modified) and stable (i.e. the | order of two equal elements is maintained). | | If a key function is given, apply it once to each list item and sort them, | ascending or descending, according to their function values. | | The reverse flag can be set to sort in descending order. | | ---------------------------------------------------------------------- | Class methods defined here: | | __class_getitem__(...) | See PEP 585 | | ---------------------------------------------------------------------- | Static methods defined here: | | __new__(*args, **kwargs) | Create and return a new object. See help(type) for accurate signature. | | ---------------------------------------------------------------------- | Data and other attributes defined here: | | __hash__ = None
help(dict)
Help on class dict in module builtins:
class dict(object)
| dict() -> new empty dictionary
| dict(mapping) -> new dictionary initialized from a mapping object's
| (key, value) pairs
| dict(iterable) -> new dictionary initialized as if via:
| d = {}
| for k, v in iterable:
| d[k] = v
| dict(**kwargs) -> new dictionary initialized with the name=value pairs
| in the keyword argument list. For example: dict(one=1, two=2)
|
| Built-in subclasses:
| StgDict
|
| Methods defined here:
|
| __contains__(self, key, /)
| True if the dictionary has the specified key, else False.
|
| __delitem__(self, key, /)
| Delete self[key].
|
| __eq__(self, value, /)
| Return self==value.
|
| __ge__(self, value, /)
| Return self>=value.
|
| __getattribute__(self, name, /)
| Return getattr(self, name).
|
| __getitem__(...)
| x.__getitem__(y) <==> x[y]
|
| __gt__(self, value, /)
| Return self>value.
|
| __init__(self, /, *args, **kwargs)
| Initialize self. See help(type(self)) for accurate signature.
|
| __ior__(self, value, /)
| Return self|=value.
|
| __iter__(self, /)
| Implement iter(self).
|
| __le__(self, value, /)
| Return self<=value.
|
| __len__(self, /)
| Return len(self).
|
| __lt__(self, value, /)
| Return self<value.
|
| __ne__(self, value, /)
| Return self!=value.
|
| __or__(self, value, /)
| Return self|value.
|
| __repr__(self, /)
| Return repr(self).
|
| __reversed__(self, /)
| Return a reverse iterator over the dict keys.
|
| __ror__(self, value, /)
| Return value|self.
|
| __setitem__(self, key, value, /)
| Set self[key] to value.
|
| __sizeof__(...)
| D.__sizeof__() -> size of D in memory, in bytes
|
| clear(...)
| D.clear() -> None. Remove all items from D.
|
| copy(...)
| D.copy() -> a shallow copy of D
|
| get(self, key, default=None, /)
| Return the value for key if key is in the dictionary, else default.
|
| items(...)
| D.items() -> a set-like object providing a view on D's items
|
| keys(...)
| D.keys() -> a set-like object providing a view on D's keys
|
| pop(...)
| D.pop(k[,d]) -> v, remove specified key and return the corresponding value.
|
| If the key is not found, return the default if given; otherwise,
| raise a KeyError.
|
| popitem(self, /)
| Remove and return a (key, value) pair as a 2-tuple.
|
| Pairs are returned in LIFO (last-in, first-out) order.
| Raises KeyError if the dict is empty.
|
| setdefault(self, key, default=None, /)
| Insert key with a value of default if key is not in the dictionary.
|
| Return the value for key if key is in the dictionary, else default.
|
| update(...)
| D.update([E, ]**F) -> None. Update D from dict/iterable E and F.
| If E is present and has a .keys() method, then does: for k in E: D[k] = E[k]
| If E is present and lacks a .keys() method, then does: for k, v in E: D[k] = v
| In either case, this is followed by: for k in F: D[k] = F[k]
|
| values(...)
| D.values() -> an object providing a view on D's values
|
| ----------------------------------------------------------------------
| Class methods defined here:
|
| __class_getitem__(...)
| See PEP 585
|
| fromkeys(iterable, value=None, /)
| Create a new dictionary with keys from iterable and values set to value.
|
| ----------------------------------------------------------------------
| Static methods defined here:
|
| __new__(*args, **kwargs)
| Create and return a new object. See help(type) for accurate signature.
|
| ----------------------------------------------------------------------
| Data and other attributes defined here:
|
| __hash__ = None

A Higher-Order Function is a function that:
In short:
"Functions that work with other functions."
def greet(name):
return f"Hello, {name}!"
def call_func(func, value):
return func(value)
print(call_func(greet, "Purushotham"))
# Output: Hello, Purushotham!
def outer(x):
def inner(y):
return x + y
return inner
add5 = outer(5)
print(add5(10)) # Output: 15
Python provides several built-in higher-order functions:
map(function, iterable)Applies a function to every item in an iterable.
nums = [1, 2, 3, 4]
squares = list(map(lambda x: x**2, nums))
print(squares) # Output: [1, 4, 9, 16]
filter(function, iterable)Filters items where the function returns True.
nums = [1, 2, 3, 4, 5]
evens = list(filter(lambda x: x % 2 == 0, nums))
print(evens) # Output: [2, 4]
reduce(function, iterable)Applies a function cumulatively to reduce the iterable to a single value.
Requires:
from functools import reduce
from functools import reduce
nums = [1, 2, 3, 4]
product = reduce(lambda x, y: x * y, nums)
print(product) # Output: 24
from functools import reduce
nums = [1, 2, 3, 4, 5]
# Filter even numbers, square them, then sum the result
result = reduce(lambda x, y: x + y,
map(lambda x: x**2,
filter(lambda x: x % 2 == 0, nums)))
print(result) # Output: 20 (2² + 4² = 4 + 16)
A decorator modifies or enhances the behavior of a function without changing its code.
def my_decorator(func):
def wrapper():
print("Before call")
func()
print("After call")
return wrapper
@my_decorator
def say_hello():
print("Hello!")
say_hello()
A closure remembers the environment in which it was created.
def outer(x):
def inner(y):
return x + y
return inner
add5 = outer(5)
print(add5(3)) # Output: 8
In Python, functions can be:
def shout(text):
return text.upper()
def whisper(text):
return text.lower()
def greet(func):
print(func("Hello"))
greet(shout)
greet(whisper)
*args and `kwargs` - Advanced Use**def demo(*args, **kwargs):
print("Positional:", args)
print("Keyword:", kwargs)
demo(1, 2, 3, name="Purushotham", job="ML")
functools.partial)Fix certain arguments of a function and generate a new function.
from functools import partial
def power(base, exponent):
return base ** exponent
square = partial(power, exponent=2)
print(square(5)) # Output: 25
from functools import reduce
nums = [1, 2, 3, 4]
# map: apply function to each item
squares = list(map(lambda x: x**2, nums))
# filter: filter elements
even = list(filter(lambda x: x % 2 == 0, nums))
# reduce: accumulate results
product = reduce(lambda x, y: x * y, nums)
yield)Efficient memory handling for large sequences.
def count_up_to(n):
i = 1
while i <= n:
yield i
i += 1
for num in count_up_to(5):
print(num)
Improves readability and debugging.
def greet(name: str) -> str:
return f"Hello, {name}"
Use built-in tools to examine functions:
def sample(x: int) -> int:
return x * 2
print(sample.__name__)
print(sample.__annotations__)
print(callable(sample))
from functools import lru_cache
@lru_cache(maxsize=1000)
def fib(n):
if n < 2:
return n
return fib(n-1) + fib(n-2)
Object-Oriented Programming (OOP) is a programming paradigm centered around objects and classes. Python supports OOP and allows you to model real-world entities.
| Concept | Description |
|---|---|
| Class | Blueprint for creating objects |
| Object | Instance of a class |
| Constructor | __init__ method used to initialize objects |
| Encapsulation | Hiding internal data using access specifiers (_, __) |
| Abstraction | Hiding unnecessary implementation details |
| Inheritance | One class (child) inherits properties from another (parent) |
| Polymorphism | Ability to take many forms (method overriding/overloading) |
class Car:
def __init__(self, brand, color):
self.brand = brand
self.color = color
def drive(self):
print(f"{self.color} {self.brand} is driving.")
my_car = Car("Toyota", "Red")
my_car.drive() # Output: Red Toyota is driving.
Protecting internal data.
class BankAccount:
def __init__(self, balance):
self.__balance = balance # private variable
def get_balance(self):
return self.__balance
acc = BankAccount(5000)
print(acc.get_balance()) # ✅ 5000
# print(acc.__balance) # ❌ Error: private attribute
Child class inherits from parent.
class Animal:
def sound(self):
print("Some sound")
class Dog(Animal):
def sound(self):
print("Bark")
d = Dog()
d.sound() # Output: Bark
Same method name, different behavior.
class Bird:
def speak(self):
print("Tweet")
class Parrot(Bird):
def speak(self):
print("Squawk")
def make_sound(bird):
bird.speak()
make_sound(Parrot()) # Output: Squawk
Hide implementation, show interface (via abstract classes).
from abc import ABC, abstractmethod
class Shape(ABC):
@abstractmethod
def area(self):
pass
class Circle(Shape):
def __init__(self, radius):
self.radius = radius
def area(self):
return 3.14 * self.radius ** 2
c = Circle(5)
print(c.area()) # Output: 78.5
| Concept | Python Feature |
|---|---|
| Class/Object | class, __init__, object |
| Inheritance | class Child(Parent) |
| Polymorphism | Method Overriding |
| Encapsulation | _protected, __private vars |
| Abstraction | ABC, abstractmethod |


Python allows us to create, read, write, and delete files using built-in functions like open(), read(), write(), and close().
open()file = open("filename.txt", "mode")
Common modes:
| Mode | Description |
|---|---|
'r' |
Read (default), file must exist |
'w' |
Write, creates or overwrites |
'a' |
Append, creates if not exists |
'x' |
Exclusive create, fails if exists |
'b' |
Binary mode (rb, wb, etc.) |
't' |
Text mode (default, used with others) |
# Read entire content
with open("sample.txt", "r") as f:
content = f.read()
print(content)
# Read line by line
with open("sample.txt", "r") as f:
for line in f:
print(line.strip())
# Read specific number of characters
with open("sample.txt", "r") as f:
print(f.read(5)) # Reads 5 characters
# Overwrites the file if it exists
with open("output.txt", "w") as f:
f.write("Hello, world!\n")
f.write("Second line.\n")
with open("output.txt", "a") as f:
f.write("This line will be added.\n")
# Fails if the file already exists
with open("newfile.txt", "x") as f:
f.write("Created new file.")
import os
if os.path.exists("oldfile.txt"):
os.remove("oldfile.txt")
else:
print("File does not exist")
import os
print(os.path.exists("myfile.txt")) # True/False
print(os.path.isfile("myfile.txt")) # True if file
print(os.path.isdir("myfolder")) # True if folder
| Method | Description |
|---|---|
read() |
Reads entire file |
readline() |
Reads a single line |
readlines() |
Returns list of lines |
write() |
Writes to file |
writelines() |
Writes list of strings |
close() |
Closes the file |
Note: Use with open(...) to auto-close the file.

Exception: An error that occurs during the execution of a program, disrupting the normal flow.
To gracefully handle errors and keep the program from crashing.
try:
# Code that may raise an exception
x = 10 / 0
except ZeroDivisionError:
print("Cannot divide by zero.")
else:
print("No exceptions occurred.")
finally:
print("Always runs.")
| Block | Purpose |
|---|---|
try |
Code that may raise an exception |
except |
Block to catch and handle exceptions |
else |
Runs only if no exception occurs |
finally |
Runs no matter what (used for cleanup like closing files/db) |
try:
x = int("abc")
except (ValueError, TypeError) as e:
print(f"Error occurred: {e}")
Not recommended unless you're logging or debugging.
try:
something()
except Exception as e:
print("Error:", e)
You can raise exceptions manually using raise.
def divide(a, b):
if b == 0:
raise ValueError("Denominator cannot be zero.")
return a / b
| Exception | Description |
|---|---|
ZeroDivisionError |
Division by zero |
ValueError |
Wrong value (e.g., int('abc')) |
TypeError |
Operation on incompatible types |
IndexError |
Index out of range (e.g., list[10]) |
KeyError |
Accessing a missing dictionary key |
FileNotFoundError |
File not found during file operations |
AttributeError |
Invalid attribute access |
ImportError |
Error in importing module |
NameError |
Variable not defined |
StopIteration |
Raised by next() in loops when no item exists |
KeyboardInterrupt |
Interrupt by user using Ctrl+C |
MemoryError |
Out of memory |
AssertionError |
When assert statement fails |
try:
f = open("data.txt")
content = f.read()
except FileNotFoundError:
print("File not found.")
finally:
print("Operation complete.")
You can define your own error types by extending Exception.
class MyCustomError(Exception):
pass
raise MyCustomError("Something went wrong.")
try:
risky_code()
except SpecificError:
handle_it()
except AnotherError as e:
handle_with_info(e)
else:
no_errors()
finally:
always_runs()

class TooYoungError(Exception):
pass
def register(age):
if age < 18:
raise TooYoungError("Must be 18+ to register.")
try:
register(16)
except TooYoungError as e:
print(e)
Debugging is the process of finding and fixing errors (bugs) in code.
Assertions are used to check if a condition is True at a specific point in the program.
If it’s False, Python throws an AssertionError.
assert condition, "Optional error message"
x = 5
assert x > 0, "x must be positive"
If x = -1, this will raise:
AssertionError: x must be positive
Used to catch logic errors early during development.
Don’t use assertions for user input validation in production — they can be disabled with python -O.
A traceback is the stack trace showing where an error occurred.
Use the traceback module:
import traceback
try:
1 / 0
except ZeroDivisionError:
tb_str = traceback.format_exc()
print("Traceback as string:\n", tb_str)
A flexible system for recording:
More powerful and configurable than print().
import logging
logging.basicConfig(level=logging.INFO)
logging.info("This is an info message.")
logging.warning("This is a warning.")
logging.error("Something went wrong.")
| Level | Use Case |
|---|---|
| DEBUG | Detailed debugging info |
| INFO | General app flow messages |
| WARNING | Something unexpected happened |
| ERROR | A serious problem occurred |
| CRITICAL | Very serious error |
logging.basicConfig(
filename='app.log',
level=logging.DEBUG,
format='%(asctime)s - %(levelname)s - %(message)s'
)
logging.error("This goes to a file!")
try:
1 / 0
except ZeroDivisionError:
logging.exception("Exception occurred")
This prints both error message and full traceback!
import logging
logging.basicConfig(level=logging.INFO)
logging.info("This is an info message.")
logging.warning("This is a warning.")
logging.error("Something went wrong.")
WARNING:root:This is a warning. ERROR:root:Something went wrong.
logging.basicConfig(
filename='app.log',
level=logging.DEBUG,
format='%(asctime)s - %(levelname)s - %(message)s'
)
logging.error("This goes to a file!")
ERROR:root:This goes to a file!
A module is simply a Python file (.py) containing variables, functions, classes, etc., that you can import and reuse in other Python programs.
Purpose: Helps organize code, improve reusability, and maintainability.
| Type | Example |
|---|---|
| Built-in | math, os, sys, random |
| User-defined | Your own .py files |
| Third-party | Installed via pip like numpy, pandas, requests |
mathimport math
print(math.sqrt(25)) # 5.0
print(math.pi) # 3.141592653589793
File: mymodule.py
# mymodule.py
def greet(name):
return f"Hello, {name}!"
pi = 3.14
Now use it in another file:
import mymodule
print(mymodule.greet("Purushotham"))
print(mymodule.pi)
import Variants# import whole module
import math
math.sqrt(16)
# import specific function
from math import sqrt
sqrt(16)
# import with alias
import math as m
m.sqrt(16)
# import all (not recommended)
from math import *
__name__ == "__main__"# module_test.py
def run():
print("Running test...")
if __name__ == "__main__":
run()
This ensures the block only runs if file is executed directly, not when imported.
| Module | Use Case |
|---|---|
math |
Mathematical operations |
random |
Random number generation |
datetime |
Date and time manipulation |
os |
OS-level operations (files, dirs) |
sys |
Command line args, interpreter info |
json |
Work with JSON data |
re |
Regular expressions |
time |
Timing, sleep functions |
pip install)pip install requests
import requests
response = requests.get("https://api.github.com")
print(response.status_code)
import numpy as np) for readability.from module import *) in production.Project: Weather App
main.py: user interfaceweather.py: handles API requestsutils.py: helper functions (formatting, error handling)All parts communicate via module imports.