Python Cook Book
Python Cook Book
Cook Book
Minal Pandey
Introduction
Learn to cook delicious and fun recipes in
Python. codes that will help you grow in
the programming environment using this
wonderful language.
Some of the recipes you will create will be
related to: Algorithms, classes, flow
control, functions, design patterns, regular
expressions, working with databases, and
many more things.
Learning these recipes will give you a lot
of confidence when you are creating great
programs, and you will have more
understanding when reading live code.
Abstract Classes
Abstract classes serve as templates for creating concrete
classes. They define methods that must be implemented by
subclasses, ensuring a consistent interface across different
implementations. By defining common behavior and
enforcing specific methods, abstract classes promote code
reuse and maintainability. They cannot be instantiated
directly, highlighting their role as conceptual models rather
than concrete entities. Abstract classes are essential in
scenarios where multiple classes share common traits but
also require specific implementations.
Collection of similar objects
class ANamed:
name = ""
class Flower(ANamed):
pass
class City(ANamed):
pass
class Star(ANamed):
pass
rose = Flower()
rose.name = "Rose"
rome = City()
rome.name = "Rome"
sirius = Star()
sirius.name = "Sirius"
rows = [rose, rome, sirius]
names = ", ".join([r.name for r in rows])
# names is "Rose, Rome, Sirius"
Conformance checking (is, as)
from abc import ABC
class PUID(ABC):
id = 0
class Named(ABC):
name = ""
class Flower(Named):
def __init__(self, name):
self.name = name
rose = Flower("Rose")
isPUID = isinstance(rose, PUID)
isNamed = isinstance(rose, Named)
print(isPUID) # isPId is False
print(isNamed) # isNamed is True
Constructor requirements
from abc import *
class List(ABC):
@abstractmethod
def __init__(self, item_count):
self.itemCount = item_count
class SortedList(List):
def __init__(self, item_count):
super().__init__(item_count)
# implementation
print(item_count)
lst = SortedList(10)
print(lst.itemCount)
# 10
# 10
Declaration and initialization
from abc import ABC, abstractmethod
class Printable(ABC):
@abstractmethod def
print(self, color):
pass
shape = Printable() # <-error
Inheritance of abstract classes
from abc import *
class AVehicle(ABC):
@property
@abstractmethod
def max_speed(self):
pass
class ATruck(AVehicle):
@property
@abstractmethod
def capacity(self):
pass
class Kamaz5320(ATruck):
@property
def max_speed(self):
return 85
@property
def capacity(self):
return 8000
kamaz = Kamaz5320()
maxSpeed = kamaz.max_speed
# maxSpeed is 85
print(maxSpeed) # 85
Methods requirements
from abc import *
class Car(ABC):
@abstractmethod
def start_engine(self):
pass
@abstractmethod
def stop_engine(self):
pass
class SportCar(Car):
def __init__(self):
self.started = False
def start_engine(self):
if self.started:
return False
print("start engine")
self.started = True
return True
def stop_engine(self):
print("stop engine")
self.started = False
car = SportCar()
car.start_engine()
# start engine
Multiple inheritance
from abc import *
class PId(ABC):
@property
@abstractmethod
def id(self):
pass
class Priced(ABC):
@property
@abstractmethod
def price(self):
pass
class Goods(PId, Priced):
def __init__(self, p_id, p_price):
self._id = p_id
self._price = p_price
@property
def id(self):
return self._id
@property
def price(self):
return self._price
def show_id_and_price(info):
print(f"id = {info.id}, price = {info.price}")
bread = Goods(1, 5)
show_id_and_price(bread)
# printed: id = 1, price = 5
Properties requirements
from abc import *
class ACar(ABC):
@property
@abstractmethod
def engine_volume(self):
pass
@engine_volume.setter
@abstractmethod
def engine_volume(self, val):
pass
class Airwave(ACar):
def __init__(self):
self._engineVolume = 1500
@property
def engine_volume(self):
return self._engineVolume
airwave = Airwave()
print(airwave.engine_volume) # 1500
Subscript requirements
from abc import *
class AIterable(ABC):
@abstractmethod
def __getitem__(self, i):
pass
class PowerOfTwo(AIterable):
pass
def __getitem__(self, i):
return pow(2, i)
power = PowerOfTwo()
p8 = power[8]
# p8 is 256
p16 = power[16]
#p16 is 65536
print(p8)
print(p16)
Algorithms
Algorithms are step-by-step procedures or formulas for
solving problems and performing tasks. They are the
backbone of computer science, enabling efficient data
processing and decision-making. An algorithm takes input,
processes it through a series of well-defined steps, and
produces an output. They can range from simple arithmetic
operations to complex data structures and sorting
techniques. Effective algorithms are characterized by their
efficiency, scalability, and clarity. Understanding and
designing algorithms are crucial for optimizing performance
and resource utilization in software development.
Sorting algorithms:
Bubble Sort
def bubble_sort(arr):
items = arr[:]
for i in range(len(items)):
for j in range(i + 1, len(items)):
if items[j] < items[i]:
items[j], items[i] = items[i], items[j]
return items
items = [4, 1, 5, 3, 2]
sort_items = bubble_sort(items)
print("Sorted items:", sort_items)
# Sorted items: [1, 2, 3, 4, 5]
Counting Sort
def counting_sort(arr):
maximum = max(arr)
counts = [0] * (maximum + 1)
items = [0] * len(arr)
for x in arr:
counts[x] += 1
total = 0
for i in range(len(counts)):
old_count = counts[i]
counts[i] = total
total += old_count
for x in arr:
items[counts[x]] = x
counts[x] += 1
return items
items = [4, 1, 5, 3, 2]
sort_items = counting_sort(items)
print("Sorted items:", sort_items)
# Sorted items: [1, 2, 3, 4, 5]
Merge Sort
def merge_sort(items):
if len(items) <= 1:
return items
middle = len(items) // 2
left = items[:middle]
right = items[middle:]
def merge(left, right):
result = []
left_index = 0
right_index = 0
while left_index < len(left) and right_index <
len(right):
if left[left_index] < right[right_index]:
result.append(left[left_index])
left_index += 1
else:
result.append(right[right_index])
right_index += 1
result.extend(left[left_index:])
result.extend(right[right_index:])
return result
return merge(merge_sort(left), merge_sort(right))
items = [4, 1, 5, 3, 2]
sort_items = merge_sort(items)
print("Sorted items:", sort_items)
# Sorted items: [1, 2, 3, 4, 5]
Quick Sort
def quick_sort(items):
def do_sort(items, fst, lst):
if fst >= lst:
return
i = fst j = lst x =
items[(fst + lst) // 2]
while i <= j:
while items[i] < x:
i += 1
while items[j] > x:
j -= 1
if i <= j:
items[i], items[j] = items[j], items[i]
i += 1
j -= 1
do_sort(items, fst, j)
do_sort(items, i, lst)
sorted_items = items[:]
do_sort(sorted_items, 0, len(sorted_items) - 1)
return sorted_items
items = [4, 1, 5, 3, 2]
sort_items = quick_sort(items)
print("Sorted items:", sort_items)
# Sorted items: [1, 2, 3, 4, 5]
Radix Sort
def list_to_buckets(items, c_base, i):
buckets = [[] for _ in range(c_base)]
p_base = c_base ** i
for x in items:
digit = (x // p_base) % c_base
buckets[digit].append(x)
return buckets
def buckets_to_list(buckets):
result = []
for bucket in buckets:
result.extend(bucket)
return result
def radix_sort(arr, c_base=10):
max_val = max(arr)
i=0
while c_base ** i <= max_val:
arr = buckets_to_list(list_to_buckets(arr, c_base, i))
i += 1
return arr
items = [4, 1, 5, 3, 2]
sort_items = radix_sort(items)
print("Sorted items:", sort_items)
# Sorted items: [1, 2, 3, 4, 5]
Searching algorithms:
Binary Search
def binary_search(arr, x):
i = -1
j = len(arr)
while i + 1 != j:
m = (i + j) // 2
if x == arr[m]:
return m
if x < arr[m]:
j=m
else:
i=m
return None
items = [2, 3, 5, 7, 11, 13, 17]
print(binary_search(items, 1))
# Will print None
print(binary_search(items, 7))
# Will print 3
print(binary_search(items, 19))
# Will print None
Fast Linear Search
def fast_linear_search(arr, x):
i=0
count = len(arr)
arr.append(x)
while True:
if arr[i] == x:
arr.pop() # Remove the last element
return i if i < count else None
i += 1
items = [2, 3, 5, 7, 11, 13, 17]
print(fast_linear_search(items, 1))
# Will print None
print(fast_linear_search(items, 7))
# Will print 3
print(fast_linear_search(items, 19))
# Will print None
Interpolation Search
def interpolation_search(arr, x):
low = 0
high = len(arr) - 1
while low <= high and x >= arr[low] and x <= arr[high]:
mid = low + ((x - arr[low]) * (high - low)) // (arr[high] -
arr[low])
if arr[mid] < x:
low = mid + 1
elif arr[mid] > x:
high = mid - 1
else:
return mid
if arr[low] == x:
return low
if arr[high] == x:
return high
return None
items = [2, 3, 5, 7, 11, 13, 17]
print(interpolation_search(items, 1))
# Will print None
print(interpolation_search(items, 7))
# Will print 3
print(interpolation_search(items, 19))
# Will print None
Linear Search
def linear_search(arr, x):
i=0
count = len(arr)
while i < count:
if arr[i] == x:
return i
i += 1
return None
items = [2, 3, 5, 7, 11, 13, 17]
print(linear_search(items, 1)) # Will print None
print(linear_search(items, 7)) # Will print 3
print(linear_search(items, 19)) # Will print None
Changes in new versions
In software development, new versions of a program or
system often bring various changes that can include bug
fixes, performance improvements, and new features. These
updates are crucial for maintaining security, improving user
experience, and staying competitive.
Alias type syntax
# *** in version 3.10: ***
from typing import TypeAlias
Index: TypeAlias = int
# *** before: ***
Width = int
Comparison operators
# *** before: ***
b1 = 1 < "A"
# b1 is True
b2 = 1 == "A"
# b2 is False
# *** in version 3: ***
b1 = 1 < "A" # <- TypeError
b2 = 1 == "A"
# b2 is False
Context Variables
# *** in version 3.7 ***
import contextvars
number = contextvars.ContextVar("number", default="-1")
contexts = list()
def print_number():
print(f"number: {number.get()}")
print_number()
# number: -1
# Creating contexts and setting the number
for n in [1, 2, 3]:
ctx = contextvars.copy_context()
ctx.run(number.set, n)
contexts.append(ctx)
# Running print_number () function in each context
for ctx in reversed(contexts):
ctx.run(print_number)
def http_status_code_message(status_code):
match status_code:
case 200:
return "OK"
case 404:
return "Not Found"
case 500:
return "Internal Server Error"
case _:
return "Unknown Status Code"
print(http_status_code_message(200)) # OK
print(http_status_code_message(404)) # Not Found
print(http_status_code_message(123)) # Unknown Status
Code
print function
# Python 2 example
print "Hello, World!" # Hello, World!
print "The answer is", 42 # The answer is 42
# Using a trailing comma to avoid a newline at the end
print "Hello,",
print "World!" # Hello, World!
# Python 3 example
print("Hello, World!") # Hello, World!
print("The answer is", 42) # The answer is 42
# To avoid a newline at the end, use the end parameter
print("Hello,", end=" ")
print("World!") # Hello, World!
range function
# Python 2 example using range
numbers = range(1, 10)
print numbers # [1, 2, 3, 4, 5, 6, 7, 8, 9]
# Python 2 example using xrange
numbers = xrange(1, 10)
print numbers # xrange(1, 10)
print list(numbers) # [1, 2, 3, 4, 5, 6, 7, 8, 9]
# Python 3 example using range
numbers = range(1, 10)
print(numbers) # range(1, 10)
print(list(numbers)) # [1, 2, 3, 4, 5, 6, 7, 8, 9]
Classes
In object-oriented programming, classes are fundamental
building blocks that define the blueprint for objects. A class
encapsulates data for the object and methods to manipulate
that data, promoting modularity and code reuse.
Check for reference equality
class MyClass:
def __init__(self, value):
self.value = value
# Create two instances of MyClass
obj1 = MyClass(10) obj2 =
MyClass(10) obj3 = obj1
Default constructor
class Book:
def __init__(self, title="Unknown Title", author="Unknown
Author", year=0):
self.title = title
self.author = author
self.year = year
def display_info(self):
print(f'Title: {self.title}, Author: {self.author}, Year:
{self.year}')
# Create an instance using the default constructor
default_book = Book()
default_book.display_info()
# Output: Title: Unknown Title, Author: Unknown Author,
Year: 0
# Create an instance with custom values
custom_book = Book("1984", "George Orwell", 1949)
custom_book.display_info()
# Output: Title: 1984, Author: George Orwell, Year: 1949
Optional parameter values
class Car:
def __init__(self, make="Unknown Make",
model="Unknown Model", year=0, color="Unknown Color"):
self.make = make
self.model = model
self.year = year
self.color = color
def display_info(self):
print(f'Make: {self.make}, Model: {self.model}, Year:
{self.year}, Color: {self.color}')
# Create an instance using the default constructor (all
default values)
default_car = Car()
default_car.display_info() # Output: Make: Unknown Make,
Model: Unknown Model, Year: 0, Color: Unknown Color
# Create an instance with some custom values
custom_car1 = Car(make="Toyota", model="Corolla")
custom_car1.display_info()
# Output: Make: Toyota, Model: Corolla, Year: 0, Color:
Unknown Color
# Create an instance with all custom values
custom_car2 = Car(make="Honda", model="Civic",
year=2022, color="Red")
custom_car2.display_info()
# Output: Make: Honda, Model: Civic, Year: 2022, Color: Red
Replacement of the parent
constructor
class Person:
def __init__(self, first_name, last_name, age):
self.first_name = first_name
self.last_name = last_name
self.age = age
def display_person_info(self):
print(f'Name: {self.first_name} {self.last_name}, Age:
{self.age}')
class Employee(Person):
def __init__(self, first_name, last_name, age,
employee_id, position):
# Call the parent constructor to initialize first_name,
last_name, and age
super().__init__(first_name, last_name, age)
# Initialize the additional attributes
self.employee_id = employee_id
self.position = position
def display_employee_info(self):
# Call the parent class method to display basic info
super().display_person_info()
print(f'Employee ID: {self.employee_id}, Position:
{self.position}')
# Create an instance of Person
person = Person("John", "Doe", 45)
person.display_person_info()
# Output: Name: John Doe, Age: 45
# Create an instance of Employee
employee = Employee("Jane", "Smith", 30, "E123",
"Software Engineer")
employee.display_employee_info()
# Output:
# Name: Jane Smith, Age: 30
# Employee ID: E123, Position: Software Engineer
With paramenters
class Rectangle:
def __init__(self, length, width):
self.length = length
self.width = width
def area(self):
return self.length * self.width
# Creating an instance of Rectangle with specific
dimensions
rectangle1 = Rectangle(5, 3)
print("Area of rectangle1:", rectangle1.area())
# Output: Area of rectangle1: 15
# Creating another instance of Rectangle with different
dimensions
rectangle2 = Rectangle(7, 4)
print("Area of rectangle2:", rectangle2.area())
# Output: Area of rectangle2: 28
Without any paramenters
class MyClass:
def __init__(self):
print("This is the default constructor.")
def display(self):
print("Inside MyClass.")
# Creating an instance of MyClass
obj = MyClass()
obj.display()
Create a copy of the object
import copy
class Person:
def __init__(self, name, age):
self.name = name
self.age = age
def display_info(self):
print(f'Name: {self.name}, Age: {self.age}')
# Create an instance of Person
person1 = Person("Alice", 30)
person1.display_info()
# Output: Name: Alice, Age: 30
# Create a shallow copy of person1
person2 = copy.copy(person1)
person2.display_info()
# Output: Name: Alice, Age: 30
# Modify the copy
person2.name = "Bob"
person2.display_info()
# Output: Name: Bob, Age: 30
person1.display_info()
# Output: Name: Alice, Age: 30
# Create a deep copy of person1
person3 = copy.deepcopy(person1)
person3.display_info()
# Output: Name: Alice, Age: 30
Definition and initialization
# Definition
class SomeClass:
pass
# Initialization
someClass = SomeClass()
Descriptors
class AgeDescriptor:
def __init__(self):
self._age = None
def __get__(self, instance, owner):
print("Getting age")
return self._age
def __set__(self, instance, value):
if not isinstance(value, int):
raise ValueError("Age must be an integer")
if value < 0:
raise ValueError("Age cannot be negative")
print("Setting age")
self._age = value
def __delete__(self, instance):
print("Deleting age")
self._age = None
class Person:
age = AgeDescriptor()
def __init__(self, name, age):
self.name = name
self.age = age
def display_info(self):
print(f'Name: {self.name}, Age: {self.age}')
# Create an instance of Person
person = Person("Alice", 30)
person.display_info()
# Output: Name: Alice, Age: 30
# Get the age
print(person.age)
# Output: Getting age, 30
# Set a new age
person.age = 35
# Output: Setting age
# Get the updated age
print(person.age)
# Output: Getting age, 35
# Delete the age
del person.age # Output: Deleting age
# Try to get the deleted age
print(person.age)
# Output: Getting age, None
class Circle:
def __init__(self):
self.radius = 0
@property
def area(self):
return math.pi * pow(self.radius, 2)
circle = Circle()
circle.radius = 2
# circle.area is 12.566370614359172
print(circle.area)
Read-Only properties: Stored
properties
class FilmList:
def __init__(self):
self.__count = 10
@property
def count(self):
return self.__count
filmList = FilmList()
count = filmList.count
print(count) # count is 10
Stored properties
class Person:
def __init__(self, name, age):
self.name = name
self.age = age
# Creating an instance of Person
person = Person("Alice", 30)
# Accessing stored properties
print("Name:", person.name) # Output: Name: Alice
print("Age:", person.age) # Output: Age: 30
# Modifying stored properties
person.name = "Bob"
person.age = 25
# Displaying modified properties
print("Modified Name:", person.name)
# Output: Modified Name: Bob
print("Modified Age:", person.age)
# Output: Modified Age: 25
Type properties
class Circle:
pi = 3.14159
def __init__(self, radius):
self.radius = radius
def calculate_area(self):
return Circle.pi * self.radius ** 2
# Creating instances of Circle
circle1 = Circle(5)
circle2 = Circle(10)
# Accessing the type property
print("Value of pi:", Circle.pi) # Output: Value of pi: 3.14159
# Calculating areas using type property
print("Area of circle 1:", circle1.calculate_area())
# Output: Area of circle 1: 78.53975
print("Area of circle 2:", circle2.calculate_area())
# Output: Area of circle 2: 314.159
Subscripts (indexer methods):
With generic parameter
class MyList:
def __init__(self):
self.data = {}
def __getitem__(self, index):
return self.data[index]
def __setitem__(self, index, value):
self.data[index] = value
# Creating an instance of MyList
my_list = MyList()
# Using integer indices my_list[0] = 'a'
my_list[1] = 'b' print("Element at index
0:", my_list[0]) # Output: Element at
index 0: a print("Element at index 1:",
my_list[1]) # Output: Element at index
1: b
class Size(Enum):
xs, s, m, l, xl = range(5)
small = Size.s
large = Size.l
print("is l > s:", large.value > small.value)
# is l > s: True
Explicitly set base value
from enum import Enum
class Season(Enum):
Summer = 1
Fall = 2
Winter = 3
Spring = 4
winter = Season.Winter
baseWinter = winter.value
print(baseWinter) # 3
Get the list of values
from enum import Enum
class Season(Enum):
Summer, Fall, Winter, Spring = range(4)
values = list(Season)
print(values)
print(values[0])
# [<Season.Summer: 0>, <Season.Fall: 1>,
<Season.Winter: 2>, <Season.Spring: 3>]
# Season.Summer
Initializing from a base value
from enum import Enum
class Season(Enum):
Summer = 0
Fall = 1
Winter = 2
Spring = 3
winter = Season(2)
# winter is Season.Winter
print(winter) # Season.Winter
Exceptions Handling
Exceptions handling is a programming technique used to
manage unexpected or erroneous situations that may occur
during runtime. When a program encounters an exceptional
condition (e.g., division by zero, file not found), it throws an
exception, which disrupts the normal flow of execution.
Catch all exceptions
class IsNoneException(Exception):
pass
class IsEmptyException(Exception):
pass
def throw_when_null_or_empty(data):
if data is None:
raise IsNoneException()
if len(data) == 0:
raise IsEmptyException()
try:
throw_when_null_or_empty(None)
except Exception as e:
print("Error happened " + e.__class__.__name__)
# Error happened IsNoneException
Catch the specific exception
class IsNoneException(Exception):
pass
class IsEmptyException(Exception):
pass
def throw_when_null_or_empty(data):
if data is None:
raise IsNoneException()
if len(data) == 0:
raise IsEmptyException()
try:
throw_when_null_or_empty([])
except IsNoneException:
print("list is not specified")
except IsEmptyException:
print("list is empty")
# list is empty
Define an exception type
class SimpleException(Exception):
pass
raise SimpleException("Oops!")
Guaranteed code execution
def throw_if_true(param):
try:
if param:
raise OSError("test exception")
except OSError:
print("except")
finally:
print("finally")
throw_if_true(True)
# printed: "except" and "finally"
throw_if_true(False)
# printed only "finally"
If no exception occurred
def throw_if_true(param):
try:
if param:
raise OSError("test exception")
except OSError:
print("except")
else:
print("else")
throw_if_true(True)
# printed: "except"
throw_if_true(False)
# printed only "else"
Method throwing an exception
# any method can throw an error
def method_with_exception():
raise Exception("test exception")
method_with_exception()
# Exception: test exception
Re-throw exceptions
def method_with_exception():
try:
raise Exception("test exception")
except Exception as ex:
# implementation of any partial procesing
# and send error to the calling code
raise ex
try:
method_with_exception()
except Exception as e:
print(e.args[0])
# test exception
Throw an exception
class Seller:
def __init__(self):
self.cars = []
def sell(self):
if len(self.cars) == 0:
raise Exception("No cars for sale")
seller = Seller()
try:
seller.sell()
except Exception as e:
print(e.args[0])
# e.args[0] is "No cars for sale"
Extensions
Extensions in programming languages allow developers to
enhance existing types or classes without modifying their
source code. They provide a way to add new functionality,
methods, or properties to types that are already defined.
Adding object methods
from math import *
excluded_methods = frozenset(["__module__",
"__qualname__"])
def class_extend(cls):
class Meta(type):
def __new__(mcs, name, bases, attrs):
for name, value in attrs.items():
if name not in excluded_methods:
setattr(cls, name, value)
return cls
return Meta
class Point:
def __init__(self, x, y):
self.x = x
self.y = y
class Point(metaclass=class_extend(Point)):
def distance_to(self, p2):
d1 = pow(self.x - p2.x, 2)
d2 = pow(self.y - p2.y, 2)
return sqrt(d1 + d2)
point1 = Point(1, 2) point2 = Point(2,
3) distance =
point1.distance_to(point2)
print(f"{distance = }")
# distance = 1.4142135623730951
Functions
Functions in programming are blocks of reusable code that
perform a specific task. They allow developers to
encapsulate logic, promote code reusability, and enhance
readability by breaking down complex operations into
smaller, manageable parts.
Array of parameters
def get_avg(*values):
if len(values) == 0:
return 0
sum_v = 0
for value in values:
sum_v += value
return sum_v / len(values)
avg = get_avg(1, 2, 3, 4)
print(f"{avg = }") # avg is 2.5
In/Out parameters
def swap_strings(s1, s2):
tmp = s1[0]
s1[0] = s2[0]
s2[0] = tmp
s1 = ["A"] s2 = ["B"]
swap_strings(s1, s2)
class Size(Generic[T]):
def __init__(self, width: T, height: T):
self.width = width
self.height = height
def as_text(self):
return f"[{self.width}; {self.height}]"
size_int = Size[int](5, 8)
text_int = size_int.as_text()
# text_int is "[5; 8]"
print(f"{text_int=}")
print(f"{text_float=}")
Generic collections
# List of integer
int_list = list[int]()
int_list.append(5)
print(f"{int_list = }")
# Dictionary
dic = dict[int, str]()
dic[1] = "one"
print(f"{dic = }")
# Set
set_float = set[float]()
set_float.add(3.14)
print(f"{set_float = }")
# nt_list = [5]
# dic = {1: 'one'}
# set_float = {3.14}
Generic methods
from typing import TypeVar
T = TypeVar('T')
def swap(v1: list[T], v2: list[T]):
v1[0], v2[0] = v2[0], v1[0]
n1 = [5] n2 = [7]
swap(n1, n2) # n1[0]
is 7, n2[0] is 5
s1 = ["cat"]
s2 = ["dog"]
swap(s1, s2)
# s1[0] is "B", s2[0] is "A"
print(f'{n1 = }, {n2 = }')
print(f'{s1 = }, {s2 = }')
Interface conformity
from abc import ABC, abstractmethod
from typing import TypeVar, Generic
class Vehicle(ABC):
@abstractmethod
def test(self):
pass
class Car(Vehicle):
def test(self):
print(f"test {self}")
T = TypeVar('T', bound=Vehicle)
class Service(Generic[T]):
def __init__(self):
self.v_list = list[T]()
def add(self, item: T):
self.v_list.append(item)
def test(self):
for item in self.v_list:
item.test()
service = Service[Car]()
service.add(Car())
service.test()
Substitution principle
class Vehicle:
def test(self):
print(f"test {self}")
class Car(Vehicle):
pass
class Truck(Vehicle):
pass
lst = list[Vehicle]()
lst.append(Vehicle())
lst.append(Car())
lst.append(Truck())
for vehicle in lst:
vehicle.test()
Initializing of Types
Initializing types refers to the process of setting initial
values or states for variables, objects, or data structures in
a program. This process ensures that entities in the program
start with predefined values, which are often crucial for
correct functioning and behavior.
Classes:
With a constructor
class Phone:
def __init__(self, model):
self.model = model
class Employee:
def __init__(self, first_name, last_name, phone):
self.first_name = first_name
self.last_name = last_name
self.phone = phone
# Create instances
nokia_phone = Phone("Nokia 6610")
kim = Employee("Victorya", "Kim", Phone("IPhone 11 Pro"))
# Access and print phone model
print(kim.phone.model) # Iphone 11 Pro
Without any constructor
class Phone:
pass # No explicit constructor needed
class Employee:
pass # No explicit constructor needed
kim = Employee()
kim.firstName = "Victorya"
kim.lastName = "Kim"
kim.phone = Phone()
kim.phone.model = "IPhone 5"
# list of Employee
class Employee:
def __init__(self, first_name, last_name):
self.firstName = first_name
self.lastName = last_name
def __iter__(self):
return self
def __next__(self):
if self.current > self.high
raise StopIteration
else:
result = self.current
self.current += self.step
return result