# copying with the copy library
import copy
first_str = "Will"
second_str = copy.copy(first_str)
print(first_str)
print(second_str)Will
Will
Felix
October 25, 2021
# copying with the copy library
import copy
first_str = "Will"
second_str = copy.copy(first_str)
print(first_str)
print(second_str)Will
Will
{False: 'Daniel', 'Aria': [1, 2, 3], 'Jacob': True}
def some_fn():
global country # interpret the global variable
country = "Bangladesh"
print(country)
some_fn()
print(country) # global variable has changedBangladesh
Bangladesh
# passing by reference, because not reassigned and complex data type
fruits_list = ["Apple", "Grapes", "Mango", "Bananas"]
def change_list(fruits_list):
fruits_list[0] = "Kiwi"
fruits_list = ["Kiwi"] # does nothing on the outside
print("Inside the function: ", fruits_list)
change_list(fruits_list)
print()
print("Outside the function: ", fruits_list)Inside the function: ['Kiwi']
Outside the function: ['Kiwi', 'Grapes', 'Mango', 'Bananas']
<filter at 0x2220e96dd30>
# Generators - create a sequence that can be iterated over
def generator():
print("One!")
yield 1 # control execution flow
print("Two!")
yield 2
print("Three!")
yield 3
g = generator() # no code execution
g<generator object generator at 0x0000013378275970>
import random
def print_message():
print("Yohoo! Decorators are cool!")
def make_highlighted(func):
annotations = ["-", "*", "+"]
annotate = random.choice(annotations)
print(annotate * 50)
func()
print(annotate * 50)
make_highlighted(print_message)
@make_highlighted # that'S how they are really used
def print_a_message():
print("Now you'll see how decorators are used")
print_a_message--------------------------------------------------
Yohoo! Decorators are cool!
--------------------------------------------------
--------------------------------------------------
Now you'll see how decorators are used
--------------------------------------------------
def plus_highlight(func):
def highlight():
print("+"*50)
func()
print("+"*50)
return highlight
def asterisk_highlight(func):
def highlight():
print("*"*50)
func()
print("*"*50)
return highlight
@plus_highlight
@asterisk_highlight
def hello():
print("hello!")
hello()++++++++++++++++++++++++++++++++++++++++++++++++++
**************************************************
hello!
**************************************************
++++++++++++++++++++++++++++++++++++++++++++++++++
['United States', 'Great Britain', 'China']
class Node:
def __init__(self, dataval=None, nextval=None):
self.dataval = dataval
self.nextval = nextval
def __repr__(self):
return repr(self.dataval)
class LinkedList:
def __init__(self):
self.head = None
def __repr__(self): # O(N)
nodes = []
curr = self.head
while curr:
nodes.append(repr(curr))
curr = curr.nextval
return "[" + "->".join(nodes) + "]"
def prepend(self, dataval): # O(1)
self.head = Node(dataval=dataval, nextval = self.head)
def append(self, dataval):
if not self.head:
self.head = Node(dataval=dataval)
return
curr = self.head
while curr.nextval:
curr = curr.nextval
curr.nextval = Node(dataval=dataval)
def add_after(self, middle_dataval, dataval):
if middle_dataval is None:
print("Data to insert after not specified")
return
curr = self.head
while curr and curr.dataval != middle_dataval:
curr = curr.nextval
new_node = Node(dataval = dataval)
new_node.nextval = curr.nextval
curr.nextval = new_node
def find(self, data):
curr = self.head
while curr and curr.dataval != data:
curr = curr.nextval
return curr
def remove(self, data):
curr = self.head
prev = None
while curr and curr.dataval != data:
prev = curr
curr = curr.nextval
if prev is None:
self.head = curr.nextval
elif curr:
prev.nextval = curr.nextval
curr.nextval = None
def reverse(self):
curr = self.head
prev_node = None
next_node = None
while curr:
nextval = curr.nextval
curr.nextval = prev_node
prev_node = curr
curr = nextval
self.head = prev_node# instance and class variables are public by default
# hack for private attributes:
class Dog:
def __init__(self, name, breed):
self.__name = name
self.__breed = breed
def print_details(self):
print('My name is %s and I am a %s' % (self.__name, self.__breed))
d1 = Dog("Moje", "Golden Retriever")
d1.print_details()My name is Moje and I am a Golden Retriever
class Competition:
def __init__(self, name, country, prize):
self.__name = name
self.__country = country
self.__prize = prize
def get_name_country(self):
return '{} {}'.format(self.__name, self.__country, self.__prize)
def __repr__(self): # representation for print function
return "Competition: {} held in {}, prize: {}".format(self.__name, self.__country,
self.__prize)
def __str__(self):
return "'{} - {}'".format(self.get_name_country(), self.__prize)
archery = Competition("Archery", "Germany", 8000)
repr(archery)'Competition: Archery held in Germany, prize: 8000'
# Properties with Decorators
class Wrestler:
def __init__(self, name):
self.__name = name
@property # this is the method for accessing
def name(self):
print("getter method called")
return self.__name
@name.setter # this is the method for setting new vals
def name(self, value):
print("setter method called")
self.__name = value
@name.deleter
def name(self):
del self.__name
w = Wrestler("Kart")class Competition:
__raise_amount = 1.04 # class variable
def __init__(self, name, country, prize):
self.__name = name
self.__country = country
self.__prize = prize
def raise_prize(self):
self.__prize = self.__prize * self.__raise_amount
def get_name_country(self):
return '{} {}'.format(self.__name, self.__country, self.__prize)
def __repr__(self): # representation for print function
return "Competition: {} held in {}, prize: {}".format(self.__name, self.__country,
self.__prize)
def __str__(self):
return "'{} - {}'".format(self.get_name_country(), self.__prize)
@classmethod
def get_raise_amount(cls):
return cls.__raise_amount
@classmethod
def set_raise_amount(cls, amount):
cls.__raise_amount = amountclass Shape:
def __init__(self, shape_type, color="Red"): # optional
self.__type = shape_type
self.__color = color
def get_type(self):
return self.__type
def get_color(self):
return self.__color
def get_area(self):
pass
def get_perimeter(self):
pass
class Circle(Shape):
pass
circle = Circle("circle")
type(circle)__main__.Circle
class Square(Shape):
def __init__(self):
Shape.__init__(self, "square")
square = Square()
square.get_type()'square'
# Multiple and Multilevel Inheritance
class Father:
def height(self):
print("I have inherited my height from my father")
class Mother:
def intelligence(self):
print("I have inherited my intelligence from my mother")
class Child(Father, Mother):
def experience(self):
print("My experience are all my own")
c = Child()
c.height()I have inherited my height from my father
class Logger:
__instance = None
def __init__(self):
raise RuntimeError('Call get_instance() instead')
@classmethod
def get_instance(cls):
if cls.__instance is None :
print('No instance exists, creating a new one')
cls.__instance = cls.__new__(cls)
else:
print('A previously created instance exists, returning that same one')
return cls.__instance
logger1 = Logger.get_instance()
logger1Pythonic implementation - new is always called first -> it creates the instance - cls is reference to not yet existing instance - init is used to initialize the existing instance -> self is a reference to the instance
class PythonicLogger:
__instance = None
def __init__(self):
print('Object initialized')
# put your custom code here
# is called every time - could be expensive
def __new__(cls):
if cls.__instance is None:
print('No instance exists, creating a new one')
cls.__instance = super(PythonicLogger, cls).__new__(cls)
else:
print('A previously created instance exists, returning that same one')
return cls.__instanceclass SuperLogger:
__instance = None
def __new__(cls):
if cls.__instance is None:
print('No instance exists, creating a new one')
cls.__instance = super(SuperLogger, cls).__new__(cls)
# Place all initialization code here
else:
print('A previously created instance exists, returning that same one')
return cls.__instanceclass MacBookAir(Product):
def __init__(self, memory, os):
Product.__init__(self, 'MacBookAir', 1031)
self.__memory = memory
self.__os = os
class AppleIPad(Product):
def __init__(self, generation):
Product.__init__(self, 'AppleIPad', 529)
self.__generation = generation
class AppleIWatch(Product):
def __init__(self):
Product.__init__(self, 'AppleIWatch', 264)class ProductFactory:
@staticmethod
def create(item_name, *args):
if item_name == 'MacBookAir':
return MacBookAir(*args)
elif item_name == 'AppleIPad':
return AppleIPad(*args)
elif item_name == 'AppleIWatch':
return AppleIWatch(*args)
air = ProductFactory.create('MacBookAir', '16GB', 'Sierra')
ipad = ProductFactory.create('AppleIPad', '2nd')
iwatch = ProductFactory.create('AppleIWatch')
iwatch<__main__.AppleIWatch at 0x193517d2610>
Abstract Factory
An abstract factory is a factory that returns factories. A normal factory can be used to create sets of related objects. An abstract factory returns factories. Thus, an abstract factory is used to return factories that can be used to create sets of related objects.
class Car(Toy):
def show(self):
print("Remote controlled car")
class ActionFigure(Toy):
def show(self):
print("Captain America action figure")
class ConstructionToy(Toy):
def show(self):
print("Lego")
class Red(Color):
def show_color(self):
print("red")
class Green(Color):
def show_color(self):
print("green")
class Blue(Color):
def show_color(self):
print("blue")
car = Car()
red = Red()
red.show_color(), car.show()red
Remote controlled car
(None, None)
# Create concrete classes implementing the same interface.
# create Factory classes extending AbstractFactory
class ColorfulToysFactory(AbstractFactory):
def get_toy(self, toy_type):
if toy_type == None:
return None
if toy_type == "car":
return Car()
elif toy_type == "action figure":
return ActionFigure()
elif toy_type == "construction toy":
return ConstructionToy()
return None
def get_color(self, color_type):
if color_type == None:
return None
if color_type == "red":
return Red()
elif color_type == "green":
return Green()
elif color_type == "blue":
return Blue()
return None# Use the FactoryProducer to get AbstractFactory in order to get factories of
# concrete classes by passing an information such as type
RED_CAR = 'red_car'
BLUE_LEGO = 'blue_lego'
GREEN_ACTION_FIGURE = 'green_action_figure'
class ColorfulToysProducer:
__colorful_toys_factory = ColorfulToysFactory()
@classmethod
def get_toy_and_color(cls, choice):
toy = None
color = None
if choice == RED_CAR:
toy = cls.__colorful_toys_factory.get_toy('car')
color = cls.__colorful_toys_factory.get_color('red')
elif choice == BLUE_LEGO:
toy = cls.__colorful_toys_factory.get_toy('construction toy')
color = cls.__colorful_toys_factory.get_color('blue')
elif choice == GREEN_ACTION_FIGURE:
toy = cls.__colorful_toys_factory.get_toy('action figure')
color = cls.__colorful_toys_factory.get_color('green')
return toy, color
toy, color = ColorfulToysProducer.get_toy_and_color(RED_CAR)
toy, color(<__main__.Car at 0x193517f9280>, <__main__.Red at 0x193517f9dc0>)
Builder Pattern - Separate the construction of an object from representation - allow same construction process for many representations - parse a complex representation, create different objects - Consider a SQL query builder class - allows step-by-step creation of a SQL query - Query is a complex entity with many different parts - Applications might build once, run multiple times - Separates object construction from its representation - parse a complex construction process into simple constituent operations
class Mobile:
def __init__(self,
name,
weight,
screen_size,
ram,
os,
camera_mp,
battery):
self.name = name
self.weight = weight
self.screen_size = screen_size
self.ram = ram
self.os = os
self.camera_mp = camera_mp
self.battery = battery
def show(self):
print("name:", self.name)
print("weight:", self.weight)
print("screen_size:", self.screen_size)
print("ram:", self.ram)
print("os:", self.os)
print("camera_mp:", self.camera_mp)
print("battery:", self.battery)name: Samsung S10
weight: 157g
screen_size: 6.1 inch
ram: 8GB
os: android 9.0
camera_mp: 12 megapixel
battery: 3400 mAh
class Mobile():
def __init__(self):
self.name = None
self.weight = None
self.screen_size = None
self.ram = None
self.os = None
self.camera_mp = None
self.battery = None
def show(self):
print("name:", self.name)
print("weight:", self.weight)
print("screen_size:", self.screen_size)
print("ram:", self.ram)
print("os:", self.os)
print("camera_mp:", self.camera_mp)
print("battery:", self.battery)
s10 = Mobile()
s10.name = "Samsung S10"
s10.screen_size = "6.1 inch",
s10.os = "android 9.0",
s10.camera_mp = "12 megapixel",
s10.battery = "3400 mAh"
s10.show()# now the features have been encapsulated in a separate class called MyMobile
# the build method instantiates a new mobile object and encapsulates setting
# of attributes
class MyMobileBuilder():
def __init__(self):
self.__mobile = Mobile()
def get_mobile(self):
return self.__mobile
def build_name(self, name):
self.__mobile.name = name
def build_memory(self, ram):
self.__mobile.ram = ram
def build_camera(self, camera_mp):
self.__mobile.camera_mp = camera_mp
def build_otherfeatures(self, weight, screen_size, os, battery):
self.__mobile.weight = weight
self.__mobile.screen_size = screen_size
self.__mobile.os = os
self.__mobile.battery = battery
builder = MyMobileBuilder()
builder.build_name('Samsung S10')
builder.build_memory('8GB')
builder.build_camera('16 megapixels')
mobile = builder.get_mobile()
mobile.show()name: Samsung S10
weight: None
screen_size: None
ram: 8GB
os: None
camera_mp: 16 megapixels
battery: None
# Builder is less valuable in python, because you can specify default values
class Mobile:
def __init__(self,
name,
weight='157gm',
screen_size='5inches',
ram='8GB',
os='Android',
camera_mp='16 megapixels',
battery='3400 mAh'):
self.name = name
self.weight = weight
self.screen_size = screen_size
self.ram = ram
self.os = os
self.camera_mp = camera_mp
self.battery = battery
def show(self):
print("name:", self.name)
print("weight:", self.weight)
print("screen_size:", self.screen_size)
print("ram:", self.ram)
print("os:", self.os)
print("camera_mp:", self.camera_mp)
print("battery:", self.battery)
samsung_s10 = Mobile('Samsung S10')
samsung_s10.show()name: Samsung S10
weight: 157gm
screen_size: 5inches
ram: 8GB
os: Android
camera_mp: 16 megapixels
battery: 3400 mAh
class Connection:
def __init__(self):
self.__is_used = False
# Imagine a very heavy-duty initialization process here
# to set up the database connections and connect
self.connect_to_database()
def acquire(self):
self.__is_used = True
def release(self):
self.__is_used = False
def is_used(self):
return self.__is_used
def connect_to_database(self):
passclass ConnectionPool:
__instance = None
def __new__(cls, num_connections):
if cls.__instance is None:
print('No instance exists, creating a new one')
cls.__instance = super(ConnectionPool, cls).__new__(cls)
cls.__instance.__num_connections = num_connections
cls.__instance.__connections = []
for i in range(num_connections):
cls.__instance.__connections.append(Connection())
else:
print('A previously created instance exists, returning that same one')
return cls.__instance
def acquire(self):
for i in range(self.__num_connections):
connection = self.__connections[i]
if not connection.is_used():
connection.acquire()
return connection
return None
def release(self, connection):
if connection.is_used():
connection.release()