0% found this document useful (0 votes)
25 views157 pages

Python Preparation Guide

Uploaded by

felixhosanaa
Copyright
© © All Rights Reserved
We take content rights seriously. If you suspect this is your content, claim it here.
Available Formats
Download as PDF, TXT or read online on Scribd
0% found this document useful (0 votes)
25 views157 pages

Python Preparation Guide

Uploaded by

felixhosanaa
Copyright
© © All Rights Reserved
We take content rights seriously. If you suspect this is your content, claim it here.
Available Formats
Download as PDF, TXT or read online on Scribd

Preparation Guide

Table of Contents

1. Basic Operations
2. String Manipulation
3. Number Operations
4. Pattern Printing
5. File Handling
6. OOP Concepts
7. Advanced Concepts
8. Company-Specific Questions

Basic Operations

1. Converting Integer to Decimal

Method 1: Using decimal module

import decimal
integer = 10
result = decimal.Decimal(integer)
print(result) # Output: 10
print(type(result)) # Output: <class 'decimal.Decimal'>

Method 2: Using string formatting

integer = 10
result = float(integer)
print(result) # Output: 10.0
print(type(result)) # Output: <class 'float'>
2. Converting String to Decimal

Method 1: Using decimal module

import decimal
string = '12345'
result = decimal.Decimal(string)
print(result) # Output: 12345
print(type(result)) # Output: <class 'decimal.Decimal'>

Method 2: Direct conversion

string = '12345'
result = float(string)
print(result) # Output: 12345.0
print(type(result)) # Output: <class 'float'>

3. Reversing a String

Method 1: Extended slicing

string = " Programming"


result = string[::-1]
print(result) # Output: gnimmargorP nohtyP

Method 2: Using reversed()

string = " Programming"


result = ''.join(reversed(string))
print(result) # Output: gnimmargorP nohtyP

Method 3: Using loop


string = " Programming"
reversed_str = ""
for char in string:
reversed_str = char + reversed_str
print(reversed_str) # Output: gnimmargorP nohtyP

4. Counting Vowels

Method 1: Using list of vowels

vowels = ['a', 'e', 'i', 'o', 'u']


word = "programming"
count = 0
for character in word:
if character in vowels:
count += 1
print(count) # Output: 3

Method 2: Using set for faster lookup

vowels = {'a', 'e', 'i', 'o', 'u'}


word = "programming"
count = sum(1 for char in word if char in vowels)
print(count) # Output: 3

Method 3: Using filter()

vowels = {'a', 'e', 'i', 'o', 'u'}


word = "programming"
count = len(list(filter(lambda x: x in vowels, word)))
print(count) # Output: 3

5. Counting Consonants

Method 1: Using list of vowels


vowels = ['a', 'e', 'i', 'o', 'u']
word = "programming"
count = 0
for character in word:
if character not in vowels:
count += 1
print(count) # Output: 8

Method 2: Using set operations

vowels = {'a', 'e', 'i', 'o', 'u'}


word = "programming"
count = sum(1 for char in word if char not in vowels)
print(count) # Output: 8

Method 3: Using regex

import re
word = "programming"
count = len(re.findall('[^aeiou]', word))
print(count) # Output: 8

6. Counting Character Occurrences

Method 1: Using loop

word = "programming"
character = 'g'
count = 0
for i in word:
if i == character:
count += 1
print(count) # Output: 2

Method 2: Using count() method


word = "programming"
character = 'g'
count = word.count(character)
print(count) # Output: 2

Method 3: Using collections.Counter

from collections import Counter


word = "programming"
count = Counter(word)[character]
print(count) # Output: 2

7. Fibonacci Series

Method 1: Using list

fib = [0, 1]
n=5
for i in range(n):
fib.append(fib[-1] + fib[-2])
print(','.join(str(e) for e in fib)) # Output: 0,1,1,2,3,5,8

Method 2: Using generator

def fibonacci(n):
a, b = 0, 1
for _ in range(n):
yield a
a, b = b, a + b

print(list(fibonacci(8))) # Output: [0, 1, 1, 2, 3, 5, 8, 13]

Method 3: Using recursion with memoization


def fibonacci(n, memo={}):
if n in memo:
return memo[n]
if n <= 1:
return n
memo[n] = fibonacci(n-1, memo) + fibonacci(n-2, memo)
return memo[n]

print([fibonacci(i) for i in range(8)]) # Output: [0, 1, 1, 2, 3, 5, 8, 13]

8. Finding Maximum Number in List

Method 1: Using loop

numberList = [12, 3, 55, 23, 6, 78, 33, 4]


max_num = numberList[0]
for num in numberList:
if max_num < num:
max_num = num
print(max_num) # Output: 78

Method 2: Using max() function

numberList = [12, 3, 55, 23, 6, 78, 33, 4]


max_num = max(numberList)
print(max_num) # Output: 78

Method 3: Using reduce()

from functools import reduce


numberList = [12, 3, 55, 23, 6, 78, 33, 4]
max_num = reduce(lambda a, b: a if a > b else b, numberList)
print(max_num) # Output: 78
9. Finding Minimum Number in List

Method 1: Using loop

numberList = [12, 3, 55, 23, 6, 78, 33, 4]


min_num = numberList[0]
for num in numberList:
if min_num > num:
min_num = num
print(min_num) # Output: 3

Method 2: Using min() function

numberList = [12, 3, 55, 23, 6, 78, 33, 4]


min_num = min(numberList)
print(min_num) # Output: 3

Method 3: Using reduce()

from functools import reduce


numberList = [12, 3, 55, 23, 6, 78, 33, 4]
min_num = reduce(lambda a, b: a if a < b else b, numberList)
print(min_num) # Output: 3

10. Finding Middle Element in List

Method 1: Using length calculation

numList = [12, 3, 55, 23, 6, 78, 33, 5]


midElement = numList[len(numList)//2]
print(midElement) # Output: 23

Method 2: Using statistics module


import statistics
numList = [12, 3, 55, 23, 6, 78, 33, 5]
midElement = statistics.median(numList)
print(midElement) # Output: 17.5 (average of middle two)

Method 3: For odd-length lists

numList = [12, 3, 55, 23, 6, 78, 33]


midElement = numList[len(numList)//2]
print(midElement) # Output: 23

11. Converting List to String

Method 1: Using join()

lst = ["P", "Y", "T", "H", "O", "N"]


string = "".join(lst)
print(string) # Output:
print(type(string)) # Output: <class 'str'>

Method 2: Using loop

lst = ["P", "Y", "T", "H", "O", "N"]


string = ""
for char in lst:
string += char
print(string) # Output:
print(type(string)) # Output: <class 'str'>

Method 3: Using map() and join()

lst = ["P", "Y", "T", "H", "O", "N"]


string = "".join(map(str, lst))
print(string) # Output:
print(type(string)) # Output: <class 'str'>
12. Adding Two List Elements

Method 1: Using loop

lst1 = [1, 2, 3]
lst2 = [4, 5, 6]
res_lst = []
for i in range(len(lst1)):
res_lst.append(lst1[i] + lst2[i])
print(res_lst) # Output: [5, 7, 9]

Method 2: Using list comprehension

lst1 = [1, 2, 3]
lst2 = [4, 5, 6]
res_lst = [lst1[i] + lst2[i] for i in range(len(lst1))]
print(res_lst) # Output: [5, 7, 9]

Method 3: Using zip()

lst1 = [1, 2, 3]
lst2 = [4, 5, 6]
res_lst = [a + b for a, b in zip(lst1, lst2)]
print(res_lst) # Output: [5, 7, 9]

13. Checking for Anagrams

Method 1: Using sorted()

str1 = "Listen"
str2 = "Silent"

str1 = str1.replace(" ", "").upper()


str2 = str2.replace(" ", "").upper()
if sorted(str1) == sorted(str2):
print("True")
else:
print("False") # Output: True

Method 2: Using Counter

from collections import Counter

str1 = "Listen"
str2 = "Silent"

str1 = str1.replace(" ", "").upper()


str2 = str2.replace(" ", "").upper()

if Counter(str1) == Counter(str2):
print("True")
else:
print("False") # Output: True

Method 3: Using frequency dictionary

def is_anagram(str1, str2):


str1 = str1.replace(" ", "").lower()
str2 = str2.replace(" ", "").lower()

if len(str1) != len(str2):
return False

freq = {}
for char in str1:
freq[char] = freq.get(char, 0) + 1

for char in str2:


if char not in freq or freq[char] == 0:
return False
freq[char] -= 1

return True

print(is_anagram("Listen", "Silent")) # Output: True

14. Checking for Palindrome

Method 1: Using extended slicing

str1 = "Kayak".lower()
if str1 == str1[::-1]:
print("True")
else:
print("False") # Output: True

Method 2: Using loop

def is_palindrome(s):
s = s.lower().replace(" ", "")
left, right = 0, len(s) - 1
while left < right:
if s[left] != s[right]:
return False
left += 1
right -= 1
return True

print(is_palindrome("Kayak")) # Output: True

Method 3: Using reversed()

def is_palindrome(s):
s = s.lower().replace(" ", "")
return s == ''.join(reversed(s))
print(is_palindrome("Kayak")) # Output: True

15. Counting Whitespaces

Method 1: Using count()

string = "P r ogram in g"


count = string.count(" ")
print(count) # Output: 4

Method 2: Using loop

string = "P r ogram in g"


count = 0
for char in string:
if char == " ":
count += 1
print(count) # Output: 4

Method 3: Using regex

import re
string = "P r ogram in g"
count = len(re.findall(r'\s', string))
print(count) # Output: 4

16. Counting Digits, Letters, and Spaces

Method 1: Using regex

import re
name = ' is 1'
digitCount = re.sub("[^0-9]", "", name)
letterCount = re.sub("[^a-zA-Z]", "", name)
spaceCount = re.findall(r"\s", name)

print(len(digitCount)) # Output: 1
print(len(letterCount)) # Output: 7
print(len(spaceCount)) # Output: 2

Method 2: Using isdigit(), isalpha(), isspace()

name = ' is 1'


digits = 0
letters = 0
spaces = 0

for char in name:


if char.isdigit():
digits += 1
elif char.isalpha():
letters += 1
elif char.isspace():
spaces += 1

print(digits) # Output: 1
print(letters) # Output: 7
print(spaces) # Output: 2

Method 3: Using list comprehensions

name = ' is 1'


digits = sum(1 for c in name if c.isdigit())
letters = sum(1 for c in name if c.isalpha())
spaces = sum(1 for c in name if c.isspace())

print(digits) # Output: 1
print(letters) # Output: 7
print(spaces) # Output: 2
17. Counting Special Characters

Method 1: Using predefined special characters

def count_sp_char(string):
sp_char = "|@#$%^&*()<>?/\\[]{};:~"
count = 0
for i in string:
if i in sp_char:
count += 1
return count

text = 'Hello! How are you? #specialchars! 123'


result = count_sp_char(text)
print(result) # Output: 2

Method 2: Using regex

import re

def count_sp_char(string):
return len(re.findall(r'[^\w\s]', string))

text = 'Hello! How are you? #specialchars! 123'


result = count_sp_char(text)
print(result) # Output: 3

Method 3: Using string.punctuation

import string

def count_sp_char(text):
return sum(1 for char in text if char in string.punctuation)

text = 'Hello! How are you? #specialchars! 123'


result = count_sp_char(text)
print(result) # Output: 3

18. Removing All Whitespace

Method 1: Using regex

import re
string = "C O D E"
spaces = re.compile(r'\s+')
result = re.sub(spaces, "", string)
print(result) # Output: CODE

Method 2: Using join() with comprehension

string = "C O D E"


string2 = "".join(char for char in string if char != " ")
print(string2) # Output: CODE

Method 3: Using replace()

string = 'C O D E'


string2 = string.replace(" ", "")
print(string2) # Output: CODE

Method 4: Using split() and join()

string = 'C O D E'


string2 = ''.join(string.split())
print(string2) # Output: CODE
Basic Pyramid Patterns

Pattern 1: Right-Angled Triangle

text

*
**
***
****
*****

Method 1: Using nested loops

n=5
for i in range(1, n+1):
for j in range(i):
print("*", end=" ")
print()

Method 2: Using single loop

n=5
for i in range(1, n+1):
print("* " * i)

Method 3: Using list comprehension

n=5
pattern = ["* " * i for i in range(1, n+1)]
print("\n".join(pattern))
Pattern 2: Inverted Right-Angled Triangle

text

*****
****
***
**
*

Method 1: Using nested loops

n=5
for i in range(n, 0, -1):
for j in range(i):
print("*", end=" ")
print()

Method 2: Using single loop

n=5
for i in range(n, 0, -1):
print("* " * i)

Method 3: Using reversed range

n=5
for i in reversed(range(1, n+1)):
print("* " * i)

Pattern 3: Pyramid

text

*
**
***
****
*****

Method 1: Using nested loops

n=5
for i in range(1, n+1):
# Print spaces
for j in range(n - i):
print(" ", end="")
# Print stars
for k in range(i):
print("*", end=" ")
print()

Method 2: Using string methods

n=5
for i in range(1, n+1):
print(" " * (n - i) + "* " * i)

Method 3: Using center alignment

n=5
for i in range(1, n+1):
print(("* " * i).center(2 * n))

Pattern 4: Inverted Pyramid

text

*****
****
***
**
*
Method 1: Using nested loops

n=5
for i in range(n, 0, -1):
# Print spaces
for j in range(n - i):
print(" ", end="")
# Print stars
for k in range(i):
print("*", end=" ")
print()

Method 2: Using string methods

n=5
for i in range(n, 0, -1):
print(" " * (n - i) + "* " * i)

Method 3: Using reversed range

n=5
for i in reversed(range(1, n+1)):
print(" " * (n - i) + "* " * i)

Number Patterns

Pattern 5: Number Pyramid

text

1
12
123
1234
12345

Method 1: Using nested loops

n=5
for i in range(1, n+1):
for j in range(1, i+1):
print(j, end=" ")
print()

Method 2: Using string joining

n=5
for i in range(1, n+1):
print(" ".join(str(j) for j in range(1, i+1)))

Method 3: Using list comprehension

n=5
pattern = [" ".join(str(j) for j in range(1, i+1)) for i in range(1, n+1)]
print("\n".join(pattern))

Pattern 6: Same Number Pyramid

text

1
22
333
4444
55555

Method 1: Using nested loops


n=5
for i in range(1, n+1):
for j in range(i):
print(i, end=" ")
print()

Method 2: Using string operations

n=5
for i in range(1, n+1):
print((str(i) + " ") * i)

Method 3: Using list comprehension

n=5
pattern = [(str(i) + " ") * i for i in range(1, n+1)]
print("\n".join(pattern))

Pattern 7: Pascal's Triangle

text

1
11
121
1331
14641

Method 1: Using binomial coefficients

n=5
for i in range(n):
# Print leading spaces
print(" " * (n - i), end="")

# Calculate and print values


for j in range(i + 1):
# Calculate binomial coefficient
from math import comb
print(comb(i, j), end=" ")
print()

Method 2: Using dynamic programming

n=5
triangle = []
for i in range(n):
row = [1] * (i + 1)
for j in range(1, i):
row[j] = triangle[i-1][j-1] + triangle[i-1][j]
triangle.append(row)
print(" " * (n - i) + " ".join(map(str, row)))

Method 3: Using generator function

def pascal_triangle(n):
row = [1]
for i in range(n):
print(" " * (n - i) + " ".join(map(str, row)))
row = [1] + [row[j] + row[j+1] for j in range(len(row)-1)] + [1]

pascal_triangle(5)

Pattern 8: Floyd's Triangle

text

1
23
456
7 8 9 10
11 12 13 14 15

Method 1: Using counter variable


n=5
num = 1
for i in range(1, n+1):
for j in range(i):
print(num, end=" ")
num += 1
print()

Method 2: Using mathematical formula

n=5
for i in range(1, n+1):
start = (i * (i - 1)) // 2 + 1
end = (i * (i + 1)) // 2
print(" ".join(str(j) for j in range(start, end + 1)))

Method 3: Using generator

def floyd_generator(n):
num = 1
for i in range(1, n+1):
yield " ".join(str(num + j) for j in range(i))
num += i

for row in floyd_generator(5):


print(row)

Alphabet Patterns

Pattern 9: Alphabet Pyramid

text

A
BB
CCC
DDDD
EEEEE

Method 1: Using nested loops

n=5
for i in range(n):
for j in range(i+1):
print(chr(65 + i), end=" ")
print()

Method 2: Using string operations

n=5
for i in range(n):
print((chr(65 + i) + " ") * (i + 1))

Method 3: Using list comprehension

n=5
pattern = [(chr(65 + i) + " ") * (i + 1) for i in range(n)]
print("\n".join(pattern))

Pattern 10: Alphabet Sequence

text

A
AB
ABC
ABCD
ABCDE

Method 1: Using nested loops


n=5
for i in range(n):
for j in range(i+1):
print(chr(65 + j), end=" ")
print()

Method 2: Using string joining

n=5
for i in range(1, n+1):
print(" ".join(chr(65 + j) for j in range(i)))

Method 3: Using list comprehension

n=5
pattern = [" ".join(chr(65 + j) for j in range(i)) for i in range(1, n+1)]
print("\n".join(pattern))

Pattern 11: Alphabet Diamond

text

A
BC
DEF
GHIJ
KLMNO

Method 1: Using nested loops

n=5
char = 65 # ASCII for 'A'
for i in range(n):
# Print spaces
print(" " * (n - i - 1), end="")
# Print characters
for j in range(i + 1):
print(chr(char), end=" ")
char += 1
print()

Method 2: Using string formatting

n=5
char = 65
for i in range(n):
chars = " ".join(chr(char + j) for j in range(i + 1))
print(f"{chars:^{2*n}}")
char += i + 1

Method 3: Using generator

def alphabet_diamond(n):
char = 65
for i in range(n):
row = " ".join(chr(char + j) for j in range(i + 1))
yield f"{row:^{2*n}}"
char += i + 1

for row in alphabet_diamond(5):


print(row)

Pattern 12: Alphabet Triangle

text

A
BC
DEF
GHIJ
KLMNO

Method 1: Using nested loops


n=5
char = 65
for i in range(n):
for j in range(i + 1):
print(chr(char), end=" ")
char += 1
print()

Method 2: Using mathematical calculation

n=5
for i in range(n):
start_char = 65 + (i * (i + 1)) // 2
print(" ".join(chr(start_char + j) for j in range(i + 1)))

Method 3: Using generator function

def alphabet_triangle(n):
char = 65
for i in range(n):
row = " ".join(chr(char + j) for j in range(i + 1))
yield row
char += i + 1

for row in alphabet_triangle(5):


print(row)

Diamond Patterns

Pattern 13: Star Diamond

text

*
**
***
****
*****
****
***
**
*

Method 1: Using nested loops

n=5
# Upper part
for i in range(n):
print(" " * (n - i - 1) + "* " * (i + 1))

# Lower part
for i in range(n - 2, -1, -1):
print(" " * (n - i - 1) + "* " * (i + 1))

Method 2: Using absolute value

n=5
for i in range(-n + 1, n):
spaces = abs(i)
stars = n - spaces
print(" " * spaces + "* " * stars)

Method 3: Using string center method

n=5
for i in range(-n + 1, n):
row = "* " * (n - abs(i))
print(row.center(2 * n))

Pattern 14: Number Diamond

text
1
12
123
1234
12345
1234
123
12
1

Method 1: Using nested loops

n=5
# Upper part
for i in range(1, n + 1):
print(" " * (n - i), end="")
for j in range(1, i + 1):
print(j, end=" ")
print()

# Lower part
for i in range(n - 1, 0, -1):
print(" " * (n - i), end="")
for j in range(1, i + 1):
print(j, end=" ")
print()

Method 2: Using absolute value

n=5
for i in range(-n + 1, n):
row = " ".join(str(j) for j in range(1, n - abs(i) + 1))
print(f"{row:^{2*n}}")

Method 3: Using list comprehension

n=5
pattern = []
for i in range(-n + 1, n):
row = " ".join(str(j) for j in range(1, n - abs(i) + 1))
pattern.append(f"{row:^{2*n}}")

print("\n".join(pattern))

Pattern 15: Hollow Diamond

text

*
**
* *
* *
* *
* *
* *
**
*

Method 1: Using nested loops

n=5
# Upper part
for i in range(n):
if i == 0:
print(" " * (n - 1) + "*")
else:
print(" " * (n - i - 1) + "*" + " " * (2 * i - 1) + "*")

# Lower part
for i in range(n - 2, -1, -1):
if i == 0:
print(" " * (n - 1) + "*")
else:
print(" " * (n - i - 1) + "*" + " " * (2 * i - 1) + "*")

Method 2: Using mathematical approach


n=5
for i in range(-n + 1, n):
spaces = abs(i)
if abs(i) == n - 1:
print(" " * spaces + "*")
else:
print(" " * spaces + "*" + " " * (2 * (n - spaces - 1) - 1) + "*")

Method 3: Using conditional formatting

n=5
for i in range(-n + 1, n):
row = ""
for j in range(-n + 1, n):
if abs(i) + abs(j) == n - 1:
row += "*"
else:
row += " "
print(row)

Triangle Patterns

Pattern 16: Right Triangle

text

*
**
***
****
*****

Method 1: Basic nested loops


n=5
for i in range(1, n + 1):
for j in range(i):
print("*", end=" ")
print()

Method 2: Using string multiplication

n=5
for i in range(1, n + 1):
print("* " * i)

Method 3: Using list comprehension

n=5
print("\n".join(["* " * i for i in range(1, n + 1)]))

Pattern 17: Inverted Right Triangle

text

*****
****
***
**
*

Method 1: Basic nested loops

n=5
for i in range(n, 0, -1):
for j in range(i):
print("*", end=" ")
print()

Method 2: Using reversed range


n=5
for i in reversed(range(1, n + 1)):
print("* " * i)

Method 3: Using string operations

n=5
print("\n".join(["* " * i for i in range(n, 0, -1)]))

Pattern 18: Mirrored Right Triangle

text

*
**
***
****
*****

Method 1: Using nested loops

n=5
for i in range(1, n + 1):
print(" " * (n - i) + "*" * i)

Method 2: Using string formatting

n=5
for i in range(1, n + 1):
print(f"{'*' * i:>{n}}")

Method 3: Using rjust method

n=5
for i in range(1, n + 1):
print(("*" * i).rjust(n))
Pattern 19: Hollow Right Triangle

text

*
**
* *
* *
*****

Method 1: Using conditional statements

n=5
for i in range(1, n + 1):
if i == 1 or i == n:
print("* " * i)
else:
print("*" + " " * (2 * i - 3) + "*")

Method 2: Using string operations

n=5
for i in range(n):
if i == 0:
print("*")
elif i == n - 1:
print("* " * n)
else:
print("*" + " " * (2 * i - 1) + "*")

Method 3: Using list comprehension with conditions

n=5
pattern = []
for i in range(n):
if i == 0:
pattern.append("*")
elif i == n - 1:
pattern.append("* " * n)
else:
pattern.append("*" + " " * (2 * i - 1) + "*")

print("\n".join(pattern))

Special Character Patterns

Pattern 20: Butterfly Pattern

text

* *
** **
*** ***
********
********
*** ***
** **
* *

Method 1: Using nested loops

n=4
# Upper wing
for i in range(1, n + 1):
print("* " * i + " " * (n - i) * 2 + "* " * i)

# Lower wing
for i in range(n, 0, -1):
print("* " * i + " " * (n - i) * 2 + "* " * i)

Method 2: Using absolute value


n=4
size = 2 * n
for i in range(-size + 1, size):
stars = size - abs(i)
spaces = abs(i) * 2
print("* " * stars + " " * spaces + "* " * stars)

Method 3: Using mathematical approach

n=4
for i in range(1, 2 * n):
if i <= n:
stars = i
spaces = 2 * (n - i)
else:
stars = 2 * n - i
spaces = 2 * (i - n)

print("* " * stars + " " * spaces + "* " * stars)

Pattern 21: Heart Pattern

text

*** ***
***** *****
***********
*********
*******
*****
***
*

Method 1: Manual construction

heart = [
" *** *** ",
" ***** ***** ",
"***********",
" ********* ",
" ******* ",
" ***** ",
" *** ",
" * "
]

for line in heart:


print(line)

Method 2: Using mathematical formula

n=8
for i in range(n):
if i < 3:
# Top part of heart
spaces = abs(i - 1) * 2
stars = 5 - 2 * abs(i - 1)
line = " " * spaces + "*" * stars + " " * (10 - 2 * spaces - 2 * stars) + "*" * stars
print(line)
else:
# Bottom part of heart
spaces = i - 1
stars = 2 * (n - i) - 1
print(" " * spaces + "*" * stars)

Method 3: Using parametric equations

import math

def heart_shape(size):
for y in range(size, -size, -1):
line = ""
for x in range(-size, size):
# Heart equation: (x^2 + y^2 - 1)^3 - x^2*y^3 <= 0
if ((x * 0.05) ** 2 + (y * 0.1) ** 2 - 1) ** 3 - (x * 0.05) ** 2 * (y * 0.1) ** 3 <= 0:
line += "*"
else:
line += " "
print(line)

heart_shape(15)

Pattern 22: Christmas Tree

text

*
***
*****
*******
*********
|||
|||

Method 1: Using nested loops

n=5
# Tree part
for i in range(1, n + 1):
print(" " * (n - i) + "*" * (2 * i - 1))

# Trunk part
for i in range(2):
print(" " * (n - 2) + "|||")

Method 2: Using string methods

n=5
# Tree part
for i in range(1, n + 1):
print(("*" * (2 * i - 1)).center(2 * n - 1))

# Trunk part
for i in range(2):
print("|||".center(2 * n - 1))
Method 3: Using generator function

def christmas_tree(height):
# Generate tree layers
for i in range(1, height + 1):
yield ("*" * (2 * i - 1)).center(2 * height - 1)

# Generate trunk
for i in range(2):
yield "|||".center(2 * height - 1)

for line in christmas_tree(5):


print(line)

Matrix Patterns

Pattern 23: Square Pattern

text

*****
*****
*****
*****
*****

Method 1: Using nested loops

n=5
for i in range(n):
for j in range(n):
print("*", end=" ")
print()
Method 2: Using string multiplication

n=5
for i in range(n):
print("* " * n)

Method 3: Using list comprehension

n=5
pattern = ["* " * n for i in range(n)]
print("\n".join(pattern))

Pattern 24: Hollow Square

text

*****
* *
* *
* *
*****

Method 1: Using conditional statements

n=5
for i in range(n):
for j in range(n):
if i == 0 or i == n - 1 or j == 0 or j == n - 1:
print("*", end=" ")
else:
print(" ", end=" ")
print()

Method 2: Using string operations


n=5
for i in range(n):
if i == 0 or i == n - 1:
print("* " * n)
else:
print("*" + " " * (2 * n - 3) + "*")

Method 3: Using list comprehension with conditions

n=5
pattern = []
for i in range(n):
if i == 0 or i == n - 1:
pattern.append("* " * n)
else:
pattern.append("*" + " " * (2 * n - 3) + "*")

print("\n".join(pattern))

Pattern 25: Checkerboard Pattern

text

*****
****
*****
****
*****

Method 1: Using nested loops with condition

n=5
for i in range(n):
if i % 2 == 0:
print("* " * n)
else:
print(" *" * n)
Method 2: Using mathematical approach

n=5
for i in range(n):
row = ""
for j in range(n):
if (i + j) % 2 == 0:
row += "* "
else:
row += " "
print(row)

Method 3: Using list comprehension

n=5
pattern = []
for i in range(n):
if i % 2 == 0:
pattern.append("* " * n)
else:
pattern.append(" *" * n)

print("\n".join(pattern))

Pattern 26: Spiral Pattern

text

12345
16 17 18 19 6
15 24 25 20 7
14 23 22 21 8
13 12 11 10 9

Method 1: Using iterative approach


def spiral_matrix(n):
matrix = [[0] * n for _ in range(n)]
num = 1
top, bottom, left, right = 0, n-1, 0, n-1

while top <= bottom and left <= right:


# Top row
for i in range(left, right + 1):
matrix[top][i] = num
num += 1
top += 1

# Right column
for i in range(top, bottom + 1):
matrix[i][right] = num
num += 1
right -= 1

# Bottom row
for i in range(right, left - 1, -1):
matrix[bottom][i] = num
num += 1
bottom -= 1

# Left column
for i in range(bottom, top - 1, -1):
matrix[i][left] = num
num += 1
left += 1

return matrix

n=5
spiral = spiral_matrix(n)
for row in spiral:
print(" ".join(f"{num:2}" for num in row))

Method 2: Using recursive approach


def spiral_matrix_recursive(n, start=1, offset=0):
if n <= 0:
return []
if n == 1:
return [[start]]

matrix = [[0] * n for _ in range(n)]

# Fill outer layer


# Top row
for j in range(n):
matrix[0][j] = start + j

# Right column
for i in range(1, n):
matrix[i][n-1] = start + n - 1 + i

# Bottom row
for j in range(n-2, -1, -1):
matrix[n-1][j] = start + 2*n - 2 + (n-1-j)

# Left column
for i in range(n-2, 0, -1):
matrix[i][0] = start + 3*n - 3 + (n-1-i)

# Fill inner matrix recursively


inner = spiral_matrix_recursive(n-2, start + 4*n - 4, offset + 1)

# Copy inner matrix


for i in range(1, n-1):
for j in range(1, n-1):
matrix[i][j] = inner[i-1][j-1]

return matrix

n=5
spiral = spiral_matrix_recursive(n)
for row in spiral:
print(" ".join(f"{num:2}" for num in row))
Advanced Patterns

Pattern 27: Zigzag Pattern

text

* * *
* * * *
* *

Method 1: Using mathematical approach

rows = 3
cols = 9

for i in range(rows):
for j in range(cols):
if (i + j) % 4 == 0 or (j - i) % 4 == 0 and i != 0:
print("*", end="")
else:
print(" ", end="")
print()

Method 2: Using pattern recognition

def zigzag_pattern(rows, cols):


pattern = []
for i in range(rows):
row = []
for j in range(cols):
if (i == 0 and j % 4 == 0) or (i == 1 and j % 2 == 1) or (i == 2 and j % 4 == 2):
row.append("*")
else:
row.append(" ")
pattern.append("".join(row))
return pattern

for line in zigzag_pattern(3, 9):


print(line)

Method 3: Using step-based approach

rows = 3
cols = 9

for i in range(rows):
j=i
line = [" "] * cols
while j < cols:
line[j] = "*"
if i == 0 or i == 2:
j += 4
else:
j += 2
print("".join(line))

Pattern 28: Wave Pattern

text

* * *
* * *
**
**
* * *
* * *

Method 1: Using mathematical approach

n=6
for i in range(n):
for j in range(n):
if (i == j or i + j == n - 1) and (i <= n//2 or j <= n//2):
print("*", end="")
else:
print(" ", end="")
print()

Method 2: Using pattern recognition

n=6
for i in range(n):
line = []
for j in range(n):
if (i == j or i + j == n - 1) and (i < n//2 or j < n//2 or i == j == n//2):
line.append("*")
else:
line.append(" ")
print("".join(line))

Method 3: Using symmetric approach

n=6
half = n // 2
for i in range(n):
if i < half:
line = [" "] * n
line[i] = "*"
line[n - i - 1] = "*"
print("".join(line))
else:
line = [" "] * n
line[i] = "*"
line[n - i - 1] = "*"
print("".join(line))

Pattern 29: Cross Pattern

text
* *
* *
*
* *
* *

Method 1: Using diagonal approach

n=5
for i in range(n):
for j in range(n):
if i == j or i + j == n - 1:
print("*", end="")
else:
print(" ", end="")
print()

Method 2: Using string operations

n=5
for i in range(n):
line = [" "] * n
line[i] = "*"
line[n - i - 1] = "*"
print("".join(line))

Method 3: Using list comprehension

n=5
pattern = []
for i in range(n):
row = [" "] * n
row[i] = "*"
row[n - i - 1] = "*"
pattern.append("".join(row))

print("\n".join(pattern))
Pattern 30: Arrow Pattern

text

*
**
***
****
***
**
*

Method 1: Using absolute value

n=4
for i in range(-n + 1, n):
print("* " * (n - abs(i)))

Method 2: Using two loops

n=4
# Upper part
for i in range(1, n + 1):
print("* " * i)

# Lower part
for i in range(n - 1, 0, -1):
print("* " * i)

Method 3: Using mathematical approach

n=4
for i in range(1, 2 * n):
stars = i if i <= n else 2 * n - i
print("* " * stars)
19. Building a Pyramid

Method 1: Simple pyramid

def pyramid(n):
for i in range(1, n+1):
print(' '*(n-i) + '*'*(2*i-1))

pyramid(5)
# Output:
# *
# ***
# *****
# *******
# *********

Method 2: Number pyramid

def number_pyramid(n):
for i in range(1, n+1):
print(' '*(n-i), end='')
for j in range(1, i+1):
print(j, end=' ')
print()

number_pyramid(5)
# Output:
# 1
# 12
# 123
# 1234
#12345

Method 3: Alphabet pyramid

def alphabet_pyramid(n):
for i in range(1, n+1):
print(' '*(n-i), end='')
for j in range(i):
print(chr(65 + j), end=' ')
print()

alphabet_pyramid(5)
# Output:
# A
# AB
# ABC
# ABCD
#ABCDE

20. Shuffling a List

Method 1: Using random.shuffle()

from random import shuffle


lst = ['', 'is', 'Easy']
shuffle(lst)
print(lst) # Output: Random order each time

Method 2: Using random.sample()

from random import sample


lst = ['', 'is', 'Easy']
shuffled = sample(lst, len(lst))
print(shuffled) # Output: Random order each time

Method 3: Manual shuffle using Fisher-Yates algorithm

import random

def manual_shuffle(lst):
for i in range(len(lst)-1, 0, -1):
j = random.randint(0, i)
lst[i], lst[j] = lst[j], lst[i]
return lst

lst = ['', 'is', 'Easy']


print(manual_shuffle(lst)) # Output: Random order each time

21. Prime Number Generator

Method 1: Using generator

def isprime(num):
if num < 2:
return False
for i in range(2, int(num**0.5)+1):
if num % i == 0:
return False
return True

def prime_generator(n):
num = 2
count = 0
while count < n:
if isprime(num):
yield num
count += 1
num += 1

x = int(input("Enter no. of prime numbers required: "))


it = prime_generator(x)
for e in it:
print(e, end=" ")
# Input: 10
# Output: 2 3 5 7 11 13 17 19 23 29

Method 2: Using Sieve of Eratosthenes


def prime_generator_sieve(n):
sieve = [True] * (n*10) # Create a large enough sieve
sieve[0] = sieve[1] = False
primes = []

for i in range(2, len(sieve)):


if sieve[i]:
primes.append(i)
if len(primes) == n:
break
for j in range(i*i, len(sieve), i):
sieve[j] = False
return primes

x = int(input("Enter no. of prime numbers required: "))


primes = prime_generator_sieve(x)
print(' '.join(map(str, primes)))
# Input: 10
# Output: 2 3 5 7 11 13 17 19 23 29

22. Checking if a Number is Prime

Method 1: Basic check

def prime_no(n):
if n < 2:
return False
for i in range(2, n):
if n % i == 0:
return False
return True

num = int(input("Enter a number: "))


print(prime_no(num))
# Input: 37
# Output: True

Method 2: Optimized check (up to sqrt(n))


import math

def prime_no(n):
if n < 2:
return False
if n == 2:
return True
if n % 2 == 0:
return False
for i in range(3, int(math.sqrt(n)) + 1, 2):
if n % i == 0:
return False
return True

num = int(input("Enter a number: "))


print(prime_no(num))
# Input: 37
# Output: True

Method 3: Using Miller-Rabin test (probabilistic)

import random

def is_prime(n, k=5):


if n < 2:
return False
for p in [2, 3, 5, 7, 11, 13, 17, 19, 23, 29]:
if n % p == 0:
return n == p
d=n-1
s=0
while d % 2 == 0:
d //= 2
s += 1
for _ in range(k):
a = random.randint(2, n-1)
x = pow(a, d, n)
if x == 1 or x == n-1:
continue
for _ in range(s-1):
x = pow(x, 2, n)
if x == n-1:
break
else:
return False
return True

num = int(input("Enter a number: "))


print(is_prime(num))
# Input: 37
# Output: True

23. Variable Length Arguments

*Method 1: Using args for positional arguments

def average(*args):
avg = sum(args)/len(args)
return avg

result1 = average(32, 5, 65, 22, 87, 34, 2, 57)


result2 = average(5, 10, 15, 20, 25, 30, 35, 40, 45, 50)

print("Average is:", result1) # Output: Average is: 38.0


print("Average is:", result2) # Output: Average is: 27.5

**Method 2: Using kwargs for keyword arguments

def print_details(**kwargs):
for key, value in kwargs.items():
print(f"{key}: {value}")

print_details(name="John", age=30, city="New York")


# Output:
# name: John
# age: 30
# city: New York

**Method 3: Using both *args and kwargs

def func(*args, **kwargs):


print("Positional arguments:", args)
print("Keyword arguments:", kwargs)

func(1, 2, 3, name="John", age=30)


# Output:
# Positional arguments: (1, 2, 3)
# Keyword arguments: {'name': 'John', 'age': 30}

24. Lambda Functions

Method 1: Simple lambda

add = lambda x, y: x + y
print(add(5, 3)) # Output: 8

Method 2: Lambda with map()

numbers = [1, 2, 3, 4, 5]
squared = list(map(lambda x: x**2, numbers))
print(squared) # Output: [1, 4, 9, 16, 25]

Method 3: Lambda with filter()

numbers = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]


even = list(filter(lambda x: x % 2 == 0, numbers))
print(even) # Output: [2, 4, 6, 8, 10]
25. Factorial Calculation

Method 1: Using recursion

def factorial(n):
if n == 0:
return 1
else:
return n * factorial(n-1)

num = int(input("Enter a number: "))


print(f"The factorial of {num} is", factorial(num))
# Input: 5
# Output: The factorial of 5 is 120

Method 2: Using iterative approach

num = int(input("Enter a number: "))


factorial = 1
if num < 0:
print("Sorry, factorial does not exist for negative numbers")
elif num == 0:
print("The factorial of 0 is 1")
else:
for i in range(1, num + 1):
factorial = factorial * i
print(f"The factorial of {num} is", factorial)
# Input: 5
# Output: The factorial of 5 is 120

Method 3: Using math.factorial()

import math
num = int(input("Enter a number: "))
print(f"The factorial of {num} is", math.factorial(num))
# Input: 5
# Output: The factorial of 5 is 120
Method 4: Using lambda recursion

factorial = lambda n: 1 if n == 0 else n * factorial(n-1)


num = int(input("Enter a number: "))
print(f"The factorial of {num} is", factorial(num))
# Input: 5
# Output: The factorial of 5 is 120

26. List Comprehensions

Method 1: Basic list comprehension

# List of even numbers


even_list = [2*e for e in range(1, 10)]
print(even_list) # Output: [2, 4, 6, 8, 10, 12, 14, 16, 18]

Method 2: List comprehension with condition

# Create a list of even numbers from a given list


lst = [23, 56, 65, 22, 62, 32, 65, 76, 33, 99]
even_nums = [e for e in lst if e % 2 == 0]
print(even_nums) # Output: [56, 22, 62, 32, 76]

Method 3: Nested list comprehension

# Create a matrix
matrix = [[i*j for j in range(1, 4)] for i in range(1, 4)]
print(matrix) # Output: [[1, 2, 3], [2, 4, 6], [3, 6, 9]]

Method 4: List comprehension with if-else

# Replace odd numbers with -1


numbers = [1, 2, 3, 4, 5, 6]
result = [x if x % 2 == 0 else -1 for x in numbers]
print(result) # Output: [-1, 2, -1, 4, -1, 6]

27. Split and Join Functions

Method 1: Basic split and join

s = "What is right in your mind is right in your world"


# split - to convert string into list of string
s1 = s.split(" ")
print(s1)
# Output: ['What', 'is', 'right', 'in', 'your', 'mind', 'is', 'right', 'in', 'your', 'world']

s1 = s1[::-1]
print(s1)
# Output: ['world', 'your', 'in', 'right', 'is', 'mind', 'your', 'in', 'right', 'is', 'What']

# join - to convert the list again into string


print(" ".join(s1))
# Output: world your in right is mind your in right is What

Method 2: Split with different delimiters

# Split by comma
csv_data = "John,30,New York"
data = csv_data.split(",")
print(data) # Output: ['John', '30', 'New York']

# Join with different delimiter


new_data = "|".join(data)
print(new_data) # Output: John|30|New York

Method 3: Split with maxsplit parameter


s = "one two three four five"
result = s.split(" ", 2)
print(result) # Output: ['one', 'two', 'three four five']

28. Global and Local Variables

Method 1: Using global keyword

x = 5 # global variable

def f1():
global x
x = 15 # global variable updated
y = 10 # local variable
print(f"x={x} y={y}")

f1() # Output: x=15 y=10


print(x) # Output: 15

Method 2: Without global keyword (creates local variable)

x = 5 # global variable

def f1():
x = 10 # local variable (shadows global x)
y = 10 # local variable
print(f"x={x} y={y}")

f1() # Output: x=10 y=10


print(x) # Output: 5 (global x unchanged)

Method 3: Using globals() function

x = 5 # global variable
def fun():
x = 10 # local variable
d = globals() # d is dictionary
print(f"local x={x} global x={d['x']}")

fun() # Output: local x=10 global x=5

29. Type Conversion

Method 1: Basic type conversions

x = int('123')
a = float('123.42')
b = complex('3+4j')
c = str(12)
d = bool('True')
e = bin(25) # binary
f = oct(25)
g = hex(25)
h = ord('A') # char to unicode
i = chr(98) # unicode to character

print(x, a, b, c, d, e, f, g, h, i, sep="\n")
# Output:
# 123
# 123.42
# (3+4j)
# 12
# True
# 0b11001
# 0o31
# 0x19
# 65
#b

Method 2: Implicit type conversion


x=5
print(type(x)) # Output: <class 'int'>

s1 = '123'
print(type(s1)) # Output: <class 'str'>

print(str(x) + s1) # Output: 5123 (string concatenation)

print(x + int(s1)) # Output: 128 (integer addition)

Method 3: Type conversion with error handling

def safe_convert(value, to_type, default=None):


try:
return to_type(value)
except (ValueError, TypeError):
return default

result = safe_convert("123", int)


print(result) # Output: 123

result = safe_convert("abc", int, 0)


print(result) # Output: 0

Class and Object Concepts

Question 1: Basic Class Implementation

Problem: Create a Person class with attributes name, age, and gender. Implement methods to
display person details and check if the person is an adult.

Method 1: Basic implementation


class Person:
def __init__(self, name, age, gender):
self.name = name
self.age = age
self.gender = gender

def display_details(self):
return f"Name: {self.name}, Age: {self.age}, Gender: {self.gender}"

def is_adult(self):
return self.age >= 18

# Usage
person1 = Person("Alice", 25, "Female")
print(person1.display_details()) # Output: Name: Alice, Age: 25, Gender: Female
print(person1.is_adult()) # Output: True

Method 2: Using property decorators

class Person:
def __init__(self, name, age, gender):
self._name = name
self._age = age
self._gender = gender

@property
def name(self):
return self._name

@property
def age(self):
return self._age

@property
def gender(self):
return self._gender

def display_details(self):
return f"Name: {self.name}, Age: {self.age}, Gender: {self.gender}"
def is_adult(self):
return self.age >= 18

# Usage
person1 = Person("Bob", 16, "Male")
print(person1.display_details()) # Output: Name: Bob, Age: 16, Gender: Male
print(person1.is_adult()) # Output: False

Method 3: Using class methods and static methods

class Person:
def __init__(self, name, age, gender):
self.name = name
self.age = age
self.gender = gender

@classmethod
def from_birth_year(cls, name, birth_year, gender):
from datetime import datetime
current_year = datetime.now().year
age = current_year - birth_year
return cls(name, age, gender)

@staticmethod
def is_valid_age(age):
return 0 <= age <= 120

def display_details(self):
return f"Name: {self.name}, Age: {self.age}, Gender: {self.gender}"

def is_adult(self):
return self.age >= 18

# Usage
person1 = Person.from_birth_year("Charlie", 1995, "Male")
print(person1.display_details()) # Output: Name: Charlie, Age: 29, Gender: Male
print(Person.is_valid_age(150)) # Output: False
Question 2: Bank Account System

Problem: Create a BankAccount class with methods for deposit, withdrawal, and checking
balance. Implement proper validation.

Method 1: Basic implementation with validation

class BankAccount:
def __init__(self, account_holder, initial_balance=0):
self.account_holder = account_holder
self.balance = initial_balance
self.transaction_history = []

def deposit(self, amount):


if amount <= 0:
raise ValueError("Deposit amount must be positive")
self.balance += amount
self.transaction_history.append(f"Deposited: ${amount}")
return f"Deposited ${amount}. New balance: ${self.balance}"

def withdraw(self, amount):


if amount <= 0:
raise ValueError("Withdrawal amount must be positive")
if amount > self.balance:
raise ValueError("Insufficient funds")
self.balance -= amount
self.transaction_history.append(f"Withdrew: ${amount}")
return f"Withdrew ${amount}. New balance: ${self.balance}"

def get_balance(self):
return f"Balance: ${self.balance}"

def get_transaction_history(self):
return self.transaction_history

# Usage
account = BankAccount("John Doe", 1000)
print(account.deposit(500)) # Output: Deposited $500. New balance: $1500
print(account.withdraw(200)) # Output: Withdrew $200. New balance: $1300
print(account.get_balance()) # Output: Balance: $1300

Method 2: Using private attributes and properties

class BankAccount:
def __init__(self, account_holder, initial_balance=0):
self._account_holder = account_holder
self._balance = initial_balance
self._transaction_history = []

@property
def balance(self):
return self._balance

@property
def account_holder(self):
return self._account_holder

def deposit(self, amount):


if amount <= 0:
raise ValueError("Deposit amount must be positive")
self._balance += amount
self._transaction_history.append(f"Deposited: ${amount}")
return self._balance

def withdraw(self, amount):


if amount <= 0:
raise ValueError("Withdrawal amount must be positive")
if amount > self._balance:
raise ValueError("Insufficient funds")
self._balance -= amount
self._transaction_history.append(f"Withdrew: ${amount}")
return self._balance

def __str__(self):
return f"Account Holder: {self.account_holder}, Balance: ${self.balance}"

# Usage
account = BankAccount("Jane Smith", 2000)
account.deposit(300)
account.withdraw(100)
print(account) # Output: Account Holder: Jane Smith, Balance: $2200

Method 3: With transaction history and interest calculation

class BankAccount:
def __init__(self, account_holder, initial_balance=0, interest_rate=0.01):
self.account_holder = account_holder
self.balance = initial_balance
self.interest_rate = interest_rate
self.transactions = []

def deposit(self, amount, description="Deposit"):


if amount <= 0:
raise ValueError("Amount must be positive")
self.balance += amount
self.transactions.append({
'type': 'deposit',
'amount': amount,
'description': description,
'balance_after': self.balance
})
return self.balance

def withdraw(self, amount, description="Withdrawal"):


if amount <= 0:
raise ValueError("Amount must be positive")
if amount > self.balance:
raise ValueError("Insufficient funds")
self.balance -= amount
self.transactions.append({
'type': 'withdrawal',
'amount': amount,
'description': description,
'balance_after': self.balance
})
return self.balance
def add_interest(self):
interest = self.balance * self.interest_rate
self.balance += interest
self.transactions.append({
'type': 'interest',
'amount': interest,
'description': 'Interest added',
'balance_after': self.balance
})
return interest

def get_transaction_history(self):
return self.transactions

def get_balance(self):
return self.balance

# Usage
account = BankAccount("Tom Johnson", 1000, 0.02)
account.deposit(500, "Salary")
account.withdraw(200, "Rent")
account.add_interest()
print(f"Balance: ${account.get_balance():.2f}") # Output: Balance: $1326.00

Inheritance

Question 3: Vehicle Inheritance Hierarchy

Problem: Create a base class Vehicle and derived classes Car, Motorcycle, and Truck with
specific attributes and methods.

Method 1: Basic inheritance

class Vehicle:
def __init__(self, make, model, year, color):
self.make = make
self.model = model
self.year = year
self.color = color
self.speed = 0

def accelerate(self, increment):


self.speed += increment
return f"Accelerating to {self.speed} km/h"

def brake(self, decrement):


self.speed = max(0, self.speed - decrement)
return f"Slowing down to {self.speed} km/h"

def __str__(self):
return f"{self.year} {self.make} {self.model} ({self.color})"

class Car(Vehicle):
def __init__(self, make, model, year, color, doors):
super().__init__(make, model, year, color)
self.doors = doors

def honk(self):
return "Beep beep!"

def __str__(self):
return f"{super().__str__()}, {self.doors} doors"

class Motorcycle(Vehicle):
def __init__(self, make, model, year, color, has_sidecar):
super().__init__(make, model, year, color)
self.has_sidecar = has_sidecar

def wheelie(self):
return "Doing a wheelie!"

def __str__(self):
sidecar_info = "with sidecar" if self.has_sidecar else "without sidecar"
return f"{super().__str__()}, {sidecar_info}"
# Usage
car = Car("Toyota", "Camry", 2022, "Blue", 4)
bike = Motorcycle("Harley", "Davidson", 2021, "Black", False)

print(car) # Output: 2022 Toyota Camry (Blue), 4 doors


print(car.honk()) # Output: Beep beep!
print(bike) # Output: 2021 Harley Davidson (Black), without sidecar
print(bike.wheelie()) # Output: Doing a wheelie!

Method 2: Using abstract base class

from abc import ABC, abstractmethod

class Vehicle(ABC):
def __init__(self, make, model, year, color):
self.make = make
self.model = model
self.year = year
self.color = color
self.speed = 0

@abstractmethod
def get_type(self):
pass

def accelerate(self, increment):


self.speed += increment
return f"Accelerating to {self.speed} km/h"

def brake(self, decrement):


self.speed = max(0, self.speed - decrement)
return f"Slowing down to {self.speed} km/h"

def __str__(self):
return f"{self.year} {self.make} {self.model} ({self.color})"

class Car(Vehicle):
def __init__(self, make, model, year, color, doors):
super().__init__(make, model, year, color)
self.doors = doors

def get_type(self):
return "Car"

def honk(self):
return "Beep beep!"

def __str__(self):
return f"{super().__str__()}, {self.doors} doors"

class Truck(Vehicle):
def __init__(self, make, model, year, color, cargo_capacity):
super().__init__(make, model, year, color)
self.cargo_capacity = cargo_capacity

def get_type(self):
return "Truck"

def load_cargo(self, weight):


if weight > self.cargo_capacity:
return "Cannot load, exceeds capacity"
return f"Loaded {weight} kg of cargo"

def __str__(self):
return f"{super().__str__()}, Capacity: {self.cargo_capacity} kg"

# Usage
vehicles = [
Car("Honda", "Civic", 2020, "Red", 4),
Truck("Ford", "F-150", 2019, "White", 2000)
]

for vehicle in vehicles:


print(f"{vehicle.get_type()}: {vehicle}")
# Output:
# Car: 2020 Honda Civic (Red), 4 doors
# Truck: 2019 Ford F-150 (White), Capacity: 2000 kg

Method 3: Multi-level inheritance

class Vehicle:
def __init__(self, make, model, year):
self.make = make
self.model = model
self.year = year
self.speed = 0

def move(self):
return "Vehicle is moving"

def stop(self):
self.speed = 0
return "Vehicle stopped"

class LandVehicle(Vehicle):
def __init__(self, make, model, year, wheels):
super().__init__(make, model, year)
self.wheels = wheels

def drive(self):
return f"Driving on {self.wheels} wheels"

class WaterVehicle(Vehicle):
def __init__(self, make, model, year, displacement):
super().__init__(make, model, year)
self.displacement = displacement

def sail(self):
return f"Sailing with displacement {self.displacement} tons"

class AmphibiousVehicle(LandVehicle, WaterVehicle):


def __init__(self, make, model, year, wheels, displacement):
LandVehicle.__init__(self, make, model, year, wheels)
WaterVehicle.__init__(self, make, model, year, displacement)
def operate(self, terrain):
if terrain == "land":
return self.drive()
elif terrain == "water":
return self.sail()
else:
return "Cannot operate on this terrain"

# Usage
amphibious = AmphibiousVehicle("Gibbs", "Aquada", 2023, 4, 1.5)
print(amphibious.operate("land")) # Output: Driving on 4 wheels
print(amphibious.operate("water")) # Output: Sailing with displacement 1.5 tons

Question 4: Employee Management System

Problem: Create an employee hierarchy with different types of employees (Manager,


Developer, Designer) with different salary calculations.

Method 1: Basic employee hierarchy

class Employee:
def __init__(self, name, employee_id, base_salary):
self.name = name
self.employee_id = employee_id
self.base_salary = base_salary

def calculate_salary(self):
return self.base_salary

def __str__(self):
return f"{self.name} (ID: {self.employee_id})"

class Manager(Employee):
def __init__(self, name, employee_id, base_salary, bonus):
super().__init__(name, employee_id, base_salary)
self.bonus = bonus
def calculate_salary(self):
return self.base_salary + self.bonus

def __str__(self):
return f"Manager: {super().__str__()}"

class Developer(Employee):
def __init__(self, name, employee_id, base_salary, programming_language):
super().__init__(name, employee_id, base_salary)
self.programming_language = programming_language

def calculate_salary(self):
# Developers get 10% extra for their expertise
return self.base_salary * 1.10

def __str__(self):
return f"Developer: {super().__str__()} - {self.programming_language}"

class Designer(Employee):
def __init__(self, name, employee_id, base_salary, design_tool):
super().__init__(name, employee_id, base_salary)
self.design_tool = design_tool

def calculate_salary(self):
# Designers get 5% extra
return self.base_salary * 1.05

def __str__(self):
return f"Designer: {super().__str__()} - {self.design_tool}"

# Usage
employees = [
Manager("Alice Johnson", "M001", 80000, 15000),
Developer("Bob Smith", "D001", 70000, ""),
Designer("Charlie Brown", "DS001", 65000, "Figma")
]

for employee in employees:


print(f"{employee}: ${employee.calculate_salary():.2f}")
# Output:
# Manager: Alice Johnson (ID: M001): $95000.00
# Developer: Bob Smith (ID: D001) - : $77000.00
# Designer: Charlie Brown (ID: DS001) - Figma: $68250.00

Method 2: Using abstract base class for employees

from abc import ABC, abstractmethod

class Employee(ABC):
def __init__(self, name, employee_id):
self.name = name
self.employee_id = employee_id

@abstractmethod
def calculate_salary(self):
pass

@abstractmethod
def get_role(self):
pass

def __str__(self):
return f"{self.get_role()}: {self.name} (ID: {self.employee_id})"

class FullTimeEmployee(Employee):
def __init__(self, name, employee_id, monthly_salary):
super().__init__(name, employee_id)
self.monthly_salary = monthly_salary

def calculate_salary(self):
return self.monthly_salary * 12

def get_role(self):
return "Full-time Employee"

class PartTimeEmployee(Employee):
def __init__(self, name, employee_id, hourly_rate, hours_per_week):
super().__init__(name, employee_id)
self.hourly_rate = hourly_rate
self.hours_per_week = hours_per_week

def calculate_salary(self):
return self.hourly_rate * self.hours_per_week * 52

def get_role(self):
return "Part-time Employee"

class Contractor(Employee):
def __init__(self, name, employee_id, project_rate, projects_completed):
super().__init__(name, employee_id)
self.project_rate = project_rate
self.projects_completed = projects_completed

def calculate_salary(self):
return self.project_rate * self.projects_completed

def get_role(self):
return "Contractor"

# Usage
employees = [
FullTimeEmployee("David Wilson", "FT001", 6000),
PartTimeEmployee("Eva Garcia", "PT001", 25, 20),
Contractor("Frank Miller", "C001", 5000, 3)
]

for employee in employees:


print(f"{employee}: Annual earnings: ${employee.calculate_salary():.2f}")
Polymorphism

Question 5: Shape Hierarchy with Polymorphism

Problem: Create a shape hierarchy where different shapes can calculate their area and
perimeter using polymorphism.

Method 1: Using abstract base class

from abc import ABC, abstractmethod


import math

class Shape(ABC):
@abstractmethod
def area(self):
pass

@abstractmethod
def perimeter(self):
pass

def __str__(self):
return f"{self.__class__.__name__} - Area: {self.area():.2f}, Perimeter: {self.perimeter():.2f}"

class Circle(Shape):
def __init__(self, radius):
self.radius = radius

def area(self):
return math.pi * self.radius ** 2

def perimeter(self):
return 2 * math.pi * self.radius

class Rectangle(Shape):
def __init__(self, length, width):
self.length = length
self.width = width

def area(self):
return self.length * self.width

def perimeter(self):
return 2 * (self.length + self.width)

class Triangle(Shape):
def __init__(self, side1, side2, side3):
self.side1 = side1
self.side2 = side2
self.side3 = side3

def area(self):
# Using Heron's formula
s = self.perimeter() / 2
return math.sqrt(s * (s - self.side1) * (s - self.side2) * (s - self.side3))

def perimeter(self):
return self.side1 + self.side2 + self.side3

# Usage
shapes = [
Circle(5),
Rectangle(4, 6),
Triangle(3, 4, 5)
]

for shape in shapes:


print(shape)
# Output:
# Circle - Area: 78.54, Perimeter: 31.42
# Rectangle - Area: 24.00, Perimeter: 20.00
# Triangle - Area: 6.00, Perimeter: 12.00

Method 2: Using duck typing (without ABC)


import math

class Circle:
def __init__(self, radius):
self.radius = radius

def area(self):
return math.pi * self.radius ** 2

def perimeter(self):
return 2 * math.pi * self.radius

class Square:
def __init__(self, side):
self.side = side

def area(self):
return self.side ** 2

def perimeter(self):
return 4 * self.side

def print_shape_info(shape):
"""This function works with any object that has area() and perimeter() methods"""
print(f"{shape.__class__.__name__}: Area = {shape.area():.2f}, Perimeter = {shape.perimeter():.2f}")

# Usage
circle = Circle(7)
square = Square(5)

print_shape_info(circle) # Output: Circle: Area = 153.94, Perimeter = 43.98


print_shape_info(square) # Output: Square: Area = 25.00, Perimeter = 20.00

Method 3: Using function overloading (simulated)

class Calculator:
def add(self, *args):
if len(args) == 2 and all(isinstance(arg, (int, float)) for arg in args):
return args[0] + args[1]
elif len(args) == 2 and all(isinstance(arg, str) for arg in args):
return args[0] + " " + args[1]
elif len(args) == 3:
return sum(args)
else:
raise ValueError("Unsupported operation")

def multiply(self, *args):


if len(args) == 2 and all(isinstance(arg, (int, float)) for arg in args):
return args[0] * args[1]
elif len(args) == 2 and isinstance(args[0], str) and isinstance(args[1], int):
return args[0] * args[1]
else:
raise ValueError("Unsupported operation")

# Usage
calc = Calculator()
print(calc.add(5, 3)) # Output: 8
print(calc.add("Hello", "World")) # Output: Hello World
print(calc.add(1, 2, 3)) # Output: 6
print(calc.multiply(4, 5)) # Output: 20
print(calc.multiply("Hi", 3)) # Output: HiHiHi

Encapsulation

Question 6: Secure User Account System

Problem: Create a user account system with proper encapsulation, including private
attributes and validation.

Method 1: Basic encapsulation with validation

class UserAccount:
def __init__(self, username, email, password):
self._username = username
self._email = email
self._set_password(password)
self._is_active = True
self._login_attempts = 0

def _set_password(self, password):


if len(password) < 8:
raise ValueError("Password must be at least 8 characters long")
# In a real application, you would hash the password here
self._password = password

def authenticate(self, password):


if not self._is_active:
return False

if password == self._password:
self._login_attempts = 0
return True
else:
self._login_attempts += 1
if self._login_attempts >= 3:
self._is_active = False
print("Account locked due to too many failed attempts")
return False

def reset_password(self, old_password, new_password):


if self.authenticate(old_password):
self._set_password(new_password)
return True
return False

def unlock_account(self, admin_key):


# Simple admin key check - in real application, use proper authentication
if admin_key == "ADMIN123":
self._is_active = True
self._login_attempts = 0
return True
return False
@property
def username(self):
return self._username

@property
def email(self):
return self._email

@property
def is_active(self):
return self._is_active

# Usage
user = UserAccount("john_doe", "[email protected]", "secure123")
print(user.authenticate("wrong_pass")) # Output: False
print(user.authenticate("wrong_pass2")) # Output: False
print(user.authenticate("wrong_pass3")) # Output: False, then "Account locked due to too many failed attempts"
print(user.is_active) # Output: False

user.unlock_account("ADMIN123") # Unlock account


print(user.is_active) # Output: True

Method 2: Using property setters for validation

class UserProfile:
def __init__(self, username, email, age):
self._username = username
self._email = email
self._age = age

@property
def username(self):
return self._username

@username.setter
def username(self, value):
if not value or not isinstance(value, str):
raise ValueError("Username must be a non-empty string")
if len(value) < 3:
raise ValueError("Username must be at least 3 characters long")
self._username = value

@property
def email(self):
return self._email

@email.setter
def email(self, value):
if "@" not in value or "." not in value:
raise ValueError("Invalid email format")
self._email = value

@property
def age(self):
return self._age

@age.setter
def age(self, value):
if not isinstance(value, int) or value < 0 or value > 120:
raise ValueError("Age must be a positive integer between 0 and 120")
self._age = value

def __str__(self):
return f"User: {self.username}, Email: {self.email}, Age: {self.age}"

# Usage
profile = UserProfile("jane_doe", "[email protected]", 25)
print(profile) # Output: User: jane_doe, Email: [email protected], Age: 25

try:
profile.username = "ab" # Too short
except ValueError as e:
print(e) # Output: Username must be at least 3 characters long

try:
profile.email = "invalid-email" # Invalid format
except ValueError as e:
print(e) # Output: Invalid email format
profile.age = 30
print(profile.age) # Output: 30

Method 3: Advanced encapsulation with password hashing

import hashlib
import os

class SecureUser:
def __init__(self, username, password):
self.username = username
self._salt = os.urandom(32) # Generate a random salt
self._hashed_password = self._hash_password(password, self._salt)
self._is_locked = False
self._failed_attempts = 0

def _hash_password(self, password, salt):


"""Hash the password with the salt using SHA-256"""
return hashlib.pbkdf2_hmac('sha256', password.encode(), salt, 100000)

def verify_password(self, password):


"""Verify if the provided password matches the stored hash"""
if self._is_locked:
return False

attempted_hash = self._hash_password(password, self._salt)


if attempted_hash == self._hashed_password:
self._failed_attempts = 0
return True
else:
self._failed_attempts += 1
if self._failed_attempts >= 5:
self._is_locked = True
return False

def change_password(self, old_password, new_password):


"""Change the password after verifying the old one"""
if self.verify_password(old_password):
self._hashed_password = self._hash_password(new_password, self._salt)
return True
return False

def reset_password(self, admin_token, new_password):


"""Admin can reset password with special token"""
if admin_token == "SECURE_ADMIN_TOKEN_123":
self._hashed_password = self._hash_password(new_password, self._salt)
self._is_locked = False
self._failed_attempts = 0
return True
return False

@property
def is_locked(self):
return self._is_locked

def unlock_account(self, admin_token):


if admin_token == "SECURE_ADMIN_TOKEN_123":
self._is_locked = False
self._failed_attempts = 0
return True
return False

# Usage
user = SecureUser("alice", "mySecurePassword123")
print(user.verify_password("wrong_pass")) # Output: False
print(user.verify_password("mySecurePassword123")) # Output: True

user.change_password("mySecurePassword123", "newSecurePassword456")
print(user.verify_password("newSecurePassword456")) # Output: True
Abstraction

Question 7: Database Connection Abstraction

Problem: Create an abstract database connection class with concrete implementations for
different database types.

Method 1: Using ABC for database abstraction

from abc import ABC, abstractmethod

class DatabaseConnection(ABC):
@abstractmethod
def connect(self):
pass

@abstractmethod
def disconnect(self):
pass

@abstractmethod
def execute_query(self, query):
pass

@abstractmethod
def commit(self):
pass

@abstractmethod
def rollback(self):
pass

class MySQLConnection(DatabaseConnection):
def __init__(self, host, user, password, database):
self.host = host
self.user = user
self.password = password
self.database = database
self.connection = None

def connect(self):
# Simulate MySQL connection
print(f"Connecting to MySQL database {self.database} on {self.host}")
self.connection = f"mysql_connection_{self.database}"
return True

def disconnect(self):
if self.connection:
print(f"Disconnecting from MySQL database {self.database}")
self.connection = None

def execute_query(self, query):


if not self.connection:
raise Exception("Not connected to database")
print(f"Executing MySQL query: {query}")
return f"result_of_{query}"

def commit(self):
print("Committing transaction in MySQL")

def rollback(self):
print("Rolling back transaction in MySQL")

class PostgreSQLConnection(DatabaseConnection):
def __init__(self, host, user, password, database):
self.host = host
self.user = user
self.password = password
self.database = database
self.connection = None

def connect(self):
# Simulate PostgreSQL connection
print(f"Connecting to PostgreSQL database {self.database} on {self.host}")
self.connection = f"postgresql_connection_{self.database}"
return True
def disconnect(self):
if self.connection:
print(f"Disconnecting from PostgreSQL database {self.database}")
self.connection = None

def execute_query(self, query):


if not self.connection:
raise Exception("Not connected to database")
print(f"Executing PostgreSQL query: {query}")
return f"result_of_{query}"

def commit(self):
print("Committing transaction in PostgreSQL")

def rollback(self):
print("Rolling back transaction in PostgreSQL")

# Usage
def database_operations(db_connection):
db_connection.connect()
result = db_connection.execute_query("SELECT * FROM users")
print(f"Result: {result}")
db_connection.commit()
db_connection.disconnect()

mysql_db = MySQLConnection("localhost", "root", "password", "mydb")


postgresql_db = PostgreSQLConnection("localhost", "postgres", "password", "mydb")

database_operations(mysql_db)
database_operations(postgresql_db)

Method 2: Payment Processor Abstraction

from abc import ABC, abstractmethod

class PaymentProcessor(ABC):
@abstractmethod
def process_payment(self, amount):
pass

@abstractmethod
def refund_payment(self, amount, transaction_id):
pass

class CreditCardProcessor(PaymentProcessor):
def __init__(self, card_number, expiry_date, cvv):
self.card_number = card_number
self.expiry_date = expiry_date
self.cvv = cvv

def process_payment(self, amount):


# Simulate credit card payment processing
print(f"Processing credit card payment of ${amount}")
transaction_id = f"CC_{self.card_number[-4:]}_{amount}"
print(f"Payment successful. Transaction ID: {transaction_id}")
return transaction_id

def refund_payment(self, amount, transaction_id):


print(f"Refunding ${amount} to credit card via transaction {transaction_id}")
return True

class PayPalProcessor(PaymentProcessor):
def __init__(self, email):
self.email = email

def process_payment(self, amount):


# Simulate PayPal payment processing
print(f"Processing PayPal payment of ${amount} for {self.email}")
transaction_id = f"PP_{self.email}_{amount}"
print(f"Payment successful. Transaction ID: {transaction_id}")
return transaction_id

def refund_payment(self, amount, transaction_id):


print(f"Refunding ${amount} to PayPal account via transaction {transaction_id}")
return True

class CryptoProcessor(PaymentProcessor):
def __init__(self, wallet_address):
self.wallet_address = wallet_address

def process_payment(self, amount):


# Simulate cryptocurrency payment processing
print(f"Processing crypto payment of ${amount} to {self.wallet_address}")
transaction_id = f"CRYPTO_{self.wallet_address[:8]}_{amount}"
print(f"Payment successful. Transaction ID: {transaction_id}")
return transaction_id

def refund_payment(self, amount, transaction_id):


print(f"Refunding ${amount} in cryptocurrency via transaction {transaction_id}")
return True

# Usage
def process_order(payment_processor, amount):
transaction_id = payment_processor.process_payment(amount)
print(f"Order processed with transaction: {transaction_id}")
return transaction_id

credit_card = CreditCardProcessor("4111111111111111", "12/25", "123")


paypal = PayPalProcessor("[email protected]")
crypto = CryptoProcessor("1A1zP1eP5QGefi2DMPTfTL5SLmv7DivfNa")

process_order(credit_card, 100)
process_order(paypal, 75)
process_order(crypto, 200)

Special Methods and Operator Overloading

Question 8: Vector Class with Operator Overloading

Problem: Create a Vector class that supports mathematical operations through operator
overloading.
Method 1: Basic vector operations

class Vector:
def __init__(self, *components):
self.components = list(components)

def __repr__(self):
return f"Vector({', '.join(map(str, self.components))})"

def __add__(self, other):


if len(self.components) != len(other.components):
raise ValueError("Vectors must have the same dimension")
return Vector(*[a + b for a, b in zip(self.components, other.components)])

def __sub__(self, other):


if len(self.components) != len(other.components):
raise ValueError("Vectors must have the same dimension")
return Vector(*[a - b for a, b in zip(self.components, other.components)])

def __mul__(self, scalar):


if not isinstance(scalar, (int, float)):
raise ValueError("Can only multiply by scalar")
return Vector(*[a * scalar for a in self.components])

def __rmul__(self, scalar):


return self.__mul__(scalar)

def __truediv__(self, scalar):


if not isinstance(scalar, (int, float)):
raise ValueError("Can only divide by scalar")
return Vector(*[a / scalar for a in self.components])

def __abs__(self):
return sum(a ** 2 for a in self.components) ** 0.5

def dot(self, other):


if len(self.components) != len(other.components):
raise ValueError("Vectors must have the same dimension")
return sum(a * b for a, b in zip(self.components, other.components))

def __eq__(self, other):


if not isinstance(other, Vector):
return False
return self.components == other.components

# Usage
v1 = Vector(1, 2, 3)
v2 = Vector(4, 5, 6)

print(v1 + v2) # Output: Vector(5, 7, 9)


print(v2 - v1) # Output: Vector(3, 3, 3)
print(v1 * 3) # Output: Vector(3, 6, 9)
print(2 * v2) # Output: Vector(8, 10, 12)
print(v1 / 2) # Output: Vector(0.5, 1.0, 1.5)
print(abs(v1)) # Output: 3.7416573867739413
print(v1.dot(v2)) # Output: 32
print(v1 == v2) # Output: False

Method 2: Matrix class with advanced operations

class Matrix:
def __init__(self, rows, cols, data=None):
self.rows = rows
self.cols = cols
if data is None:
self.data = [[0] * cols for _ in range(rows)]
else:
if len(data) != rows or any(len(row) != cols for row in data):
raise ValueError("Invalid data dimensions")
self.data = data

def __repr__(self):
return "Matrix(\n" + "\n".join(" " + str(row) for row in self.data) + "\n)"

def __add__(self, other):


if self.rows != other.rows or self.cols != other.cols:
raise ValueError("Matrices must have the same dimensions")
result = Matrix(self.rows, self.cols)
for i in range(self.rows):
for j in range(self.cols):
result.data[i][j] = self.data[i][j] + other.data[i][j]
return result

def __sub__(self, other):


if self.rows != other.rows or self.cols != other.cols:
raise ValueError("Matrices must have the same dimensions")
result = Matrix(self.rows, self.cols)
for i in range(self.rows):
for j in range(self.cols):
result.data[i][j] = self.data[i][j] - other.data[i][j]
return result

def __mul__(self, other):


if isinstance(other, (int, float)):
# Scalar multiplication
result = Matrix(self.rows, self.cols)
for i in range(self.rows):
for j in range(self.cols):
result.data[i][j] = self.data[i][j] * other
return result
elif isinstance(other, Matrix):
# Matrix multiplication
if self.cols != other.rows:
raise ValueError("Number of columns in first matrix must equal number of rows in second matrix")
result = Matrix(self.rows, other.cols)
for i in range(self.rows):
for j in range(other.cols):
for k in range(self.cols):
result.data[i][j] += self.data[i][k] * other.data[k][j]
return result
else:
raise ValueError("Can only multiply by scalar or another matrix")

def __rmul__(self, scalar):


return self.__mul__(scalar)

def transpose(self):
result = Matrix(self.cols, self.rows)
for i in range(self.rows):
for j in range(self.cols):
result.data[j][i] = self.data[i][j]
return result

def __eq__(self, other):


if not isinstance(other, Matrix):
return False
return self.data == other.data

# Usage
m1 = Matrix(2, 3, [[1, 2, 3], [4, 5, 6]])
m2 = Matrix(2, 3, [[7, 8, 9], [10, 11, 12]])
m3 = Matrix(3, 2, [[1, 2], [3, 4], [5, 6]])

print("Matrix 1:")
print(m1)
print("Matrix 2:")
print(m2)
print("Matrix 1 + Matrix 2:")
print(m1 + m2)
print("Matrix 1 * 3:")
print(m1 * 3)
print("Matrix 1 * Matrix 3:")
print(m1 * m3)
print("Transpose of Matrix 1:")
print(m1.transpose())

Advanced OOP Concepts

Question 9: Singleton Pattern Implementation

Problem: Implement a singleton database connection class that ensures only one instance
exists.

Method 1: Using new method


class DatabaseConnection:
_instance = None

def __new__(cls, *args, **kwargs):


if cls._instance is None:
cls._instance = super().__new__(cls)
cls._instance._initialized = False
return cls._instance

def __init__(self, connection_string):


if not self._initialized:
self.connection_string = connection_string
self.connection = self._connect()
self._initialized = True

def _connect(self):
print(f"Connecting to database: {self.connection_string}")
return f"connection_to_{self.connection_string}"

def execute_query(self, query):


print(f"Executing query: {query}")
return f"result_of_{query}"

def __str__(self):
return f"DatabaseConnection({self.connection_string})"

# Usage
db1 = DatabaseConnection("mysql://localhost:3306/mydb")
db2 = DatabaseConnection("postgresql://localhost:5432/mydb")

print(db1 is db2) # Output: True (same instance)


print(db1.connection_string) # Output: mysql://localhost:3306/mydb
print(db2.connection_string) # Output: mysql://localhost:3306/mydb (same as db1)

db1.execute_query("SELECT * FROM users")

Method 2: Using metaclass


class SingletonMeta(type):
_instances = {}

def __call__(cls, *args, **kwargs):


if cls not in cls._instances:
cls._instances[cls] = super().__call__(*args, **kwargs)
return cls._instances[cls]

class Logger(metaclass=SingletonMeta):
def __init__(self, log_file="app.log"):
self.log_file = log_file
self._open_log_file()

def _open_log_file(self):
print(f"Opening log file: {self.log_file}")
self.file = open(self.log_file, "a")

def log(self, message):


log_entry = f"[LOG] {message}\n"
self.file.write(log_entry)
self.file.flush()

def __del__(self):
if hasattr(self, 'file'):
self.file.close()
print("Log file closed")

# Usage
logger1 = Logger("application.log")
logger2 = Logger("different.log") # This will use the same instance

print(logger1 is logger2) # Output: True


logger1.log("This is a test message")
logger2.log("Another message") # Both write to the same file

Method 3: Using decorator


def singleton(cls):
instances = {}

def get_instance(*args, **kwargs):


if cls not in instances:
instances[cls] = cls(*args, **kwargs)
return instances[cls]

return get_instance

@singleton
class Configuration:
def __init__(self, config_file="config.ini"):
self.config_file = config_file
self.settings = self._load_config()

def _load_config(self):
print(f"Loading configuration from {self.config_file}")
# Simulate loading config
return {"database": "localhost", "port": 8080, "debug": True}

def get_setting(self, key):


return self.settings.get(key)

def set_setting(self, key, value):


self.settings[key] = value

# Usage
config1 = Configuration()
config2 = Configuration("different.ini") # Same instance

print(config1 is config2) # Output: True


print(config1.get_setting("database")) # Output: localhost
config2.set_setting("database", "production.db")
print(config1.get_setting("database")) # Output: production.db (changed in both)
Question 10: Factory Pattern Implementation

Problem: Implement a factory pattern to create different types of documents.

Method 1: Simple factory method

from abc import ABC, abstractmethod

class Document(ABC):
@abstractmethod
def create(self):
pass

@abstractmethod
def save(self):
pass

class PDFDocument(Document):
def create(self):
return "Creating PDF document"

def save(self):
return "Saving PDF document"

class WordDocument(Document):
def create(self):
return "Creating Word document"

def save(self):
return "Saving Word document"

class ExcelDocument(Document):
def create(self):
return "Creating Excel document"

def save(self):
return "Saving Excel document"
class DocumentFactory:
@staticmethod
def create_document(doc_type):
if doc_type == "pdf":
return PDFDocument()
elif doc_type == "word":
return WordDocument()
elif doc_type == "excel":
return ExcelDocument()
else:
raise ValueError(f"Unknown document type: {doc_type}")

# Usage
factory = DocumentFactory()

documents = [
factory.create_document("pdf"),
factory.create_document("word"),
factory.create_document("excel")
]

for doc in documents:


print(doc.create())
print(doc.save())
print()

Method 2: Factory with registration system

from abc import ABC, abstractmethod

class Document(ABC):
@abstractmethod
def create(self):
pass

@abstractmethod
def save(self):
pass
class PDFDocument(Document):
def create(self):
return "Creating PDF document"

def save(self):
return "Saving PDF document"

class WordDocument(Document):
def create(self):
return "Creating Word document"

def save(self):
return "Saving Word document"

class DocumentFactory:
_creators = {}

@classmethod
def register_document(cls, doc_type, creator):
cls._creators[doc_type] = creator

@classmethod
def create_document(cls, doc_type, *args, **kwargs):
creator = cls._creators.get(doc_type)
if not creator:
raise ValueError(f"Unknown document type: {doc_type}")
return creator(*args, **kwargs)

# Register document types


DocumentFactory.register_document("pdf", PDFDocument)
DocumentFactory.register_document("word", WordDocument)

# Usage
pdf_doc = DocumentFactory.create_document("pdf")
word_doc = DocumentFactory.create_document("word")

print(pdf_doc.create()) # Output: Creating PDF document


print(word_doc.save()) # Output: Saving Word document
Method 3: Abstract factory pattern

from abc import ABC, abstractmethod

# Abstract products
class Button(ABC):
@abstractmethod
def render(self):
pass

class Checkbox(ABC):
@abstractmethod
def render(self):
pass

# Concrete products for Windows


class WindowsButton(Button):
def render(self):
return "Rendering Windows button"

class WindowsCheckbox(Checkbox):
def render(self):
return "Rendering Windows checkbox"

# Concrete products for Mac


class MacButton(Button):
def render(self):
return "Rendering Mac button"

class MacCheckbox(Checkbox):
def render(self):
return "Rendering Mac checkbox"

# Abstract factory
class GUIFactory(ABC):
@abstractmethod
def create_button(self):
pass
@abstractmethod
def create_checkbox(self):
pass

# Concrete factories
class WindowsFactory(GUIFactory):
def create_button(self):
return WindowsButton()

def create_checkbox(self):
return WindowsCheckbox()

class MacFactory(GUIFactory):
def create_button(self):
return MacButton()

def create_checkbox(self):
return MacCheckbox()

# Client code
def create_ui(factory):
button = factory.create_button()
checkbox = factory.create_checkbox()

print(button.render())
print(checkbox.render())

# Usage
windows_factory = WindowsFactory()
mac_factory = MacFactory()

print("Windows UI:")
create_ui(windows_factory)

print("\nMac UI:")
create_ui(mac_factory)
Design Patterns

Question 11: Observer Pattern Implementation

Problem: Implement an observer pattern for a news publisher and subscribers.

Method 1: Basic observer pattern

class NewsPublisher:
def __init__(self):
self._subscribers = []
self._latest_news = None

def attach(self, subscriber):


if subscriber not in self._subscribers:
self._subscribers.append(subscriber)

def detach(self, subscriber):


if subscriber in self._subscribers:
self._subscribers.remove(subscriber)

def notify_subscribers(self):
for subscriber in self._subscribers:
subscriber.update(self._latest_news)

def add_news(self, news):


self._latest_news = news
self.notify_subscribers()

def get_latest_news(self):
return self._latest_news

class Subscriber:
def __init__(self, name):
self.name = name
self.received_news = []
def update(self, news):
self.received_news.append(news)
print(f"{self.name} received news: {news}")

def get_news_history(self):
return self.received_news

# Usage
publisher = NewsPublisher()

subscriber1 = Subscriber("Alice")
subscriber2 = Subscriber("Bob")
subscriber3 = Subscriber("Charlie")

publisher.attach(subscriber1)
publisher.attach(subscriber2)

publisher.add_news("Breaking: 4.0 announced!")


# Output:
# Alice received news: Breaking: 4.0 announced!
# Bob received news: Breaking: 4.0 announced!

publisher.attach(subscriber3)
publisher.add_news("Update: 4.0 release delayed")
# Output:
# Alice received news: Update: 4.0 release delayed
# Bob received news: Update: 4.0 release delayed
# Charlie received news: Update: 4.0 release delayed

publisher.detach(subscriber2)
publisher.add_news("Final: 3.12 is the latest")
# Output:
# Alice received news: Final: 3.12 is the latest
# Charlie received news: Final: 3.12 is the latest

Method 2: Using ABC for observer pattern


from abc import ABC, abstractmethod

class Observer(ABC):
@abstractmethod
def update(self, subject):
pass

class Subject(ABC):
def __init__(self):
self._observers = []

def attach(self, observer):


if observer not in self._observers:
self._observers.append(observer)

def detach(self, observer):


if observer in self._observers:
self._observers.remove(observer)

def notify(self):
for observer in self._observers:
observer.update(self)

class WeatherStation(Subject):
def __init__(self):
super().__init__()
self._temperature = 0
self._humidity = 0
self._pressure = 0

@property
def temperature(self):
return self._temperature

@property
def humidity(self):
return self._humidity

@property
def pressure(self):
return self._pressure

def set_measurements(self, temperature, humidity, pressure):


self._temperature = temperature
self._humidity = humidity
self._pressure = pressure
self.notify()

def __str__(self):
return f"Weather: {self.temperature}°C, {self.humidity}% humidity, {self.pressure} hPa"

class DisplayDevice(Observer):
def __init__(self, name):
self.name = name
self.last_update = None

def update(self, subject):


self.last_update = str(subject)
print(f"{self.name} display updated: {self.last_update}")

def get_last_update(self):
return self.last_update

# Usage
weather_station = WeatherStation()

phone_display = DisplayDevice("Phone")
tv_display = DisplayDevice("TV")
tablet_display = DisplayDevice("Tablet")

weather_station.attach(phone_display)
weather_station.attach(tv_display)

weather_station.set_measurements(25, 65, 1013)


# Output:
# Phone display updated: Weather: 25°C, 65% humidity, 1013 hPa
# TV display updated: Weather: 25°C, 65% humidity, 1013 hPa
weather_station.attach(tablet_display)
weather_station.set_measurements(26, 70, 1012)
# Output:
# Phone display updated: Weather: 26°C, 70% humidity, 1012 hPa
# TV display updated: Weather: 26°C, 70% humidity, 1012 hPa
# Tablet display updated: Weather: 26°C, 70% humidity, 1012 hPa

Real-world OOP Problems

Question 12: E-commerce System

Problem: Design an e-commerce system with products, shopping cart, and orders.

Method 1: Basic e-commerce system

from datetime import datetime


from typing import List, Dict

class Product:
def __init__(self, product_id: str, name: str, price: float, description: str = "", stock: int = 0):
self.product_id = product_id
self.name = name
self.price = price
self.description = description
self.stock = stock

def update_stock(self, quantity: int):


if self.stock + quantity < 0:
raise ValueError("Insufficient stock")
self.stock += quantity

def __str__(self):
return f"{self.name} - ${self.price:.2f} (Stock: {self.stock})"
class CartItem:
def __init__(self, product: Product, quantity: int = 1):
self.product = product
self.quantity = quantity

def update_quantity(self, quantity: int):


if quantity <= 0:
raise ValueError("Quantity must be positive")
self.quantity = quantity

def get_total_price(self):
return self.product.price * self.quantity

def __str__(self):
return f"{self.product.name} x {self.quantity} = ${self.get_total_price():.2f}"

class ShoppingCart:
def __init__(self):
self.items: Dict[str, CartItem] = {}

def add_item(self, product: Product, quantity: int = 1):


if product.product_id in self.items:
self.items[product.product_id].update_quantity(
self.items[product.product_id].quantity + quantity
)
else:
self.items[product.product_id] = CartItem(product, quantity)

def remove_item(self, product_id: str):


if product_id in self.items:
del self.items[product_id]

def update_quantity(self, product_id: str, quantity: int):


if product_id in self.items:
if quantity <= 0:
self.remove_item(product_id)
else:
self.items[product_id].update_quantity(quantity)

def get_total(self):
return sum(item.get_total_price() for item in self.items.values())

def clear(self):
self.items.clear()

def __str__(self):
if not self.items:
return "Cart is empty"

items_str = "\n".join(str(item) for item in self.items.values())


return f"{items_str}\nTotal: ${self.get_total():.2f}"

class Order:
def __init__(self, order_id: str, cart: ShoppingCart, customer_name: str, shipping_address: str):
self.order_id = order_id
self.customer_name = customer_name
self.shipping_address = shipping_address
self.order_date = datetime.now()
self.items = cart.items.copy()
self.total_amount = cart.get_total()
self.status = "Pending"

def process_order(self):
# Check stock availability
for item in self.items.values():
if item.quantity > item.product.stock:
raise ValueError(f"Insufficient stock for {item.product.name}")

# Update stock
for item in self.items.values():
item.product.update_stock(-item.quantity)

self.status = "Processed"
return True

def cancel_order(self):
if self.status == "Cancelled":
return False
# Restore stock
for item in self.items.values():
item.product.update_stock(item.quantity)

self.status = "Cancelled"
return True

def __str__(self):
items_str = "\n".join(f" {item}" for item in self.items.values())
return (f"Order #{self.order_id}\n"
f"Customer: {self.customer_name}\n"
f"Date: {self.order_date.strftime('%Y-%m-%d %H:%M')}\n"
f"Status: {self.status}\n"
f"Items:\n{items_str}\n"
f"Total: ${self.total_amount:.2f}")

# Usage
# Create products
laptop = Product("P001", "Laptop", 999.99, "High-performance laptop", 10)
mouse = Product("P002", "Mouse", 25.50, "Wireless mouse", 50)
keyboard = Product("P003", "Keyboard", 75.00, "Mechanical keyboard", 25)

# Create shopping cart


cart = ShoppingCart()
cart.add_item(laptop, 1)
cart.add_item(mouse, 2)
cart.add_item(keyboard, 1)

print("Shopping Cart:")
print(cart)

# Create order
order = Order("ORD001", cart, "John Doe", "123 Main St, Anytown")
print("\nOrder Details:")
print(order)

# Process order
try:
order.process_order()
print(f"\nOrder processed successfully. New status: {order.status}")
print(f"Laptop stock after order: {laptop.stock}")
except ValueError as e:
print(f"Error processing order: {e}")

# Try to cancel order


if order.cancel_order():
print(f"\nOrder cancelled. New status: {order.status}")
print(f"Laptop stock after cancellation: {laptop.stock}")

30. Decorators

Method 1: Function decorator

def welcome(fx):
def mfx(*args, **kwargs):
print("Before function execution")
result = fx(*args, **kwargs)
print("Thanks for using the function")
return result
return mfx

# Decorator function without arguments


@welcome
def hello():
print("Hello!")

# Decorator function with arguments


@welcome
def add(a, b):
print(a + b)

hello()
# Output:
# Before function execution
# Hello!
# Thanks for using the function
add(1, 3)
# Output:
# Before function execution
#4
# Thanks for using the function

Method 2: Class decorator

class Calculator:
def __init__(self, func):
self.function = func

def __call__(self, *args, **kwargs):


result = self.function(*args, **kwargs)
return result ** 2

@Calculator
def add(a, b):
return a + b

print(add(10, 20)) # Output: 900

Method 3: Decorator with parameters

def repeat(n):
def decorator(func):
def wrapper(*args, **kwargs):
for _ in range(n):
result = func(*args, **kwargs)
return result
return wrapper
return decorator

@repeat(3)
def say_hello():
print("Hello!")
say_hello()
# Output:
# Hello!
# Hello!
# Hello!

Method 4: Property decorator

class Circle:
def __init__(self, radius):
self._radius = radius

@property
def radius(self):
"""Getter method for radius"""
return self._radius

@radius.setter
def radius(self, value):
"""Setter method for radius"""
if value < 0:
raise ValueError("Value cannot be negative")
self._radius = value

@property
def area(self):
"""Method to calculate the area"""
return 3.14 * self._radius ** 2

obj = Circle(radius=5)
print(f"radius: {obj.radius}") # Output: radius: 5
print(f"area: {obj.area}") # Output: area: 78.5

31. Iterators

Method 1: Basic iterator usage


lst = [23, 65, 22, 76, 34, 98, 43]
it = iter(lst)
while True:
try:
print(next(it))
except StopIteration:
break
# Output: 23 65 22 76 34 98 43

Method 2: Creating custom iterator

class CountDown:
def __init__(self, start):
self.current = start
self.end = 0

def __iter__(self):
return self

def __next__(self):
if self.current <= self.end:
raise StopIteration
else:
self.current -= 1
return self.current + 1

for num in CountDown(5):


print(num, end=" ")
# Output: 5 4 3 2 1

Method 3: Iterator with condition

class EvenNumbers:
def __init__(self, max):
self.max = max
self.current = 0

def __iter__(self):
return self

def __next__(self):
if self.current >= self.max:
raise StopIteration
result = self.current
self.current += 2
return result

for num in EvenNumbers(10):


print(num, end=" ")
# Output: 0 2 4 6 8

32. Generators

Method 1: Basic generator function

def countdown(n):
while n > 0:
yield n
n -= 1

for i in countdown(5):
print(i, end=" ")
# Output: 5 4 3 2 1

Method 2: Generator expression

squares = (x*x for x in range(1, 6))


for num in squares:
print(num, end=" ")
# Output: 1 4 9 16 25

Method 3: Generator for Fibonacci sequence


def fibonacci():
a, b = 0, 1
while True:
yield a
a, b = b, a + b

fib = fibonacci()
for _ in range(10):
print(next(fib), end=" ")
# Output: 0 1 1 2 3 5 8 13 21 34

33. Exception Handling

Method 1: Basic try-except

try:
a = int(input("Enter a number: "))
b = int(input("Enter another number: "))
print(a / b)
except ZeroDivisionError:
print("Division by zero is not allowed")
except ValueError:
print("Please enter valid integers")
except Exception as e:
print(f"An error occurred: {e}")
else:
print("Division successful")
finally:
print("This will always execute")

Method 2: Multiple exceptions in one block

try:
# Some code that might raise exceptions
result = 10 / int(input("Enter a number: "))
except (ZeroDivisionError, ValueError):
print("Invalid input or division by zero")
Method 3: Custom exceptions

class NegativeNumberError(Exception):
pass

def check_positive(number):
if number < 0:
raise NegativeNumberError("Number cannot be negative")
return number

try:
num = int(input("Enter a positive number: "))
check_positive(num)
print(f"You entered: {num}")
except NegativeNumberError as e:
print(e)
except ValueError:
print("Please enter a valid integer")

34. File Handling

Method 1: Reading a file

# Reading entire file


with open('sample.txt', 'r') as file:
content = file.read()
print(content)

# Reading line by line


with open('sample.txt', 'r') as file:
for line in file:
print(line.strip())

# Reading all lines into a list


with open('sample.txt', 'r') as file:
lines = file.readlines()
print(lines)
Method 2: Writing to a file

# Writing to a file (overwrites existing content)


with open('output.txt', 'w') as file:
file.write("Hello, World!\n")
file.write("This is a new line\n")

# Appending to a file
with open('output.txt', 'a') as file:
file.write("This line is appended\n")

Method 3: Working with CSV files

import csv

# Writing to CSV
with open('data.csv', 'w', newline='') as file:
writer = csv.writer(file)
writer.writerow(['Name', 'Age', 'City'])
writer.writerow(['John', '30', 'New York'])
writer.writerow(['Jane', '25', 'London'])

# Reading from CSV


with open('data.csv', 'r') as file:
reader = csv.reader(file)
for row in reader:
print(row)

35. Regular Expressions

Method 1: Basic pattern matching

import re

text = "The rain in Spain falls mainly in the plain."


# Find all occurrences of 'ai'
result = re.findall('ai', text)
print(result) # Output: ['ai', 'ai', 'ai']

# Search for pattern


match = re.search('Spain', text)
if match:
print("Found at position:", match.start()) # Output: Found at position: 12

# Split at pattern
result = re.split('\s', text)
print(result) # Output: ['The', 'rain', 'in', 'Spain', 'falls', 'mainly', 'in', 'the', 'plain.']

Method 2: Pattern substitution

import re

text = "The rain in Spain falls mainly in the plain."

# Replace pattern
result = re.sub('\s', '-', text)
print(result) # Output: The-rain-in-Spain-falls-mainly-in-the-plain.

# Replace with count limit


result = re.sub('\s', '-', text, 3)
print(result) # Output: The-rain-in-Spain falls mainly in the plain.

Method 3: Advanced pattern matching

import re

text = "Emails: [email protected], [email protected], [email protected]"

# Extract email addresses


emails = re.findall(r'[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,}', text)
print(emails) # Output: ['[email protected]', '[email protected]', '[email protected]']
# Validate email format
def is_valid_email(email):
pattern = r'^[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,}$'
return bool(re.match(pattern, email))

print(is_valid_email("[email protected]")) # Output: True


print(is_valid_email("invalid.email")) # Output: False

36. Data Structures

Method 1: Stack implementation

class Stack:
def __init__(self):
self.items = []

def push(self, item):


self.items.append(item)

def pop(self):
if not self.is_empty():
return self.items.pop()
return None

def peek(self):
if not self.is_empty():
return self.items[-1]
return None

def is_empty(self):
return len(self.items) == 0

def size(self):
return len(self.items)

stack = Stack()
stack.push(1)
stack.push(2)
stack.push(3)
print(stack.pop()) # Output: 3
print(stack.peek()) # Output: 2

Method 2: Queue implementation

from collections import deque

class Queue:
def __init__(self):
self.items = deque()

def enqueue(self, item):


self.items.append(item)

def dequeue(self):
if not self.is_empty():
return self.items.popleft()
return None

def is_empty(self):
return len(self.items) == 0

def size(self):
return len(self.items)

queue = Queue()
queue.enqueue(1)
queue.enqueue(2)
queue.enqueue(3)
print(queue.dequeue()) # Output: 1
print(queue.dequeue()) # Output: 2

Method 3: Linked List implementation


class Node:
def __init__(self, data):
self.data = data
self.next = None

class LinkedList:
def __init__(self):
self.head = None

def append(self, data):


new_node = Node(data)
if not self.head:
self.head = new_node
return
current = self.head
while current.next:
current = current.next
current.next = new_node

def display(self):
current = self.head
while current:
print(current.data, end=" -> ")
current = current.next
print("None")

ll = LinkedList()
ll.append(1)
ll.append(2)
ll.append(3)
ll.display() # Output: 1 -> 2 -> 3 -> None

37. Multithreading and Multiprocessing

Method 1: Threading example

import threading
import time
def print_numbers():
for i in range(1, 6):
time.sleep(1)
print(i)

def print_letters():
for letter in 'ABCDE':
time.sleep(1.5)
print(letter)

# Create threads
t1 = threading.Thread(target=print_numbers)
t2 = threading.Thread(target=print_letters)

# Start threads
t1.start()
t2.start()

# Wait for threads to complete


t1.join()
t2.join()

print("Done!")

Method 2: Multiprocessing example

import multiprocessing
import time

def square_numbers():
for i in range(5):
time.sleep(1)
print(f"Square: {i*i}")

def cube_numbers():
for i in range(5):
time.sleep(1.5)
print(f"Cube: {i*i*i}")

# Create processes
p1 = multiprocessing.Process(target=square_numbers)
p2 = multiprocessing.Process(target=cube_numbers)

# Start processes
p1.start()
p2.start()

# Wait for processes to complete


p1.join()
p2.join()

print("Done!")

Method 3: Thread pool executor

from concurrent.futures import ThreadPoolExecutor


import time

def task(n):
time.sleep(1)
return n * n

with ThreadPoolExecutor(max_workers=3) as executor:


results = executor.map(task, [1, 2, 3, 4, 5])
for result in results:
print(result)
# Output: 1 4 9 16 25 (after delays)

38. Database Operations

Method 1: SQLite operations


import sqlite3

# Create connection and cursor


conn = sqlite3.connect('example.db')
cursor = conn.cursor()

# Create table
cursor.execute('''CREATE TABLE IF NOT EXISTS users
(id INTEGER PRIMARY KEY, name TEXT, age INTEGER)''')

# Insert data
cursor.execute("INSERT INTO users (name, age) VALUES (?, ?)", ('John', 30))
cursor.execute("INSERT INTO users (name, age) VALUES (?, ?)", ('Jane', 25))

# Commit changes
conn.commit()

# Query data
cursor.execute("SELECT * FROM users")
rows = cursor.fetchall()
for row in rows:
print(row)

# Close connection
conn.close()

Method 2: Using context manager

import sqlite3

with sqlite3.connect('example.db') as conn:


cursor = conn.cursor()
cursor.execute("SELECT * FROM users")
rows = cursor.fetchall()
for row in rows:
print(row)

Method 3: Using SQLAlchemy ORM


from sqlalchemy import create_engine, Column, Integer, String
from sqlalchemy.ext.declarative import declarative_base
from sqlalchemy.orm import sessionmaker

Base = declarative_base()

class User(Base):
__tablename__ = 'users'
id = Column(Integer, primary_key=True)
name = Column(String)
age = Column(Integer)

# Create engine and session


engine = create_engine('sqlite:///example.db')
Base.metadata.create_all(engine)
Session = sessionmaker(bind=engine)
session = Session()

# Add new user


new_user = User(name='Bob', age=35)
session.add(new_user)
session.commit()

# Query users
users = session.query(User).all()
for user in users:
print(user.id, user.name, user.age)

39. Web Scraping

Method 1: Using requests and BeautifulSoup

import requests
from bs4 import BeautifulSoup

url = 'https://example.com'
response = requests.get(url)

if response.status_code == 200:
soup = BeautifulSoup(response.content, 'html.parser')

# Extract all paragraph text


paragraphs = soup.find_all('p')
for p in paragraphs:
print(p.get_text())

# Extract all links


links = soup.find_all('a')
for link in links:
print(link.get('href'))

Method 2: Using Scrapy (framework)

# This would typically be in a separate file (spider.py)


import scrapy

class ExampleSpider(scrapy.Spider):
name = 'example'
start_urls = ['https://example.com']

def parse(self, response):


# Extract data
title = response.css('title::text').get()
paragraphs = response.css('p::text').getall()

yield {
'title': title,
'paragraphs': paragraphs
}

# Run with: scrapy runspider spider.py -o output.json

Method 3: Using Selenium for JavaScript-heavy sites


from selenium import webdriver
from selenium.webdriver.common.by import By

driver = webdriver.Chrome()
driver.get('https://example.com')

# Find elements
title = driver.find_element(By.TAG_NAME, 'h1').text
paragraphs = driver.find_elements(By.TAG_NAME, 'p')

for p in paragraphs:
print(p.text)

driver.quit()

40. API Integration

Method 1: Using requests library

import requests

# GET request
response = requests.get('https://api.github.com/users/octocat')
if response.status_code == 200:
data = response.json()
print(data['login'], data['id'])

# POST request
payload = {'key1': 'value1', 'key2': 'value2'}
response = requests.post('https://httpbin.org/post', data=payload)
print(response.json())

Method 2: Using authentication

import requests
from requests.auth import HTTPBasicAuth
response = requests.get(
'https://api.github.com/user',
auth=HTTPBasicAuth('username', 'password')
)
print(response.json())

Method 3: Using OAuth

import requests

headers = {
'Authorization': 'Bearer YOUR_ACCESS_TOKEN'
}
response = requests.get('https://api.github.com/user', headers=headers)
print(response.json())

41. Data Analysis with Pandas

Method 1: Basic DataFrame operations

import pandas as pd

# Create DataFrame
data = {
'Name': ['John', 'Jane', 'Bob', 'Alice'],
'Age': [25, 30, 35, 40],
'City': ['New York', 'London', 'Paris', 'Tokyo']
}
df = pd.DataFrame(data)

print(df)
# Output:
# Name Age City
# 0 John 25 New York
# 1 Jane 30 London
# 2 Bob 35 Paris
# 3 Alice 40 Tokyo

# Basic operations
print(df.head(2)) # First 2 rows
print(df.describe()) # Statistical summary
print(df['Age'].mean()) # Average age

Method 2: Data filtering and manipulation

import pandas as pd

# Filter data
filtered_df = df[df['Age'] > 30]
print(filtered_df)

# Add new column


df['Salary'] = [50000, 60000, 70000, 80000]
print(df)

# Group by operation
grouped = df.groupby('City')['Age'].mean()
print(grouped)

Method 3: Handling missing data

import pandas as pd
import numpy as np

# Create DataFrame with missing values


data = {
'A': [1, 2, np.nan, 4],
'B': [5, np.nan, np.nan, 8],
'C': [9, 10, 11, 12]
}
df = pd.DataFrame(data)

print(df.isnull().sum()) # Count missing values


df_filled = df.fillna(df.mean()) # Fill with mean
print(df_filled)

42. Data Visualization

Method 1: Using Matplotlib

import matplotlib.pyplot as plt


import numpy as np

# Line plot
x = np.linspace(0, 10, 100)
y = np.sin(x)
plt.plot(x, y)
plt.title('Sine Wave')
plt.xlabel('X')
plt.ylabel('Y')
plt.show()

# Bar chart
categories = ['A', 'B', 'C', 'D']
values = [10, 20, 15, 25]
plt.bar(categories, values)
plt.show()

Method 2: Using Seaborn

import seaborn as sns


import pandas as pd

# Load sample dataset


tips = sns.load_dataset('tips')

# Create visualization
sns.scatterplot(x='total_bill', y='tip', data=tips, hue='time')
plt.show()
# Box plot
sns.boxplot(x='day', y='total_bill', data=tips)
plt.show()

Method 3: Using Plotly for interactive plots

import plotly.express as px

# Create interactive scatter plot


df = px.data.iris()
fig = px.scatter(df, x='sepal_width', y='sepal_length', color='species')
fig.show()

# Create interactive line plot


x = list(range(10))
y = [i**2 for i in x]
fig = px.line(x=x, y=y, title='Quadratic Function')
fig.show()

43. Machine Learning Basics

Method 1: Linear Regression with scikit-learn

from sklearn.linear_model import LinearRegression


from sklearn.model_selection import train_test_split
from sklearn.metrics import mean_squared_error
import numpy as np

# Generate sample data


np.random.seed(0)
X = np.random.rand(100, 1)
y = 2 + 3 * X + np.random.rand(100, 1)

# Split data
X_train, X_test, y_train, y_test = train_test_split(X, y, test_size=0.2)
# Create and train model
model = LinearRegression()
model.fit(X_train, y_train)

# Make predictions
y_pred = model.predict(X_test)

# Evaluate model
mse = mean_squared_error(y_test, y_pred)
print(f"Mean Squared Error: {mse}")
print(f"Coefficients: {model.coef_}")
print(f"Intercept: {model.intercept_}")

Method 2: Classification with Decision Trees

from sklearn.tree import DecisionTreeClassifier


from sklearn.datasets import load_iris
from sklearn.model_selection import train_test_split
from sklearn.metrics import accuracy_score

# Load data
iris = load_iris()
X, y = iris.data, iris.target

# Split data
X_train, X_test, y_train, y_test = train_test_split(X, y, test_size=0.2)

# Create and train model


model = DecisionTreeClassifier()
model.fit(X_train, y_train)

# Make predictions
y_pred = model.predict(X_test)

# Evaluate model
accuracy = accuracy_score(y_test, y_pred)
print(f"Accuracy: {accuracy}")
Method 3: Clustering with K-Means

from sklearn.cluster import KMeans


from sklearn.datasets import make_blobs
import matplotlib.pyplot as plt

# Generate sample data


X, y = make_blobs(n_samples=300, centers=4, cluster_std=0.60, random_state=0)

# Apply K-Means
kmeans = KMeans(n_clusters=4)
kmeans.fit(X)
y_kmeans = kmeans.predict(X)

# Visualize clusters
plt.scatter(X[:, 0], X[:, 1], c=y_kmeans, s=50, cmap='viridis')
centers = kmeans.cluster_centers_
plt.scatter(centers[:, 0], centers[:, 1], c='red', s=200, alpha=0.75)
plt.show()

44. Natural Language Processing

Method 1: Text preprocessing

import nltk
from nltk.tokenize import word_tokenize
from nltk.corpus import stopwords
from nltk.stem import PorterStemmer
import string

# Download required NLTK data


nltk.download('punkt')
nltk.download('stopwords')

text = "This is a sample sentence, showing off the stop words filtration."
# Tokenization
tokens = word_tokenize(text.lower())
print("Tokens:", tokens)

# Remove punctuation
tokens = [word for word in tokens if word not in string.punctuation]
print("Without punctuation:", tokens)

# Remove stopwords
stop_words = set(stopwords.words('english'))
filtered_tokens = [word for word in tokens if word not in stop_words]
print("Without stopwords:", filtered_tokens)

# Stemming
stemmer = PorterStemmer()
stemmed_tokens = [stemmer.stem(word) for word in filtered_tokens]
print("Stemmed:", stemmed_tokens)

Method 2: Sentiment Analysis

from textblob import TextBlob

text = "I love this product! It's amazing."


blob = TextBlob(text)

print(f"Sentiment: {blob.sentiment}")
# Output: Sentiment: Sentiment(polarity=0.8, subjectivity=0.9)

# Correct spelling
text = "I havv goood speling!"
blob = TextBlob(text)
corrected = blob.correct()
print(f"Corrected: {corrected}")
# Output: Corrected: I have good spelling!

Method 3: TF-IDF Vectorization


from sklearn.feature_extraction.text import TfidfVectorizer

documents = [
"This is the first document.",
"This document is the second document.",
"And this is the third one.",
"Is this the first document?"
]

vectorizer = TfidfVectorizer()
X = vectorizer.fit_transform(documents)

print("Feature names:", vectorizer.get_feature_names_out())


print("TF-IDF matrix shape:", X.shape)
print("TF-IDF matrix:\n", X.toarray())

45. Deep Learning with TensorFlow/Keras

Method 1: Basic Neural Network

import tensorflow as tf
from tensorflow import keras
import numpy as np

# Load and preprocess data


(X_train, y_train), (X_test, y_test) = keras.datasets.mnist.load_data()
X_train = X_train.reshape(-1, 28*28) / 255.0
X_test = X_test.reshape(-1, 28*28) / 255.0

# Build model
model = keras.Sequential([
keras.layers.Dense(128, activation='relu'),
keras.layers.Dense(64, activation='relu'),
keras.layers.Dense(10, activation='softmax')
])

# Compile model
model.compile(optimizer='adam',
loss='sparse_categorical_crossentropy',
metrics=['accuracy'])

# Train model
history = model.fit(X_train, y_train, epochs=5, validation_split=0.2)

# Evaluate model
test_loss, test_acc = model.evaluate(X_test, y_test)
print(f"Test accuracy: {test_acc}")

Method 2: Convolutional Neural Network

import tensorflow as tf
from tensorflow import keras

# Load and preprocess data


(X_train, y_train), (X_test, y_test) = keras.datasets.cifar10.load_data()
X_train = X_train.astype('float32') / 255
X_test = X_test.astype('float32') / 255
y_train = keras.utils.to_categorical(y_train, 10)
y_test = keras.utils.to_categorical(y_test, 10)

# Build CNN model


model = keras.Sequential([
keras.layers.Conv2D(32, (3, 3), activation='relu', input_shape=(32, 32, 3)),
keras.layers.MaxPooling2D((2, 2)),
keras.layers.Conv2D(64, (3, 3), activation='relu'),
keras.layers.MaxPooling2D((2, 2)),
keras.layers.Conv2D(64, (3, 3), activation='relu'),
keras.layers.Flatten(),
keras.layers.Dense(64, activation='relu'),
keras.layers.Dense(10, activation='softmax')
])

# Compile and train


model.compile(optimizer='adam',
loss='categorical_crossentropy',
metrics=['accuracy'])
history = model.fit(X_train, y_train, epochs=10, validation_data=(X_test, y_test))

Method 3: Transfer Learning

import tensorflow as tf
from tensorflow import keras

# Load pre-trained model


base_model = keras.applications.VGG16(weights='imagenet',
include_top=False,
input_shape=(150, 150, 3))

# Freeze base model layers


base_model.trainable = False

# Add custom layers


model = keras.Sequential([
base_model,
keras.layers.GlobalAveragePooling2D(),
keras.layers.Dense(256, activation='relu'),
keras.layers.Dropout(0.5),
keras.layers.Dense(1, activation='sigmoid')
])

# Compile model
model.compile(optimizer='adam',
loss='binary_crossentropy',
metrics=['accuracy'])

print(model.summary())

46. Web Development with Flask

Method 1: Basic Flask app


from flask import Flask, render_template, request

app = Flask(__name__)

@app.route('/')
def home():
return "Hello, World!"

@app.route('/greet/<name>')
def greet(name):
return f"Hello, {name}!"

@app.route('/form', methods=['GET', 'POST'])


def form():
if request.method == 'POST':
name = request.form['name']
return f"Hello, {name}!"
return '''
<form method="post">
Name: <input type="text" name="name">
<input type="submit" value="Submit">
</form>
'''

if __name__ == '__main__':
app.run(debug=True)

Method 2: REST API with Flask

from flask import Flask, jsonify, request

app = Flask(__name__)

# Sample data
books = [
{'id': 1, 'title': 'Book 1', 'author': 'Author 1'},
{'id': 2, 'title': 'Book 2', 'author': 'Author 2'}
]
@app.route('/books', methods=['GET'])
def get_books():
return jsonify(books)

@app.route('/books/<int:book_id>', methods=['GET'])
def get_book(book_id):
book = next((b for b in books if b['id'] == book_id), None)
if book:
return jsonify(book)
return jsonify({'error': 'Book not found'}), 404

@app.route('/books', methods=['POST'])
def add_book():
new_book = {
'id': len(books) + 1,
'title': request.json['title'],
'author': request.json['author']
}
books.append(new_book)
return jsonify(new_book), 201

if __name__ == '__main__':
app.run(debug=True)

Method 3: Using Flask with templates

from flask import Flask, render_template

app = Flask(__name__)

@app.route('/')
def index():
users = [
{'name': 'John', 'age': 25},
{'name': 'Jane', 'age': 30},
{'name': 'Bob', 'age': 35}
]
return render_template('index.html', users=users)
if __name__ == '__main__':
app.run(debug=True)
Templates would be in a templates folder:

index.html:

<!DOCTYPE html>

<html>

<body>

<h1>Users</h1>

<ul>

{% for user in users %}

<li>{{ user.name }} ({{ user.age }})</li>

{% endfor %}
</ul>

</body>

</html>

text

### 47. Testing with pytest

**Method 1: Basic pytest tests**


```
# test_math_operations.py
def add(a, b):
return a + b

def subtract(a, b):


return a - b

def test_add():
assert add(2, 3) == 5
assert add(-1, 1) == 0
assert add(0, 0) == 0

def test_subtract():
assert subtract(5, 3) == 2
assert subtract(0, 5) == -5
assert subtract(10, 10) == 0

# Run with: pytest test_math_operations.py

Method 2: Parameterized tests


import pytest

def multiply(a, b):


return a * b

@pytest.mark.parametrize("a,b,expected", [
(2, 3, 6),
(0, 5, 0),
(-2, 4, -8),
(2.5, 4, 10.0)
])
def test_multiply(a, b, expected):
assert multiply(a, b) == expected

Method 3: Mocking and fixtures

import pytest
from unittest.mock import Mock

@pytest.fixture
def mock_database():
mock = Mock()
mock.get_user.return_value = {'name': 'John', 'age': 30}
return mock

def test_get_user(mock_database):
user = mock_database.get_user(1)
assert user['name'] == 'John'
assert user['age'] == 30
mock_database.get_user.assert_called_once_with(1)

48. Performance Optimization

Method 1: Using list comprehensions vs loops


import time

# Slow version with loop


start = time.time()
result = []
for i in range(1000000):
result.append(i * i)
end = time.time()
print(f"Loop time: {end - start:.4f} seconds")

# Fast version with list comprehension


start = time.time()
result = [i * i for i in range(1000000)]
end = time.time()
print(f"List comprehension time: {end - start:.4f} seconds")

Method 2: Using generators for memory efficiency

import sys

# List approach (memory intensive)


list_size = sys.getsizeof([i for i in range(1000000)])
print(f"List size: {list_size / (1024 * 1024):.2f} MB")

# Generator approach (memory efficient)


gen_size = sys.getsizeof(i for i in range(1000000))
print(f"Generator size: {gen_size / 1024:.2f} KB")

Method 3: Using caching/memoization

from functools import lru_cache


import time

# Without caching
def fibonacci(n):
if n < 2:
return n
return fibonacci(n-1) + fibonacci(n-2)
start = time.time()
result = fibonacci(35)
end = time.time()
print(f"Without cache: {end - start:.4f} seconds")

# With caching
@lru_cache(maxsize=None)
def fibonacci_cached(n):
if n < 2:
return n
return fibonacci_cached(n-1) + fibonacci_cached(n-2)

start = time.time()
result = fibonacci_cached(35)
end = time.time()
print(f"With cache: {end - start:.4f} seconds")

49. Working with Dates and Times

Method 1: Using datetime module

from datetime import datetime, date, time, timedelta

# Current date and time


now = datetime.now()
print(f"Current datetime: {now}")

# Specific date
d = date(2023, 12, 25)
print(f"Christmas: {d}")

# Time operations
tomorrow = now + timedelta(days=1)
print(f"Tomorrow: {tomorrow}")

# Formatting
formatted = now.strftime("%Y-%m-%d %H:%M:%S")
print(f"Formatted: {formatted}")

Method 2: Using timezone-aware datetimes

from datetime import datetime, timezone


import pytz

# UTC time
utc_now = datetime.now(timezone.utc)
print(f"UTC time: {utc_now}")

# Convert to different timezone


ny_tz = pytz.timezone('America/New_York')
ny_time = utc_now.astimezone(ny_tz)
print(f"New York time: {ny_time}")

# List all timezones


#print(pytz.all_timezones)

Method 3: Using dateutil for parsing

from dateutil import parser


from dateutil.relativedelta import relativedelta

# Parse various date formats


date1 = parser.parse("2023-12-25")
date2 = parser.parse("25/12/2023")
date3 = parser.parse("December 25, 2023")

print(date1, date2, date3)

# Relative dates
now = datetime.now()
next_month = now + relativedelta(months=1)
print(f"Next month: {next_month}")
# Last day of month
last_day = now + relativedelta(day=31)
print(f"Last day of month: {last_day}")

50. Advanced Features

Method 1: Metaclasses

class Meta(type):
def __new__(cls, name, bases, dct):
# Add a class attribute
dct['created_by'] = 'Meta'

# Capitalize all method names


for attr_name, attr_value in dct.items():
if callable(attr_value):
dct[attr_name.upper()] = attr_value
del dct[attr_name]

return super().__new__(cls, name, bases, dct)

class MyClass(metaclass=Meta):
def my_method(self):
return "Hello from method"

obj = MyClass()
print(obj.MY_METHOD()) # Output: Hello from method
print(MyClass.created_by) # Output: Meta

Method 2: Context Managers

# Class-based context manager


class FileManager:
def __init__(self, filename, mode):
self.filename = filename
self.mode = mode
self.file = None
def __enter__(self):
self.file = open(self.filename, self.mode)
return self.file

def __exit__(self, exc_type, exc_val, exc_tb):


if self.file:
self.file.close()

# Using the context manager


with FileManager('test.txt', 'w') as f:
f.write('Hello, World!')

# Function-based context manager using contextlib


from contextlib import contextmanager

@contextmanager
def open_file(filename, mode):
try:
f = open(filename, mode)
yield f
finally:
f.close()

with open_file('test.txt', 'r') as f:


content = f.read()
print(content)

Method 3: Descriptors

class PositiveNumber:
def __set_name__(self, owner, name):
self.name = name

def __get__(self, instance, owner):


return instance.__dict__[self.name]

def __set__(self, instance, value):


if value <= 0:
raise ValueError("Value must be positive")
instance.__dict__[self.name] = value

class Circle:
radius = PositiveNumber()

def __init__(self, radius):


self.radius = radius

@property
def area(self):
return 3.14 * self.radius ** 2

try:
c = Circle(5)
print(f"Radius: {c.radius}, Area: {c.area}")

c2 = Circle(-5) # This will raise ValueError


except ValueError as e:
print(f"Error: {e}")

Company-Specific Questions

Google Interview Questions

1. Reverse a linked list

class Node:
def __init__(self, data):
self.data = data
self.next = None

def reverse_linked_list(head):
prev = None
current = head
while current:
next_node = current.next
current.next = prev
prev = current
current = next_node
return prev

# Example usage
head = Node(1)
head.next = Node(2)
head.next.next = Node(3)
head.next.next.next = Node(4)

reversed_head = reverse_linked_list(head)
while reversed_head:
print(reversed_head.data, end=" ")
reversed_head = reversed_head.next
# Output: 4 3 2 1

2. Find the longest substring without repeating characters

def longest_substring(s):
char_set = set()
left = 0
max_length = 0

for right in range(len(s)):


while s[right] in char_set:
char_set.remove(s[left])
left += 1
char_set.add(s[right])
max_length = max(max_length, right - left + 1)

return max_length

print(longest_substring("abcabcbb")) # Output: 3
print(longest_substring("bbbbb")) # Output: 1
print(longest_substring("pwwkew")) # Output: 3
Facebook Interview Questions

1. Binary tree level order traversal

class TreeNode:
def __init__(self, val=0, left=None, right=None):
self.val = val
self.left = left
self.right = right

def level_order_traversal(root):
if not root:
return []

result = []
queue = [root]

while queue:
level_size = len(queue)
current_level = []

for _ in range(level_size):
node = queue.pop(0)
current_level.append(node.val)

if node.left:
queue.append(node.left)
if node.right:
queue.append(node.right)

result.append(current_level)

return result

# Example usage
root = TreeNode(3)
root.left = TreeNode(9)
root.right = TreeNode(20)
root.right.left = TreeNode(15)
root.right.right = TreeNode(7)

print(level_order_traversal(root)) # Output: [[3], [9, 20], [15, 7]]

2. Product of array except self

def product_except_self(nums):
n = len(nums)
result = [1] * n

# Calculate left products


left_product = 1
for i in range(n):
result[i] = left_product
left_product *= nums[i]

# Calculate right products and multiply


right_product = 1
for i in range(n-1, -1, -1):
result[i] *= right_product
right_product *= nums[i]

return result

print(product_except_self([1, 2, 3, 4])) # Output: [24, 12, 8, 6]

Amazon Interview Questions

1. Two sum problem

def two_sum(nums, target):


num_map = {}
for i, num in enumerate(nums):
complement = target - num
if complement in num_map:
return [num_map[complement], i]
num_map[num] = i
return []

print(two_sum([2, 7, 11, 15], 9)) # Output: [0, 1]

2. LRU Cache implementation

from collections import OrderedDict

class LRUCache:
def __init__(self, capacity):
self.capacity = capacity
self.cache = OrderedDict()

def get(self, key):


if key not in self.cache:
return -1
# Move the accessed key to the end
self.cache.move_to_end(key)
return self.cache[key]

def put(self, key, value):


if key in self.cache:
# Update value and move to end
self.cache[key] = value
self.cache.move_to_end(key)
else:
if len(self.cache) >= self.capacity:
# Remove the least recently used item
self.cache.popitem(last=False)
self.cache[key] = value

# Example usage
cache = LRUCache(2)
cache.put(1, 1)
cache.put(2, 2)
print(cache.get(1)) # Output: 1
cache.put(3, 3) # Evicts key 2
print(cache.get(2)) # Output: -1

Microsoft Interview Questions

1. Reverse words in a string

def reverse_words(s):
words = s.split()
return ' '.join(words[::-1])

print(reverse_words("the sky is blue")) # Output: "blue is sky the"

2. Validate binary search tree

class TreeNode:
def __init__(self, val=0, left=None, right=None):
self.val = val
self.left = left
self.right = right

def is_valid_bst(root):
def validate(node, low=-float('inf'), high=float('inf')):
if not node:
return True
if node.val <= low or node.val >= high:
return False
return (validate(node.left, low, node.val) and
validate(node.right, node.val, high))

return validate(root)

# Example usage
root = TreeNode(2)
root.left = TreeNode(1)
root.right = TreeNode(3)
print(is_valid_bst(root)) # Output: True
root2 = TreeNode(5)
root2.left = TreeNode(1)
root2.right = TreeNode(4)
root2.right.left = TreeNode(3)
root2.right.right = TreeNode(6)
print(is_valid_bst(root2)) # Output: False

Apple Interview Questions

1. Merge intervals

def merge_intervals(intervals):
if not intervals:
return []

intervals.sort(key=lambda x: x[0])
merged = [intervals[0]]

for i in range(1, len(intervals)):


current = intervals[i]
last = merged[-1]

if current[0] <= last[1]:


last[1] = max(last[1], current[1])
else:
merged.append(current)

return merged

print(merge_intervals([[1,3],[2,6],[8,10],[15,18]]))
# Output: [[1,6],[8,10],[15,18]]

2. Design a stack with getMin() in O(1) time


class MinStack:
def __init__(self):
self.stack = []
self.min_stack = []

def push(self, val):


self.stack.append(val)
if not self.min_stack or val <= self.min_stack[-1]:
self.min_stack.append(val)

def pop(self):
if self.stack:
val = self.stack.pop()
if val == self.min_stack[-1]:
self.min_stack.pop()
return val
return None

def top(self):
if self.stack:
return self.stack[-1]
return None

def getMin(self):
if self.min_stack:
return self.min_stack[-1]
return None

# Example usage
min_stack = MinStack()
min_stack.push(-2)
min_stack.push(0)
min_stack.push(-3)
print(min_stack.getMin()) # Output: -3
min_stack.pop()
print(min_stack.top()) # Output: 0
print(min_stack.getMin()) # Output: -2

You might also like