1. What is Python?
Python is a high-level, interpreted, interactive, dynamically typed
object-oriented scripting language.
key features:
o It doesn’t need to be compiled before execution.
o Python allows programming in OOS and Procedural
paradigms.
o Python is a cross-platform language, i.e., a Python program
written on a Windows system will also run on a Linux system
with little or no modifications.
2. purpose of PYTHONPATH environment variable
PYTHONPATH variable tells Python Interpreter where to locate the
module files imported into a program. It should include Python source
library directory and the directories containing Python source code.
3. Which data types are supported in Python
o Numeric Types: int, float, complex(1j)
o Sequence Types: List, Tuple, Strings
o Set: Set, Frozenset
o Dictionary:
o Boollean:
What are type annotations? What are generic type annotations?
While Python is a dynamically typed language, there is a way to
annotate types for clarity purposes.
These are the built-in types:
o int
o float
o bool
o str
o bytes
Complex types from the typing module:
o List
o Set
o Dict
o Tuple
o Optional
4. How is memory managed in Python?
o Memory in Python is managed by Python private heap space.
All Python objects and data structures are located in a private
heap. This private heap manage by Python Interpreter, and a
programmer doesn’t have access to this private heap.
o Python memory manager takes care of the allocation of Python
private heap space.
o Memory for Python private heap space is made available by
Python’s in-built garbage collector, which recycles and frees up
all the unused memory.
What are loop interruption statements in Python?
o break
o continue
Benefits of using Python over the other scripting languages
such as Javascript.
o Application development is faster and easy.
o Extensive support of modules for any kind of application
development including data analytics/ ML / math-intensive
applications.
5. What is docstring in Python?
o Docstring used to describe the functionality of the function.
o Python include a description (quick notes) for their methods
using docstrings. Docstrings not completely ignored by the
Python Interpreter, Python documentation strings can actually
be accessed at the run time using the dot operator when
docstring is the first statement in a method/ function.
o docstring in functions is optional.
def power(a, b):
"""Returns arg1 raised to power arg2."""
return a ** b
print(power.__doc__)
6. Can you write code to determine the name of an object in
Python?
No objects in Python have any associated names. So there is no way
of getting the one for an object. The assignment is only the means of
binding a name to the value. The name then can only refer to access
the value. The most we can do is to find the reference name of the
object.
class Test:
def __init__(self, name):
[Link] = []
[Link] = name
def __str__(self):
return '{} holds ...'.format([Link])
obj = Test('obj')
print(obj)
complex
Comples numbers are especially used in electronics, optics and
quantum theory for describing waves and periodic phenomena.
Uses:
o Fouries transform uses complex numbers.
o Audio signal processing in ML.
o Speech recognition system.
o A complex number has real and imaginary part components (a
+ bj).
o Used to convert number or string to complex number.
Syntex: complex(real, imaginary)
num=2+3j
print([Link])
print([Link])
7. Define Pass statement in Python?
A Pass statement used when we cannot decide what to do in our
code, but we must type something for making syntactically correct.
8. What does the Python nonlocal statement do
o nonlocal statement causes the listed identifiers to refer to
previously bound variables in the nearest enclosing scope
excluding globals.
def count():
x = 2
y = 3
def sum():
print(x + y)
return sum()
count()
// 2
def count():
x = 2
def sum():
nonlocal x
x += 1
return x
return sum
obj = count()
obj2 = count()
print(obj())
print(obj())
print(obj2()) # (obj2 has its own separate count)
9. Function is First Class
A function in Python is an aggregation of related statements
designed to perform a computational, logical, or evaluative task.
Properties of first class functions:
o A function is an instance of the Object type.
o store them in data structures such as hash tables, lists, …
o Functions can be passed as arguments to other functions.
o Functions can return another function.
o store the function in a variable.
def shout(text):
return [Link]()
print (shout('Hello'))
#2
def shout(text):
return [Link]()
def whisper(text):
return [Link]()
def greet(func):
greeting = func("""Hi, I am created by a function.""")
print (greeting)
greet(shout)
greet(whisper)
#3
def add(x):
def add2(y):
return x + y
return add2
obj = add(2)
print(obj(3))
10. Pass by Reference or pass by value
def fun(x):
x[0] = 20
lst = [1, 2, 3]
fun(lst)
print(lst)
def fun(x, y=5): # Default arguments.
print(x)
print(y)
fun(10)
o Memoize_factorial main purpose is to store the intermediate
results in the variable called memory.
11. Closure
A Closure is a function object that remembers values if they are not
present in memory.
def outer(a):
b = 2
def inner():
nonlocal b
print(a + b)
return inner()
outer(3)
When and why to use Closures:
As closures are used as callback functions.
o For data hiding
o Reduce the use of global variables
12. Logging
import logging
[Link](filename='[Link]', level=[Link])
def logger(func):
def log_func(*args):
[Link]('Running "{}" with arguments
{}'.format(func.__name__,args))
print(func(*args))
return log_func
def add(x, y):
return x + y
def sub(x, y):
return x - y
add_logger = logger(add)
sub_logger = logger(sub)
add_logger(3, 3)
add_logger(4, 5)
13. Kwargs
We can pass a variable number of arguments to a function using two
special symbols:
1.)*args (Non-Keyword Arguments)
o *args allows take more arguments than the number of formal
arguments that previously defined.
o Using *, the variable that associate with * becomes an iterable.
o Ex. To make a multiply function that takes any number of
arguments and able to multiply them all together. It can be
done using *args.
2.)**kwargs (Keyword Arguments)
o Used to pass a keyworded, variable-length argument list.
o A keyword argument is where we provide a name to the
variable as pass it into the function.
o One can think of the kwargs as being a dictionary that maps
each keyword to the value that we pass alongside it. That is
why when we iterate over the kwargs there doesn’t seem to be
any order in which they were printed out.
N: “We use the *args OR **kwargs – when we have doubts about
the number of arguments pass in a function.”
def fun(a, b):
print(a, b)
fun(b="Practice", a="Geeks") # Keyword arguments
#2 Variable-length arguments:
def fun(*argv):
for item in argv:
print(item)
fun("Hello", "Welcome", "to", "World")
# 3
def fun(**kwargv):
for keys, value in [Link]():
print("%s == %s" % (keys, value))
fun(a="Hello", b="Welcome", c="to", d="World")
#4 with one extra argument.
def fun(arg1, **kwargv):
for keys, value in [Link]():
print("%s == %s" % (keys, value))
fun("Hi", a="Hello", b="Welcome", c="to", d="World")
14. Forced keyword arguments
keyword-only arguments can enforce with:
o If write '*,' in our function parameter list, all parameters after
that must be passed as keyword arguments.
o Arguments after variable-length arguments must be keyword
arguments.
def fun(a, b, *, c, d):
print(a, b, c, d)
fun(1, 2, c=3, d=4)
# fun(1, 2, 3, 4)
# not allowed:
def fun(*args, last):
for arg in args:
print(arg)
print(last)
fun(8, 9, 10, last=50)
15. Variables
o Global variables are defined and declared outside a function
and we use them inside a function.
o Global keyword is used inside a function only when we want to
do assignments or when we want to change a variable. Global
is not needed for printing and accessing.
Rules of global keyword:
o If a variable is assigned a value anywhere within the function’s
body, it’s assumed to be a local unless explicitly declared as
global.
o Variables that are only referenced inside a function are
implicitly global.
#1
def fun():
str = "Me too."
print(str)
str = "I love"
fun()
#2
a = 10
print("outside", a)
def fun():
global a
a = 15
print("inside ", a)
fun()
print("outside", a)
#3
a = 10
print(id(a))
def fun():
a = 20
x = globals()["a"]
print(id(x))
print("inside ", a)
globals()["a"] = 15
fun()
print("outside", a)
16. Function Arguments and parameters
def fun(name): # name is the parameter
print(name)
fun("Alex") # 'Alex' is the argument
17. Positional and keyword arguments
Benefits of keyword arguments can be:
o Call arguments by their names to make it more clear what they
represent.
o Most readable.
o assign keyword If not able to assign positional argument.
#positional argument
def fun(a,b):
print(a, b)
fun(1,2)
#keyword argument
def fun(a,b):
print(a, b)
fun(b=1,a=2)
#3
def fun(a, b, c):
print(a, b, c)
fun(1, 2, 3) #
positional arguments
fun(a=1, b=2, c=3) #
keyword arguments
fun(c=3, b=2, a=1) #
Order is not important
fun(1, b=2, c=3)
# This is not allowed:
fun(1, b=2, 3) #
positional argument after keyword argument
fun(1, b=2, a=3) #
multiple values for argument 'a'
18. Default arguments
Default arguments with a predefined value, must be defined as the
last parameters in a function.
def fun(a, b, c, d=4):
# default arguments
print(a, b, c, d)
fun(1, 2, 3)
fun(1, b=2, c=3, d=100)
def fun(a, b=2, c, d=4):
# default arguments must be at the end
print(a, b, c, d)
Unpacking into agruments
o Lists/ tuples can be unpacked into arguments with (*) if the
length of the container matches the number of function
parameters.
o Dictionaries can be unpacked into arguments with (**) if the
length and keys match the function parameters.
def fun(a, b, c):
print(a, b, c)
list = [4, 5, 6] # or tuple
fun(*list) #
list/tuple unpacking, length must match
dict = {'a': 1, 'b': 2, 'c': 3} # dict
unpacking, keys and length must match
fun(**dict)
# dict = {'a': 1, 'b': 2, 'd': 3} # not
possible since wrong keyword
19. Parameter passing
Parameter passing can be done by Call-by-Object or Call-by-
Object-Reference.
o Parameter passed in is actually a reference to an object (but
the reference is passed by value).
o Difference between mutable and immutable data types.
This means that:
o Mutable objects (lists,dict) can be changed within a method.
But if you rebind the reference in the method, the outer
reference will still point at the original object.
o Immutable objects (int, string) cannot be changed within a
method.
But immutable object CONTAINED WITHIN a mutable object
can be re-assigned within a method.
# immutable objects
def fun(x):
x = 5 # x += 5 also no effect
since x is immutable and a new variable must be created
var = 10
print('var before fun():', var)
fun(var)
print('var after fun():', var)
#2 mutable objects
def fun(list):
[Link](4)
list2 = [1, 2, 3]
print("list2 before fun", list2)
fun(list2)
print("list2 after fun", list2)
#3 immutable objects within a mutable object
a_list[0] = -100
a_list[2] = "Paul"
list = [1, 2, "Max"]
print('list before fun', list)
fun(list)
print('list after fun', list)
#4 Rebind a mutable reference
def fun(list):
list = [50, 60, 70]
[Link](50)
list2 = [1, 2, 3]
print("list2 before", list2)
fun(list2)
print("list2 after", list2)
20. Operators
val = input("Enter your value: ")
print(val)
#2. Ternary
a, b = 10, 20
min = a if a < b else b
print(min)
print("Both equal" if a == b else "a > b" if a > b else "b > a")
# Ternary operator as nested if-else
#3. Direct Method by using tuples, Dictionary and lambda Python.
print((b, a)[a < b])
# Use tuple for selecting an item
print({True: a, False: b}[a < b])
# Use Dictionary for selecting an item
#4. Only one expression will be evaluated unlike in tuple and
Dictionary.
print((lambda: b, lambda: a)[a < b]())
Operator Overloading
Python magic methods or special functions for operator
overloading.
o + :__add__(self, other)
o – :__sub__(self, other)
o * :__mul__(self, other)
o / :__truediv__(self, other)
o // :__floordiv__(self, other)
o % :__mod__(self, other)
o ** :__pow__(self, other)
class Plus:
def __init__(self, a):
self.a = a
def __add__(self, o):
return self.a + o.a
obj1 = Plus(1)
obj2 = Plus(2)
obj3 = Plus("Geeks")
obj4 = Plus("For")
print(obj1 + obj2)
print(obj3 + obj4)
// 2
class complex:
def __init__(self, a, b):
self.a = a
self.b = b
def __add__(self, other):
return self.a + other.a, self.b + other.b
obj1 = complex(1, 2)
obj2 = complex(2, 3)
print(obj1 + obj2)
// 3
class Condition:
def __init__(self, a):
self.a = a
def __gt__(self, other):
if self.a > other.a:
return True
else:
return False
obj1 = Condition(2)
obj2 = Condition(3)
if obj1 > obj2:
print("obj1")
else:
print("obj2")
Divisions
print (5//2)
print (-5.0/2)
21. Any and All
Any and All are used for And/Or.
o Any: Returns True if any of the elements of a given iterable
(List, Dictionary, Tuple, set, etc) are True else False.
o All: Returns True if all elements in the given iterable are True.
else False.
obj = ["True", "True", "True"]
result = all(obj)
result = any(obj)
print(result)
22. Assignment
import operator
a = 3
b = 3
c = 4
x = [Link](a, b)
x = [Link](a, b)
x = [Link](a, b)
x = [Link](a, b)
x = [Link](a, b)
x = [Link](a, b)
x = [Link](a, b)
print(x)
if ([Link](a, b)): #
using lt() to check if a is less than b
print(a)
else:
print("a !< b")
if ([Link](a, b)): #
using le() to check if a is less than or equal to b
print("a<=b")
else:
print(b)
if ([Link](a, b)): #
using eq() to check if a is equal to b
print("a==b")
else:
print(c)
if ([Link](a, b)): #
using gt() to check if a is greater than b
print("a>b")
else:
print(b)
if ([Link](a, b)): #
using ge() to check if a is greater than or equal to b
print("a=>b")
else:
print(a)
if ([Link](a, b)): #
using ne() to check if a is not equal to b
print("a!==b")
else:
print(b)
//5
li = [1, 5, 6, 7, 8]
for i in range(0,len(li)):
print (li[i],end=" ")
[Link](li,3,3) # using
setitem() to assign 3 at 4th position
for i in range(0,len(li)): #
printing modified list after setitem()
print (li[i],end=" ")
[Link](li,1) # using
delitem() to delete value at 2nd index
for i in range(0,len(li)):
print (li[i],end=" ")
print ([Link](li,3)) # using
getitem() to access 4th element
//6
li = [1, 5, 6, 7, 8]
for i in range(0,len(li)):
print (li[i],end=" ")
[Link](li,slice(1,4),[2,3,4]) # using
setitem() to assign 2,3,4 at 2nd,3rd and 4th index
for i in range(0,len(li)):
print (li[i],end=" ")
[Link](li,slice(2,4)) # using
delitem() to delete value at 3rd and 4th index
for i in range(0,len(li)):
print (li[i],end=" ")
print ([Link](li,slice(0,2))) # using
getitem() to access 1st and 2nd element
//7
str = "geeksfor"
str2 = "geeks"
print ([Link](str,str2))
if ([Link](str,str2)): #
contains() to check if str contains str2
print ("geeksfor")
else : print ("not contain geeks")
//8 Python code to demonstrate working of and_(), or_(), xor(),
invert()
print (operator.and_(a,b)) # using
and_() to display bitwise and operation
print (operator.or_(a,b)) # using
or_() to display bitwise or operation
print ([Link](a,b)) # using
xor() to display bitwise exclusive or operation
[Link](a) # using
invert() to invert value of a
print ([Link](a))
23. ISS (Difference between == and is)
List1 and list2 refers to different objects.
list1 = []
list2 = []
list3=list1
if (list1 == list2):
print("True")
else:
print("False")
if (list1 is list2):
print("True")
else:
print("False")
print(id(list1))
print(id(list2))
24. Membership and Identity Operators
Membership operators are operators used to validate the
membership of a value. It test for membership in a sequence, such
as strings, lists, or tuples.
Identity operators:
Determine whether a value is of a certain class or type. They are
usually used to determine the type of data a certain variable contains.
‘is’ operator – Evaluates to True if the variables on either side of the
operator point to the same object otherwise False.
# The ‘in’ operator is used to check if a value exists in a sequence
or not.
list1=[1,2,3,4,5]
list2=[6,7,8,9]
for item in list1:
if item in list2:
print("overlapping")
else:
print("not overlapping")
#2 Identity operators
x = 5
if (type(x) is int):
print("true")
else:
print("false")
#3 ‘is not’ operator
x = 5.2
if (type(x) is not int):
print("true")
else:
print("false")
25. Conditions
print("Welcome to the rollercoaster")
height = int(input("What is your height in cm? "))
if height >= 120:
print("You can ride")
age = int(input("What is your age:"))
if age <= 12:
print("Please pay $5")
elif age <= 18:
print("Please pay $7")
else:
print(("Plese pay $10"))
else:
print("Sorry")
#2 Leap year or Not
year = int(input("Which year you want to check? "))
if year % 4 == 0:
if year % 100 == 0:
if year % 400 == 0:
print(f"Year {year} is leap")
else:
print(f"Not Leap Year is {year}")
else:
print(f"Year {year} is leap")
else:
print(f"Not Leap Year is {year}")
#3 if-elif-else ladder
i = 20
if (i == 10):
print ("i is 10")
elif (i == 15):
print ("i is 15")
elif (i == 20):
print ("i is 20")
else:
print ("i is not present")
26. While
o Using iteritem(): iteritems() is used to loop through the
dictionary printing the dictionary key-value pair sequentially.
o Using items(): items() performs the similar task on dictionary
as iteritems() but have certain disadvantages when compared
with iteritems().
▪ It is very time-consuming. Calling it on large dictionaries
consumes quite a lot of time.
▪ Takes lot of memory. Sometimes takes double the
memory when called on a dictionary.
o enumerate(): is used to loop through the containers printing
the index number along with the value present in that particular
index.
o zip(): is used to combine 2 similar containers(list-list or dict-
dict) printing the values sequentially. The loop exists only till
the smaller container ends.
o items: using items to print the dictionary key-value pair.
for key, value in enumerate(['The', 'Big', 'Bang', 'Theory']):
print(key, value)
#2
questions = ['name', 'colour', 'shape']
answers = ['apple', 'red', 'a circle']
for question, answer in zip(questions, answers):
print('What is your {0}? I am {1}.'.format(question, answer))
#3
king = {'Akbar': 'The Great', 'Chandragupta': 'The Maurya', 'Modi' :
'The Changer'}
for key, value in [Link]():
print(key, value)
#4 Using reversed()
lis = [ 1 , 3, 5, 6, 2, 1, 3 ]
for i in reversed(lis):
print (i,end=" ")
zip()
o Takes iterable/ containers and returns a single iterator object,
having mapped values from all the containers.
o It is used to map the similar index of multiple containers so that
they can be used just using a single entity.
o If passed iterators have different lengths, the iterator with the
least items decides the length of the new iterator.
keys=['fruite', 'mobile', 'count']
values=['Apple', 'Sony', '1']
newDict=dict(zip(keys, values))
print(newDict)
Get address of the varriable
Don't Create constant varriable in Python.
def arguments(num);
#Formal arguments
print(id(num))
arguments(10)
#Actual arguments
BitWise Operators
Operators Name Explains
& AND print(12 & 13)
| OR print(12 | 13)
^ XOR Sets each bit to 1 if only one of two bits is 1. print(12 ^ 13)
~ NOT Inverts all the bits. print(~12)
<< (Zero fill left shift) = Shift left by pushing zeros in from the
right and let the leftmost bits fall off
print(10 << 2)
>> (Signed right shift) = Shift right by pushing copies of the
leftmost bit in from the left, and rightmost bits fall off
print(10 >> 2)
For Else
The else keyword in a for loop specifies a block of code to be
executed when the loop is finished.
for x in range(6):
print(x)
else:
print("Finally finished!")
Decorators
It allows programmers to modify the behaviour of function or class.
Decorators allow us to wrap another function in order to extend the
behaviour of the wrapped function, without permanently modifying it.
# defining a decorator
def hello_decorator(func):
# inner1 is a
Wrapper function in which the argument is called
def inner1(): # inner
function can access outer local functions "func"
print("Hello, this is before function execution")
func() #
calling the actual function inside the wrapper function.
print("This is after function execution")
return inner1
def function_to_be_used(): #
defining a function, to be called inside wrapper
print("This is inside the function !!")
# passing 'function_to_be_used' inside the decorator to control its
behaviour
function_to_be_used = hello_decorator(function_to_be_used)
function_to_be_used()
o Tuple: A tuple is a collection of objects which is ordered and
immutable.
▪ my_tuple = ("Max", 28, "New York")
▪ Values of a tuple are separated by ‘commas’. Although it
is not necessary.
▪ Creation of tuple without the use of parentheses is known
as Tuple Packing.
▪ Add/ change items, Not possible and will raise a
TypeError.
o List: List is a collection data type which is ordered and
mutable.
▪ my_list = ["banana", "cherry", "apple"]
▪ Useful for preserving a sequence of data and further
iterating over it.
o Set: A Set is an unordered , unindexed, mutable, and has no
duplicate elements.
▪ Use curly braces or the built-in set function to create a
set.
▪ An empty set cannot be created with Curly braces, as
this is interpreted as dict use set().
o Dictionary: Is an unordered, mutable and indexed. No
duplicate members.
▪ Collection of key-value pairs.
▪ Dictionary keys are case sensitive.
▪ Create a dictionary with braces, or with the built-in dict
funtion.
Reasons to use a tuple over a list
o Use tuple for heterogeneous (different) datatypes and list for
homogeneous datatypes.
o Since tuple are immutable, iterating through tuple is faster than
list.
o Tuples with their immutable elements can be used as key for a
dictionary. This is not possible with lists.
Create a tuple
Special case: A tuple with only one element needs to have a comma
at the end, otherwise it is not recognized as tuple
my_tuple = ("Max", 28, "New York")
my_tuple_2 = "Linda", 25, "Miami"
# Parentheses are optional
my_tuple_3 = (25, 1, 2, 3, 4,)
x = my_tuple.count('Max')
x = my_tuple[-1]
x = my_tuple.index(28)
# Return index of first item of 28.
x = len(my_tuple)
x = max(my_tuple_3)
x = min(my_tuple_3)
x = any(my_tuple_3)
x = all(my_tuple_3)
x = enumerate(my_tuple_3)
x = sum(my_tuple_3)
x = sorted(my_tuple_3)
x = tuple(my_tuple_3)
# Convert an iterable to a tuple.
name, age, city = my_tuple
print(x)
print(name, age, city)
#2
tuple_4 = tuple([1,2,3]) # convert an iterable
(list, dict, string) with the built-in tuple function
print(tuple_4)
Tuple Usefule methods
my_tuple = ('a', 'b') * 5
print(my_tuple)
#2 convert list to a tuple.
my_list = ['a', 'b', 'c', 'd']
list_to_tuple = tuple(my_list)
print(list_to_tuple)
tuple_to_list = list(list_to_tuple)
print(tuple_to_list)
#3 convert string to tuple.
string_to_tuple = tuple('Hello')
print(string_to_tuple)
Unpack tuple
o Number of variables have to match number of tuple elements.
o Unpack multiple elements to a list with *.
o Nested tuples: Tuples can contain other tuples (or other
container types).
my_tuple = (0, 1, 2, 3, 4, 5)
first, *items_between, last = my_tuple
print(first, items_between, last)
#2. Nested tuples
a = ((0, 1), ('age', 'height'))
print(a[0])
Compare tuple and list
# compare the size
import sys
my_list = [0, 1, 2, "hello", True]
my_tuple = (0, 1, 2, "hello", True)
print([Link](my_list), "bytes")
print([Link](my_tuple), "bytes")
# compare the execution time of a list vs. tuple creation statement
import timeit
print([Link](stmt="[0, 1, 2, 3, 4, 5]", number=1000000))
print([Link](stmt="(0, 1, 2, 3, 4, 5)", number=1000000))
Creating A List
o Extend() :Add all elements of a list to the another list.
o copy() :Returns a copy of the list.
o reduce() :apply a particular function passed in its argument to
all of the list elements stores the intermediate result and only
returns the final summation value.
o ord() :Returns an integer representing the Unicode code point
of the given Unicode character.
o cmp() :This function returns 1, if first list is “greater” than
second list.
o enumerate() :Returns enumerate object of list.
o accumulate() :apply a particular function passed in its
argument to all of the list elements returns a list containing the
intermediate results.
my_list = ["banana", "cherry", "apple", 5, True, 0, 1, 1]
print(my_list)
list_2 = list()
# Or create an empty list with the list function
print(list_2)
x = my_list.append("orange")
x = my_list.index(1)
x = my_list.insert(1, "blueberry")
x = my_list.pop()
x = my_list.remove("cherry")
x = my_list.clear()
x = my_list.reverse()
x = my_list.sort()
x = my_list.count(0)
x = sum(my_list)
x = max(my_list)
x = min(my_list)
x = all(my_list)
x = any(my_list)
x = len(my_list)
x = enumerate(my_list)
print(x)
list_with_zeros = [0] * 5
# create list with repeated elements
list_concat = list_with_zeros + my_list
string_to_list = list('Hello')
# convert string to list
Copy a list
list_org = ["banana", "cherry", "apple"]
list_copy = list_org.copy()
list_copy.append(True)
# modifying the copy not affects the original
print(list_org)
print(list_copy)
List comprehension
o A elegant and fast way to create a new list from an existing list.
o List comprehension consists of an expression followed by a for
statement inside square brackets.
a = [1, 2, 3, 4, 5, 6, 7, 8]
b = [i * i for i in a]
print(b)
# nested Lists
a = [[1, 2], [3, 4]]
print(a)
print(a[0])
Sets
o update() :Updates a set with the union of itself and others.
o union() :Returns the union of sets in a new set.
o difference() :Returns the difference of two or more sets as a
new set.
o difference_update() :Removes all elements of another set
from this set.
o discard() :Removes an element from set if it is a member. (Do
nothing if the element is not in set).
o intersection() :Returns the intersection of two sets as a new
set.
o intersection_update() :Updates the set with the intersection of
itself and another.
o isdisjoint() :Returns True if two sets have a null intersection.
o issubset() :Returns True if another set contains this set.
o issuperset() :Returns True if this set contains another set.
o symmetric_difference() :Returns the symmetric difference of
two sets as a new set.
o symmetric_difference_update() :Updates a set with the
symmetric difference of itself and another.
my_set = {"apple", "banana", "cherry"}
my_set_2 = set(["one", "two", "three"]) # or
use the set function and create from an iterable
my_set_3 = set("aaabbbcccdddeeeeeffff")
my_set.add("three")
my_set.remove("three")
my_set.discard("three") #
removes x
my_set.pop() #
removes x
my_set.clear()
print(my_set)
Union and Intersection
union() : combine elements from both sets, no duplication. This does
not change the two sets
intersection(): take elements that are in both sets
odds = {1, 3, 5, 7, 9}
evens = {0, 2, 4, 6, 8}
u = [Link](evens)
i = [Link](evens)
print(u)
Difference of sets
difference() : returns a set with all the elements from the setA that
are not in setB.
[Link](B) is not the same as [Link](A)
symmetric_difference() : returns a set with all the elements that are
in setA and setB but not in both
A.symmetric_difference(B) = B.symmetric_difference(A)
setA = {1, 2, 3, 4, 5, 6, 7, 8, 9}
setB = {1, 2, 3, 10, 11, 12}
diff_set = [Link](setB)
diff_set = [Link](setA)
diff_set = setA.symmetric_difference(setB)
diff_set = setB.symmetric_difference(setA)
print(diff_set)
Updating sets
update() : Update the set by adding elements from another set.
intersection_update() : Update the set by keeping only the
elements found in both
difference_update() : Update the set by removing elements found in
another set.
symmetric_difference_update() : Update the set by only keeping
the elements found in either set, but not in both
Note: all update methods also work with other iterables as argument,
e.g lists, tuples
setA = {1, 2, 3, 4, 5, 6, 7, 8, 9}
setB = {1, 2, 3, 10, 11, 12}
[Link](setB)
setA = {1, 2, 3, 4, 5, 6, 7, 8, 9}
setA.intersection_update(setB)
setA = {1, 2, 3, 4, 5, 6, 7, 8, 9}
setA.difference_update(setB)
setA = {1, 2, 3, 4, 5, 6, 7, 8, 9}
setA.symmetric_difference_update(setB)
[Link]([1, 2, 3, 4, 5, 6])
Subset, Superset, and Disjoint
setA = {1, 2, 3, 4, 5, 6}
setB = {1, 2, 3}
print([Link](setB))
print([Link](setA)) #
issubset(setX): Returns True if setX contains the set
print([Link](setB)) #
issuperset(setX): Returns True if the set contains setX
print([Link](setA))
setC = {7, 8, 9}
print([Link](setB)) # True if
both sets have a null intersection
print([Link](setC))
Frozenset
Frozen set is just an immutable version of normal set. While
elements of a set can be modified at any time, elements of frozen set
remains the same after creation.
my_frozenset = frozenset(iterable)
odds = frozenset({1, 3, 5, 7, 9})
evens = frozenset({0, 2, 4, 6, 8})
print([Link](evens))
print([Link](evens))
print([Link](evens))
Dictionaries
o get() :It is a conventional method to access a value for a key.
o dictionary_name.values() :returns a list of all the values
available in a given dictionary.
o str() :Produces a printable string representation of a dictionary.
o update() :Adds dictionary dict2’s key-values pairs to dict.
o setdefault() :Set dict[key]=default if key is not already in dict.
o keys() :Returns list of dictionary dict’s keys.
o items() :Returns a list of dict’s (key, value) tuple pairs.
o has_key() :Returns true if key in dictionary dict, false
otherwise.
o fromkeys() :Create a new dictionary with keys from seq and
values set to value.
o type() :Returns the type of the passed variable.
o cmp() :Compares elements of both dict.
my_dict = {"name":"Max", "age":28, "city":"New York"}
my_dict_2 = dict(name="Lisa", age=27, city="Boston")
name_in_dict = my_dict["name"]
my_dict["email"] = "max@[Link]"
# add a new key.
my_dict["email"] = "coolmax@[Link]"
# overwrite the existing key.
del my_dict["email"]
# delete a key-value pair.
print(my_dict)
print("popped value:", my_dict.pop("age"))
# removes the last inserted key-value pair.
print("popped item:", my_dict.popitem())
Check for keys
my_dict = {"name":"Max", "age":28, "city":"New York"}
if "name" in my_dict:
print(my_dict["name"])
try:
print(my_dict["firstname"])
except KeyError:
print("No key found")
Looping through dictionary
for key in my_dict:
print(key, my_dict[key])
for key in my_dict.keys():
print(key)
for value in my_dict.values():
# loop over values
print(value)
for key, value in my_dict.items():
# loop over keys and values
print(key, value)
Merge two dictionaries
Use the update() method to merge 2 dicts existing keys are
overwritten, new keys are added
my_dict = {"name":"Max", "age":28, "email":"max@[Link]"}
my_dict_2 = dict(name="Lisa", age=27, city="Boston")
my_dict.update(my_dict_2)
print(my_dict)
Possible key types
Any immutable type, like strings or numbers can be used as a key.
Also, a tuple can be used if it contains only immutable elements.
do not mistake the keys as indices of a list, e.g my_dict[0] is not
possible here
my_dict = {3: 9, 6: 36, 9:81} # use
numbers as key, but be careful
print(my_dict[3], my_dict[6], my_dict[9])
my_tuple = (8, 7) # use
a tuple with immutable elements (e.g. number, string)
my_dict = {my_tuple: 15}
print(my_dict[my_tuple])
print(my_dict[8, 7])
# my_list = [8, 7] # a
list is not possible because it is not immutable
# my_dict = {my_list: 15}
1. Strings
o Python strings are immutable.
o Use triple quotes for multiline strings.
o count(): Returns the number of times a specified value occurs
in a string.
o endswith(): Returns true if the string ends with the specified
value.
o find(): Searches the string for a specified value and returns the
position.
o format(): Formats specified values in a string.
o format_map(): Formats specified values in a string
o index(): Searches the string for a specified value and returns
the position.
o isalnum(): Returns True if all characters in the string are
alphanumeric.
o isalpha(): Returns True if all characters in the string are in the
alphabet.
o isdecimal(): Returns True if all characters in the string are
decimals.
o isdigit(): Returns True if all characters in the string are digits.
o isidentifier(): Returns True if the string is an identifier.
o islower(): Returns True if all characters in the string are lower
case.
o isupper():
o lower(): Converts a string into lower case.
o isnumeric(): Returns True if all characters in the string are
numeric.
o isprintable(): Returns True if all characters in the string are
printable.
o isspace(): Returns True if all characters in the string are
whitespaces.
o istitle(): Returns True if the string follows the rules of a title.
o join(): Joins the elements of an iterable to the end of the string.
o ljust(): Returns a left justified version of the string.
o lstrip(): Returns a left trim version of the string.
o maketrans(): Returns a translation table to be used in
translations.
o partition(): Returns a tuple where the string is parted into three
parts.
o rpartition(): Returns a tuple where the string is parted into
three parts.
o replace(): Returns a string where a specified value is replaced
with a specified value.
o rfind(): Searches the string for a specified value and returns
the last position of where it was found.
o rindex(): Searches the string for a specified value and returns
the last position of where it was found.
o rjust(): Returns a right justified version of the string.
o rstrip(): Returns a right trim version of the string.
o rsplit(): Splits the string at the specified separator, and returns
a list.
o split(): Splits the string at the specified separator, and returns
a list
o splitlines(): Splits the string at line breaks and returns a list.
o startswith(): Returns true if the string starts with the specified
value.
o strip(): Returns a trimmed version of the string.
o swapcase(): Swaps cases, lower case becomes upper case.
o title(): Converts the first character of each word to upper case.
o translate(): Returns a translated string.
o zfill(): Fills the string with a specified number of 0 values at the
beginning.
txt = "My name is John, and I am {}"
my_string = """Hello
World"""
print(type(my_string))
print(my_string[1])
print(len(my_string))
print(my_string.strip())
print(my_string.lower())
print(my_string.upper())
print(my_string.replace("H", "J"))
print(my_string.split(","))
print(len(my_string))
print("hello".startswith("he"))
# startswith and endswith
print("hello".endswith("llo"))
print("Hello".find("o"))
# find first index of a given substring, -1 otherwise
print("Hello".count("e"))
my_list = ['How', 'are', 'you', 'doing']
str = ' '.join(my_list)
# join elements of a list into a string
print(str)
b = my_string[0]
# get character by referring to index
b = my_string[1:3]
# Substrings with slicing
b = my_string[::2]
# start to end with every second item
b = my_string[::-1]
Iterating
my_string = 'Hello'
for i in my_string:
print(i)
2. Format
a = "Hello {0} and {1}".format("Bob", "Tom")
# use braces as placeholders
a = "Hello {} and {}".format("Bob", "Tom")
# positions are optional for the default order
a = "The integer value is {}".format(2)
print(a)
a = "The float value is {0:.3f}".format(2.1234)
# some special format rules for numbers
a = "The float value is {0:e}".format(2.1234)
a = "The binary value is {0:b}".format(2)
print(a)
print("Hello %s and %s" % ("Bob", "Tom"))
# must be a tuple for multiple arguments
val = 3.14159265359
print("The decimal value is %d" % val)
# old style formatting by using % operator
3. f-Strings
Use the variables directly inside the braces.
name = "Eric"
age = 25
a = f"Hello, {name}. You are {age}."
print(a)
pi = 3.14159
a = f"Pi is {pi:.3f}"
print(a)
a = f"The value is {2*60}" # f-
Strings are evaluated at runtime, which allows expressions
print(a)
More on immutability and concatenation
o since a string is immutable, adding strings with + always
creates a new string, and therefore is expensive for multiple
operations.
o join method is much faster.
from timeit import default_timer as timer
my_list = ["a"] * 1000000
start = timer()
a = "".join(my_list)
end = timer()
print("concatenate string with join(): %.5f" % (end - start))
4. What are split(), sub(), and subn() methods in Python?
These methods belong to Python RegEx, ‘re’ module and are used to
modify strings.
o split():Used to split a given string into a list.
o sub(): Used to find a substring where a regex pattern matches,
and then it replaces the matched substring with a different
string.
o subn(): similar to the sub() method, but it returns the new
string, along with the number of replacements.
import re
#split
str = "Split string into list"
newStr = [Link]()
print(newStr)
#2 sub
newStr = [Link]("s", "9", str)
print(newStr)
#3 subn
str = [Link]('ov', '~*' , 'The rain in Spain', flags =
[Link])
print(str)
5. Lambda
def sum(a,b):
c=a+b
return c
obj=sum(2,3)
print(obj)
#2
f=lambda a,b:a+b
obj=f(2,3)
print(obj)
6. Lambda with Map. Filter
f=lambda n: 1 if n==0 else n+f(n-1)
print(f(5))
#2
list(filter(lambda x:x>6,range(9))) [7, 8]
list(map(lambda x:x**2,range(5))) [0, 1, 4, 9, 16, 25]
from functools import reduce
reduce(lambda x,y:x-y,[1,2,3,4,5]) -13
7. Lambda functions
o A lambda function is defined without a name.
o A lambda function can take any number of arguments, but
have only one expression.
o Anonymous functions are defined using the lambda keyword.
o It's most common use is as an argument to higher-order
functions.
f = lambda x: x+10
val1 = f(5)
val2 = f(100)
print(val1, val2)
f = lambda x,y: x*y
val3 = f(2,10)
val4 = f(7,5)
print(val3, val4)
8. Lamdba inside another function
def myfunc(n):
return lambda x: x * n
doubler = myfunc(2)
print(doubler(6))
9. Custom sorting using a lambda function as key parameter
The key function transforms each element before sorting.
points2D = [(1, 9), (4, 1), (5, -3), (10, 2)]
sorted_by_y = sorted(points2D, key= lambda x: x[1])
print(sorted_by_y)
mylist = [- 1, -4, -2, -3, 1, 2, 3, 4]
sorted_by_abs = sorted(mylist, key= lambda x: abs(x))
print(sorted_by_abs)
10. Use lambda for map function
map(func, seq), transforms each element with the function.
a = [1, 2, 3, 4, 5, 6]
b = list(map(lambda x: x * 2 , a))
c = [x*2 for x in a] #
Use map if you have an already defined function
print(b)
print(c)
11. Use lambda for filter function
filter(func, seq), returns all elements for which func evaluates to True.
a = [1, 2, 3, 4, 5, 6, 7, 8]
b = list(filter(lambda x: (x%2 == 0) , a))
c = [x for x in a if x%2 == 0]
print(b)
print(c)
12. Reduce
reduce(func, seq), repeatedly applies the func to the elements and
returns a single value. func takes 2 arguments.
from functools import reduce
a = [1, 2, 3, 4]
product_a = reduce(lambda x, y: x*y, a)
print(product_a)
sum_a = reduce(lambda x, y: x+y, a)
print(sum_a)
Stop after 20 iterations
class MyNumbers:
def __iter__(self):
self.a = 1
return self
def __next__(self):
if self.a <= 20:
x = self.a
self.a += 1
return x
else:
raise StopIteration
myclass = MyNumbers()
myiter = iter(myclass)
for x in myiter:
print(x)
For
o To prevent the iteration to go on forever, use the StopIteration
statement.
o Use pass statement to write empty loops.
o Pass is also used for empty control statements
d = dict()
d['xyz'] = 123
d['abc'] = 345
for i in d:
print("% s % d" % (i, d[i]))
#2
for letter in 'geeksforgeeks':
if letter == 'e' or letter == 's':
#Returns the control to the beginning of the loop.
continue
print('Current Letter :', letter)
#3
for letter in 'geeksforgeeks':
if letter == 'e' or letter == 's':
break
#Brings control out of the loop.
print('Current Letter :', letter)
#4 pass
function and classes.
for letter in 'geeksforgeeks':
pass
print('Last Letter :', letter)
#5
def addition(n):
#Pass one or more iterable to the map() function.
return n + n
numbers = (1, 2, 3, 4)
result = map(addition, numbers)
print(list(result))
product()
o It computes the cartesian product of input iterables.
o To allow the product of an iterable with itself, specify the
number of repetitions
o It is equivalent to nested for-loops.
▪ Ex. product(A, B) returns the same as ((x,y) for x in A
for y in B).
from itertools import product
prod = product([1, 2], [3, 4])
print(list(prod))
# convert the iterator to a list
prod = product([1, 2], [3], repeat=2)
print(list(prod))
permutations()
This tool returns successive length permutations of elements in an
iterable, with all possible orderings, and no repeated elements.
from itertools import permutations
perm = permutations([1, 2, 3])
print(list(perm))
perm = permutations([1, 2, 3], 2)
# optional: the length of the permutation tuples
print(list(perm))
combinations() and combinations_with_replacement()
r-length tuples, in sorted order. So, if the input iterable is sorted, the
combination tuples will be produced in sorted order. combinations()
does not allow repeated elements, but
combinations_with_replacement() does.
from itertools import combinations, combinations_with_replacement
comb = combinations([1, 2, 3, 4], 2) # second argument
is mandatory and specifies length of the output tuples.
print(list(comb))
comb = combinations_with_replacement([1, 2, 3, 4], 2)
print(list(comb))
accumulate()
Make an iterator that returns accumulated sums, or accumulated
results of other binary functions.
from itertools import accumulate
acc = accumulate([1,2,3,4]) #
return accumulated sums
print(list(acc))
# other possible functions are possible
import operator
acc = accumulate([1,2,3,4], func=[Link])
print(list(acc))
acc = accumulate([1,5,2,6,3,4], func=max)
print(list(acc))
groupby()
Make an iterator that returns consecutive keys and groups from the
iterable. The key is a function computing a key value for each
element. If not specified, key defaults to an identity function and
returns unchanged element.
from itertools import groupby
def smaller_than_3(x):
# use a function as key
return x < 3
group_obj = groupby([1, 2, 3, 4], key=smaller_than_3)
for key, group in group_obj:
print(key, list(group))
# or use a lamda expression, e.g. words with an 'i':
group_obj = groupby(["hi", "nice", "hello", "cool"], key=lambda x:
"i" in x)
for key, group in group_obj:
print(key, list(group))
persons = [{'name': 'Tim', 'age': 25}, {'name': 'Dan', 'age': 25},
{'name': 'Lisa', 'age': 27}, {'name': 'Claire', 'age':
28}]
for key, group in groupby(persons, key=lambda x: x['age']):
print(key, list(group))
Infinite iterators: count(), cycle(), repeat()
from itertools import count, cycle, repeat
for i in count(10): #
count(x): count from x: x, x+1, x+2, x+3...
print(i)
if i >= 13:
break
sum = 0
for i in cycle([1, 2, 3]): #
cycle infinitely through an iterable
print(i)
sum += i
if sum >= 12:
break
for i in repeat("A", 3): #
repeat x infinitely or n times
print(i)
Array
Array don't have fixed size.
append() Adds an element at the end of the list
clear() Removes all the elements from the list
copy() Returns a copy of the list
count() Returns the number of elements with the specified value
extend() Add the elements of a list (or any iterable), to the end of the
current list
index() Returns the index of the first element with the specified value
insert() Adds an element at the specified position
pop() Removes the element at the specified position
remove() Removes the first item with the specified value
reverse() Reverses the order of the list
sort() Sorts the list
from array import *
vals = array('i', [1,2,3,4,5,-6,7,8,9,10])
// Creating an Array
floatArr = array('d', [2.5, 3.2, 3.3])
// creating an array with float type
print([Link])
[Link](100)
[Link](1, 4)
[Link](1)
[Link](2)
[Link](2)
vals[2] = 6
# Updating an Elements in an Array
print(len(vals))
import array as arr
a = [Link]('i', [1, 2, 3, 4, 5, 6, 7, 8, 9, 10])
for i in range(0, 3):
print(a[i], end=" ")
//2. Sum of the array
def _sum(arr):
return(sum(arr))
ans = _sum([12, 3, 4, 15])
print (ans)
//3. Slpit Array
def splitArr(arr, n, k):
for i in range(0, k):
x = arr[0]
for j in range(0, n-1):
arr[j] = arr[j + 1]
arr[n-1] = x
arr = [12, 10, 5, 6, 52, 36]
n = len(arr)
position = 2
splitArr(arr, n, position)
for i in range(0, n):
print(arr[i], end = ' ')
Array Rotation
def leftRotate(arr, d, n):
for i in range(d):
leftRotatebyOne(arr, n)
def leftRotatebyOne(arr, n):
temp = arr[0]
for i in range(n-1):
arr[i] = arr[i+1]
arr[n-1] = temp
def printArray(arr,size):
for i in range(size):
print ("%d"% arr[i],end=" ")
arr = [1, 2, 3, 4, 5, 6, 7]
leftRotate(arr, 2, 7)
printArray(arr, 7)
Reverse Array Rotation
def rverseArray(arr, start, end):
while (start < end):
temp = arr[start]
arr[start] = arr[end]
arr[end] = temp
start += 1
end = end-1
def leftRotate(arr, d):
n = len(arr)
rverseArray(arr, 0, d-1)
rverseArray(arr, d, n-1)
rverseArray(arr, 0, n-1)
def printArray(arr):
for i in range(0, len(arr)):
print (arr[i])
arr = [1, 2, 3, 4, 5, 6, 7]
leftRotate(arr, 2)
# Rotate array by 2
printArray(arr)
Monotonic
def isMonotonic(A):
return (all(A[i] <= A[i + 1] for i in range(len(A) - 1)) or
all(A[i] >= A[i + 1] for i in range(len(A) - 1)))
A = [6, 5, 4, 4]
print(isMonotonic(A))
Convert from Python to JSON
o [Link](): Convert string to Python dict.
▪ [Link]() method can read a file which contains a
JSON object.
▪ returns JSON object as a dictionary
▪ parsing JSON string
▪ Serialization (From Python to JSON)
▪ The Python module json converts a Python dictionary
object into JSON object, and list and tuple are converted
into JSON array, and int and float converted as JSON
number, None converted as JSON null.
o [Link]() : method can be used for conversion to JSON.
It takes 2 parameters:
▪ dictionary – name of dictionary which should be
converted to JSON object.
▪ file pointer – pointer of the file opened in write or
append mode.
▪ Deserialization (FROM JSON to Python)
▪ loads(): To deserialize a JSON document to a Python
object.
▪ load(): To deserialize a JSON formatted stream to a
Python object.
import json
employee = '{"id":"09", "name": "Nitin", "department":"Finance"}'
employee_dict = [Link](employee)
json_object = [Link](employee_dict, indent=4)
print(json_object)
print(employee_dict)
# 2
f = open('[Link]',)
data = [Link](f)
for i in data['emp_details']:
print(i)
[Link]()
update() / append
This method update the dictionary with elements from another
dictionary object.
x = '{ "organization":"GeeksForGeeks", "city":"Noida",
"country":"India"}'
y = {"pin":110096}
z = [Link](x)
[Link](y)
# appending the data
print([Link](z))
JSON format
{
"firstName": "Jane",
"lastName": "Doe",
"hobbies": ["running", "swimming", "singing"],
"age": 28,
"children": [
{
"firstName": "Alex",
"age": 5
},
{
"firstName": "Bob",
"age": 7
}
]
}
Decoding
Decoding a custom object with the defaut JSONDecoder, but it will
be decoded into a dictionary. It write a custom decode function that
take a dictionary as input, and creates custom object. Use this
function for the object_hook argument in the [Link]() method.
z = [Link](zJSON)
print(type(z))
print(z)
def decode_complex(dct):
if complex.__name__ in dct:
return complex(dct["real"], dct["imag"])
return dct
z = [Link](zJSON, object_hook=decode_complex) #
Now the object is of type complex after decoding
print(type(z))
print(z)
Template encode and decode functions
This works for all custom classes if all instance variables are given in
the __init__ method.
import json
class User:
def __init__(self, name, age, active, balance, friends):
[Link] = name
[Link] = age
[Link] = active
[Link] = balance
[Link] = friends
class Player:
def __init__(self, name, nickname, level):
[Link] = name
[Link] = nickname
[Link] = level
def encode_obj(obj):
"""Takes in a custom object and returns a dictionary
representation of the object."""
obj_dict = { #
Populate the dictionary with object meta data
"__class__": obj.__class__.__name__,
"__module__": obj.__module__
}
obj_dict.update(obj.__dict__) #
Populate the dictionary with object properties
return obj_dict
def decode_dct(dct):
if "__class__" in dct:
class_name = [Link]("__class__")
module_name = [Link]("__module__") # Get
the module name from the dict and import it
module = __import__(module_name)
class_ = getattr(module,class_name)
obj = class_(**dct)
else:
obj = dct
return obj
# User class works with our encoding and decoding methods
user = User(name = "John",age = 28, friends = ["Jane", "Tom"],
balance = 20.70, active = True)
userJSON = [Link](user,default=encode_obj, sort_keys=True)
print(userJSON)
user_decoded = [Link](userJSON, object_hook=decode_dct)
print(type(user_decoded))
player = Player('Max', 'max1234', 5)
playerJSON = [Link](player,default=encode_obj, sort_keys=True)
print(playerJSON)
player_decoded = [Link](playerJSON, object_hook=decode_dct)
print(type(player_decoded))
What do file-related modules in Python do?
Python comes with some file-related modules that have functions to
manipulate text files and binary files in a file system. These modules
can be used to create text or binary files, update their content, copy,
delete, and more. Some file-related modules are os, [Link], and
[Link]. The [Link] module has functions to access the file system,
while the [Link] module can be used to copy or delete files.
Explain the use of the 'with' statement and its syntax.
Using the ‘with’ statement, we can open a file and close it as soon as
the block of code, where ‘with’ is used, exits.
“With” statement, you get better syntax and exceptions handling. In
addition, it will automatically close the file.
Create File
#1
fr = open("[Link]", "r")
print([Link]())
[Link]()
#2
with open('./[Link]','r') as fr:
print([Link]())
#3
with open('./[Link]','w') as fw:
data = 'some data to be written to the file'
print([Link](data))
Open File
o Text files :Each line of text is terminated with a special
character called EOL (End of Line), which is the new line
character (‘\n’) in Python by default.
o Binary files :There is no terminator for a line and the data is
stored after converting it into machine-understandable binary
language.
o Read Only (‘r’) :Open text file for reading. The handle is
positioned at the beginning of the file. If the file does not exist,
raises I/O error. This is also the default mode in which the file is
opened.
o Read and Write (‘r+’) :Open the file for reading and writing.
The handle is positioned at the beginning of the file. Raises I/O
error if the file does not exist.
o Write Only (‘w’) :Open the file for writing. For existing file, the
data is truncated and over-written. The handle is positioned at
the beginning of the file. Creates the file if the file does not
exist.
o Write and Read (‘w+’) :Open the file for reading and writing.
For existing file, data is truncated and over-written. The handle
is positioned at the beginning of the file.
o Append Only (‘a’) :Open the file for writing. The file is created
if it does not exist. The handle is positioned at the end of the
file. The data being written will be inserted at the end, after the
existing data.
o Append and Read (‘a+’) :Open the file for reading and writing.
The file is created if it does not exist. The handle is positioned
at the end of the file. The data being written will be inserted at
the end, after the existing data.
o seek(n): Takes the file handle to the nth bite from the
beginning.
o readlines(): returns a list containing each line in the file as a
list item.
o When the file is opened in append mode, the handle is
positioned at the end of the file. The data being written will be
inserted at the end, after the existing data.
#1
with open('[Link]', 'r+') as fr:
data = 'some data to be written to the file'
print([Link](data))
#2
file1 = open("[Link]", "w")
L = ["This is Delhi ", "This is Paris ", "This is London"]
[Link](L)
[Link]()
file1 = open("[Link]", "r+")
print([Link]())
print()
#3
f = open("[Link]", "r")
[Link](4)
print([Link]())
#4 Difference between read and readline
file1 = open("[Link]", "r")
print([Link](8))
print("Output of Readline(9) function is ")
print([Link](9))
[Link]()
#5 Appending to a file
file1 = open("[Link]", "w")
L = ["This is Delhi", "This is Paris", "This is London"]
[Link](L)
[Link]()
#6 Append-adds at last
file1 = open("[Link]", "a")
[Link]("Today")
[Link]()
file1 = open("[Link]", "r")
print("Output of Readlines after appending")
print([Link]())
[Link]()
#7 Write-Overwrites
file1 = open("[Link]", "w")
[Link]("Tomorrow")
[Link]()
file1 = open("[Link]", "r")
print([Link]())
[Link]()
Delete a file
import os
if [Link]("[Link]"):
[Link]("[Link]")
else:
print("The file does not exist")
Simple interest
def simple_interest(p,t,r):
si = (p * t * r)/100
print('The Simple Interest is', si)
return si
simple_interest(8, 6, 8)
Compound interest
def compound_interest(principle, rate, time):
CI = principle * (pow((1 + rate / 100), time))
print("Compound interest is", CI)
compound_interest(10000, 10.25, 5)
conver_12_to_24_hour_format
def convert24(str1):
if str1[-2:] == "AM" and str1[:2] == "12":
return "00" + str1[2:-2]
elif str1[-2:] == "AM":
return str1[:-2]
elif str1[-2:] == "PM" and str1[:2] == "12":
return str1[:-2]
else:
return str(int(str1[:2]) + 12) + str1[2:8]
print(convert24("08:05:45 PM"))
Sqrt
print(bin(25))
print(oct(0b11001))
print(hex(0b11001))
print(int('052', 8))
#2
import math as m
x=[Link](25)
print(x)
#3
x=[Link](15)
print([Link](x))
print([Link](x))
Pows
import math
print(3**2)
print([Link](3,2))
print([Link])
#2
from math import sqrt, pow
print(pow(4,5))
Area of triangle
a = 5
b = 6
c = 7
s = (a + b + c) / 2
# calculate the semi-perimeter
area = (s*(s-a)*(s-b)*(s-c)) ** 0.5
print('The area of the triangle is %0.2f' %area)
HCF
def hcf(x, y):
if x > y:
smaller = y
else:
smaller = x
for i in range(1, smaller + 1):
if ((x % i == 0) and (y % i == 0)):
hcf = i
return hcf
num1 = int(input("Enter first number: "))
num2 = int(input("Enter second number: "))
print("The H.C.F. of", num1, "and", num2, "is", hcf(num1, num2))
Largest number
num1 = 10
num2 = 14
num3 = 12
if (num1 >= num2) and (num1 >= num3):
largest = num1
elif (num2 >= num1) and (num2 >= num3):
largest = num2
else:
largest = num3
print("The largest number is", largest)
LCM
def lcm(x, y):
if x > y:
greater = y
else:
greater = x
for i in range(1, greater + 1):
if ((x % i == 0) and (y % i == 0)):
lcm = i
return lcm
num1 = int(input("Enter first number: "))
num2 = int(input("Enter second number: "))
print("The L.C.M. of", num1, "and", num2, "is", lcm(num1, num2))
Math Function
# Random Number
import random
print([Link](0,9))
#2 Sum 2 Numbers
number1 = input("First number: ")
number2 = input("Second number: ")
sum = float(number1) + float(number2)
print("The sum of {0} and {1} is {2}" .format(number1, number2, sum))
Quadratic Equation
import cmath
a = 1
b = 5
c = 6
d = (b**2) - (4*a*c)
# calculate the discriminant
sol1 = (-[Link](d))/(2*a)
sol2 = (-b+[Link](d))/(2*a)
print('The solution are {0} and {1}'.format(sol1,sol2))
Random Numbers
Python defines a set of functions that are used to generate or
manipulate random numbers.
o the random module
o reproduce numbers with [Link]()
o create cryptographically strong random numbers with the
secrets module
o create random and arrays with [Link]
The random module
This module implements pseudo-random number generators for
various distributions. It uses the Mersenne Twister algorithm as its
core generator. It is called pseudo-random, because the numbers
seem random, but are reproducable.
o normalvariate(): random float from a normal distribution with
mu and sigma.
o choice(): choose a random element from a sequence.
o choices(): choose k elements with replacement, and return k
sized list.
o sample(): choose k unique random elements from a sequence.
o shuffle(): shuffle list in place.
import random
a = [Link]()
# random float in [0,1]
a = [Link](1,10)
# random float in range [a,b]
a = [Link](1,10)
# random integer in range [a,b]. b is included
a = [Link](1,10)
# random integer in range [a,b]. b is excluded
a = [Link](0, 1)
a = [Link](list("ABCDEFGHI"))
a = [Link](list("ABCDEFGHI"), 3)
a = [Link](list("ABCDEFGHI"),k=3)
print(a)
a = list("ABCDEFGHI")
[Link](a)
print(a)
The seed generator
With [Link](), you can make results reproducible, and the
chain of calls after [Link]() will produce the same trail of data.
The sequence of random numbers becomes deterministic, or
completely determined by the seed value.
import random
[Link](1)
print([Link]())
print([Link](1,10))
print([Link](list("ABCDEFGHI")))
print('Re-seeding with 42...')
[Link](42)
# Re-seed
The secrets module
The secrets module is used for generating cryptographically strong
random numbers suitable for managing data such as passwords,
account authentication, security tokens, and related secrets.
import secrets
a = [Link](10)
a = [Link](5)
# return an integer with k random bits.
a = [Link](list("ABCDEFGHI"))
print(a)
Random numbers with NumPy
Create random numbers for and arrays. The NumPy pseudorandom
number generator is different from the Python standard library
pseudorandom number generator. Importantly, seeding the Python
pseudorandom number generator does not impact the NumPy
pseudorandom number generator. It must be seeded and used
separately.
import numpy as np
[Link](1) #
rand(d0,d1,…,dn)
print([Link](3)) # generate
an array with random floats, arrays has size (d0,d1,…,dn)
[Link](1)
print([Link](3))
values = [Link](0, 10, (5,3)) # generate an
array with random integers in range [a,b) with size n
print(values)
# generate an array with
Gaussian values, array has size (d0,d1,…,dn)
# values from standard normal
distribution with mean 0.0 and standard deviation 1.0
values = [Link](5)
print(values)
# randomly shuffle an array.
# only shuffles the array
along the first axis of a multi-dimensional array
arr = [Link]([[1,2,3], [4,5,6], [7,8,9]])
[Link](arr)
print(arr)
Objects
We can create one class object and many instance object. Class
object has static member varriables.
class Test:
x=10
//static member varriable
def __init__(self,a,b):
self.a=a
self.b=b
def show(self):
print(self.a, self.b)
print(Test.x)
//Static object
obj = Test(3,4) //Instance object,
After creating ins obj, Automatically call __init__
[Link]()
Difference between sorted and sort function.
o sort(): Alwase apply on list and return None. sort() do changes
on original list.
o sorted(): Alwase return list even pass tuples/ string.
#1
l=(4,3,5,6,8,0,1,2)
print(sorted(l))
#2
l=[4,3,5,6,8,0,1,2]
print([Link]())
How memory managed in Python
o Stack Memory: Have refrences
o Private Heap Space: Have Id of declared values, Objects
stored here.
How to create static member variables in class
o Python don't have static keyword.
o Static variable get mempry inside class objects.
class Items:
a=10
# Static member variables
def __init__(self):
self.a=1
# InStance member variable
y=4
# Local variable
Items.b=6
# Static variable
@staticmethod
def f2():
Items.d=8
#Static variable
@classmethod
def f3(cls):
cls.e=1
#Static variable
Items.f=10
#Static variable
Items.g=11
#Static variable
classes
o The __init__() function is called automatically every time the
class is being used to create a new object.
o Create a class named Person, use the __init__() function to
assign values for name and age.
o The self parameter is a reference to the current instance of the
class, and is used to access variables that belong to the class.
The self
o Class methods must have an extra first parameter in the
method definition. We do not give a value for this parameter
when we call the method, Python provides it.
o If we have a method that takes no arguments, then we still to
have one argument.
o When we call a method of this object as
[Link](arg1, arg2), this is automatically converted by
Python into [Link](myobject, arg1, arg2) – this is all
the special self is about.
class Person:
def __init__(self, name, age):
[Link] = name
[Link] = age
p1 = Person("John", 36)
print([Link])
print([Link])
#2
class Dog:
attr1 = "mammal"
attr2 = "dog"
def fun(self):
print("I'm a", self.attr1)
print("I'm a", self.attr2)
Rodger = Dog()
print(Rodger.attr1)
[Link]()
What is __init__ & self in Python?
'self' is used to represent the instance of a class. By using the "self"
keyword we access the attributes and methods of the class in python.
__init__ method: This method is called when an object is created
from a class and it allows the class to initialize the attributes of the
class.
class Rectangle:
def __init__(self, length, breadth, unit_cost=0):
[Link] = length
[Link] = breadth
self.unit_cost = unit_cost
def get_area(self):
return [Link] * [Link]
def calculate_cost(self):
area = self.get_area()
return area * self.unit_cost
r = Rectangle(160, 120, 2000)
print("%s sq units" % (r.get_area()))
__init__ method
class Person:
def __init__(self, name):
[Link] = name
def say_hi(self):
print('Hello, my name is', [Link])
p = Person('Nikhil')
p.say_hi()
Class and Instance Variables
o Instance variables:
▪ Instance variables are for data, unique to each instance.
▪ Instance variables are variables whose value is assigned
inside a constructor/ method with self.
▪ Defining instance variable using a constructor.
o class variables:
▪ class variables are for attributes and methods shared by
all instances of the class.
▪ class variables are variables whose value is assigned in
the class.
class Dog:
animal = 'dog'
def __init__(self, breed, color):
[Link] = breed
# Instance Variable
[Link] = color
Rodger = Dog("Pug", "brown")
Buzo = Dog("Bulldog", "black")
print('Rodger is a', [Link])
print('Breed: ', [Link])
print([Link])
Class or Static Variables
o All variables which are assigned a value in the class
declaration are class variables.
o And variables that are assigned values inside methods are
instance variables.
class method vs static method
o A static method does not receive an implicit first argument.
o A static method is also a method which is bound to the class
and not the object of the class.
o A static method can’t access/ modify class state.
class CSStudent:
stream = 'cse'
def __init__(self,name,roll):
[Link] = name
[Link] = roll
a = CSStudent('Geek', 1)
b = CSStudent('Nerd', 2)
print([Link])
print([Link])
print([Link]) # Class
variables can be accessed using class name also
[Link] = 'ece'
print([Link])
# prints 'ece'
print([Link])
# prints 'cse'
# To change the stream for all instances of the class we can change
it directly from the class
[Link] = 'mech'
print([Link])
# prints 'mech'
print([Link])
# prints 'mech'
How to define a class method and a static method?
from datetime import date
class Person:
def __init__(self, name, age):
[Link] = name
[Link] = age
@classmethod
def fromBirthYear(cls, name, year):
return cls(name, [Link]().year - year)
@staticmethod
# Static method to check if a Person is adult or not.
def isAdult(age):
return age > 18
person1 = Person('mayank', 21)
person2 = [Link]('mayank', 1996)
print([Link])
print([Link](22))
Constructors
Constructors are generally used for instantiating an [Link] task
of constructors is to initialize(assign values) to the data members of
the class when an object of class is [Link] Python the __init__()
method is called the constructor and is always called when an object
is created.
o default constructor :The default constructor is simple
constructor which doesn’t accept any [Link]’s definition
has only one argument which is a reference to the instance
being constructed.
o parameterized constructor :The parameterized constructor
take its first argument as a reference to the instance being
constructed known as self and the rest of the arguments are
provided by the programmer.
class GeekforGeeks:
def __init__(self):
# default constructor
[Link] = "GeekforGeeks"
def print_Geek(self):
print([Link])
obj = GeekforGeeks()
obj.print_Geek()
#2
class Addition:
first = 0
second = 0
answer = 0
def __init__(self, f, s):
# parameterized constructor
[Link] = f
[Link] = s
def display(self):
print("First number = " + str([Link]))
print("Second number = " + str([Link]))
print("Addition of two numbers = " + str([Link]))
def calculate(self):
[Link] = [Link] + [Link]
obj = Addition(1000, 2000)
[Link]()
[Link]()
Destructors
o Destructors are called when an object gets destroyed. In
Python, destructors are not needed because Python has a
garbage collector that handles memory management
automatically.
o The __del__() method is called when all references to the
object have been deleted.
class Employee:
def __init__(self):
print('Employee created.')
def __del__(self):
print('Destructor called, Employee deleted.')
obj = Employee()
del obj
Note :The destructor was called after the program ended or when all
the references to object are deleted.
Encapsulation
class Computer:
def __init__(self):
[Link] = 999
def sell(self):
print("Selling price: {}".format([Link]))
def setMAx(self, price):
[Link] = price
obj = Computer()
[Link]()
[Link] = 1000
[Link]()
[Link](2000)
[Link]()
Inherritance
o Single inheritance: A class inherits only one superclass
o Multiple inheritance: When a class inherits multiple
superclasses
o Multilevel inheritance: When a class inherits a superclass
and then another class inherits this derived class forming a
‘parent, child, and grandchild’ class structure
o Hierarchical inheritance: When one superclass is inherited by
multiple derived classes
class Animal:
def __init__(self, name):
[Link] = name
def speak():
pass
class Cat(Animal):
def speak(self):
return f"{[Link]} says meow"
cat = Cat("Whiskers")
print([Link]())
Multiple inheritance
class Animal:
def __init__(self, name):
[Link] = name
class Cat:
def __init__(self, price):
[Link] = price
class Dog(Animal, Cat):
def __init__(self, name, price):
super().__init__(name)
[Link] = price
def fun(self):
print([Link], [Link])
obj = Dog("Cat", 10)
print([Link]())
Multilevel inheritance
When we have a child and grandchild relationship.
class First:
def __init__(self, num1):
self.num1 = num1
class Second(First):
def __init__(self, num1, num2):
super().__init__(num1)
self.num2 = num2
def add(self):
return self.num1 + self.num2
class Third(Second):
def __init__(self, num1, num2, num3):
super().__init__(num1, num2)
self.num3 = num3
def add(self):
return self.num1 + self.num2 + self.num3
obj = First(10)
obj2 = Second(20, 30)
obj3 = Third(40, 50, 60)
print("First:", obj.num1)
print("Second:", [Link]())
print("Third:", [Link]())
o Hierarchical inheritance :Hierarchical inheritance More than
one derived classes are created from a single base.
o Hybrid inheritance :This form combines more than one form
of inheritance. Basically, it is a blend of more than one type of
inheritance.
Private members of parent class
We can make an instance variable by adding double underscores
before its name.
class C(object):
def __init__(self):
self.c = 21
self.d = 42
class D(C):
def __init__(self):
self.e = 84
C.__init__(self)
object1 = D()
print(object1.d)
Note :Since ‘d’ is made private by those underscores, it is not
available to the child class ‘D’ and hence the error.
Overriding Methods
class Animal:
def __init__(self, name):
[Link] = name
def speak(self):
print("sweet")
def speak(self):
print("Hi")
obj = Animal("A")
[Link]()
//2
class Animal:
def __init__(self):
pass
def speak(self):
print("sweet")
def speak(self, price):
[Link] = price
print("Hi", [Link])
obj = Animal()
[Link](1)
Polymorphism
class Animal:
def speak(self):
print("Animal")
class Cat(Animal):
def speak2(self):
print("Cat")
animal = Animal()
cat = Cat()
print([Link]())
print(cat.speak2())
Abstract Class
o An abstract class in Python is a class that cannot be
instantiated directly but serves as a blueprint for other classes.
It defines common methods and attributes that its subclasses
must implement. Python's abc (Abstract Base Classes) module
is often used to create abstract classes.
o Python default don't support abstract class. But with abc
module can do.
o Abstract class has at least one abstract method.
from abc import ABC, abstractmethod
class Shape(ABC):
@abstractmethod
def area(self):
pass
@abstractmethod
def perimeter(self):
pass
class Circle(Shape):
def __init__(self, radius):
[Link] = radius
def area(self):
return 3.14 * [Link] * [Link]
def perimeter(self):
return 2 * 3.14 * [Link]
circle = Circle(5)
print([Link]())
print([Link]())
Decorators
A decorator is a function that takes another function and extends the
behavior of this function without explicitly modifying it. It allows to add
new functionality to an existing function.
There are 2 kinds of decorators:
o Function decoratos
o Class decorators
A function is decorated with the @ symbol:
@my_decorator
def my_function():
pass
Function decorators
A decorator function takes another function as argument, wraps its
behaviour inside an inner function. and returns the wrapped function.
The decorated function no has extended functionality.
def start_end_decorator(func):
def wrapper():
print('Start')
func()
print('End')
return wrapper
def print_name():
print('Alex')
print_name()
print()
print_name = start_end_decorator(print_name)
print_name()
The decorator syntax
Instead of wrapping our function and asigning it to itself, Achieve
same thing by decorating our function with an @.
@start_end_decorator
def print_name():
print('Alex')
print_name()
What about function arguments
If our function has input arguments and we try to wrap it with our
decorator above, it will raise a TypeError since we have to call our
function inside the wrapper with this arguments, too. However, we
can fix this by using *args and **kwargs in the inner function:
def start_end_decorator_2(func):
def wrapper(*args, **kwargs):
print('Start')
func(*args, **kwargs)
print('End')
return wrapper
@start_end_decorator_2
def add_5(x):
return x + 5
result = add_5(10)
print(result)
Return values
Above, we do not get the result back, now return the value from our
inner function:
def start_end_decorator_3(func):
def wrapper(*args, **kwargs):
print('Start')
result = func(*args, **kwargs)
print('End')
return result
return wrapper
@start_end_decorator_3
def add_5(x):
return x + 5
result = add_5(10)
print(result)
What about the function identity?
print(add_5.__name__)
help(add_5)
The final template for own decorators
import functools
def my_decorator(func):
@[Link](func)
def wrapper(*args, **kwargs):
result = func(*args, **kwargs)
return result
return wrapper
Decorator function arguments
[Link] is a decorator that takes an argument for itself. We
can think of this as 2 inner functions, so an inner function within inner
function.
Another example: A repeat decorator that takes a number as input.
Within this function, we have the actual decorator function that wraps
our function and extends its behaviour within another inner function.
In this case, it repeats the input function the given number of times.
def repeat(num_times):
def decorator_repeat(func):
@[Link](func)
def wrapper(*args, **kwargs):
for _ in range(num_times):
result = func(*args, **kwargs)
return result
return wrapper
return decorator_repeat
@repeat(num_times=3)
def greet(name):
print(f"Hello {name}")
greet('Alex')
Nested Decorators
We can apply several decorators to a function by stacking them on
top of each other. The decorators are being executed in the order
they are listed.
def debug(func):
@[Link](func)
def wrapper(*args, **kwargs):
args_repr = [repr(a) for a in args]
kwargs_repr = [f"{k}={v!r}" for k, v in [Link]()]
signature = ", ".join(args_repr + kwargs_repr)
print(f"Calling {func.__name__}({signature})")
result = func(*args, **kwargs)
print(f"{func.__name__!r} returned {result!r}")
return result
return wrapper
@debug
@start_end_decorator_4
def say_hello(name):
greeting = f'Hello {name}'
print(greeting)
return greeting
say_hello(name='Alex')
Class decorators
We can also use a class as a decorator. Therefore, we have to
implement the __call__() method to make our object callable. Class
decorators are typically used to maintain a state, e.g. here we keep
track of the number of times our function is executed. The __call__
method does essentially the same thing as the wrapper() method we
have seen earlier. It adds some functionality, executes the function,
and returns its result. Note that here we use
functools.update_wrapper() instead of [Link] to preserve
the information about our function.
import functools
class CountCalls:
# the init
needs to have the func as argument and stores it
def __init__(self, func):
functools.update_wrapper(self, func)
[Link] = func
self.num_calls = 0
# extend
functionality, execute function, and return the result
def __call__(self, *args, **kwargs):
self.num_calls += 1
print(f"Call {self.num_calls} of {[Link].__name__!r}")
return [Link](*args, **kwargs)
@CountCalls
def say_hello(num):
print("Hello!")
say_hello(5)
say_hello(5)
Some typical use cases
o Use a timer decorator to calculate the execution time of a
function.
o Use a debug decorator to print out some more information
about the called function and its arguments.
o Use a check decorator to check if the arguments fulfill some
requirements and adapt the bevaviour accordingly.
o Register functions (plugins).
o Slow down code with [Link]() to check network behaviour.
o Cache the return values for memoization.
o Add information or update a state.
Common built-in Exceptions
o ImportError: If a module cannot be imported
o NameError: Try to use a variable that was not defined.
o FileNotFoundError: Try to open a file that does not exist or
specify the wrong path.
o ValueError: When an operation/ function receives an
argument that has the right type but an inappropriate value,
e.g. try to remove a value from a list that does not exist
o TypeError: Raised when an operation/ function is applied to an
object of inappropriate type.
o IndexError: Try to access an invalid index of a sequence, e.g
a list or a tuple.
o KeyError: ITry to access a non existing key of a dictionary.
Handling Exceptions
o We can use a try and except block to catch and handle
exceptions. If catch an exceptions program won't terminate,
and can continue.
o Can run multiple statements in a try block, and catch different
possible exceptions.
try:
a = 5 / 0
except:
print('some error occured.')
#2
try:
a = 5 / 0
except Exception as e:
print(e)
#3
try:
a = 5 / 0
except ZeroDivisionError:
print('Only a ZeroDivisionError is handled here')
#4
try:
a = 5 / 1
b = a + '10'
except ZeroDivisionError as e:
print('A ZeroDivisionError occured:', e)
except TypeError as e:
print('A TypeError occured:', e)
else clause
Can use an else statement that is run if no exception occured.
try:
a = 5 / 1
except ZeroDivisionError as e:
print('A ZeroDivisionError occured:', e)
else:
print('Everything is ok')
finally clause
Can use a finally statement that always runs, no matter if there was
an exception or not.
Example used to make some cleanup operations.
try:
a = 5 / 1
b = a + '10'
except ZeroDivisionError as e:
print('A ZeroDivisionError occured:', e)
except TypeError as e:
print('A TypeError occured:', e)
else:
print('Everything is ok')
finally:
print('Cleaning up some stuff...')
Define own Exceptions
Can define our own exception class that should be derived from the
built-in Exception class.
Most exceptions are defined with names that end in 'Error'. Exception
classes can be defined like any other class.
class ValueTooHighError(Exception):
pass
class ValueTooLowError(Exception):
# Add some more information for handlers
def __init__(self, message, value):
[Link] = message
[Link] = value
def test_value(a):
if a > 1000:
raise ValueTooHighError('Value is too high.')
if a < 5:
raise ValueTooLowError('Value is too low.', a)
# Note that the constructor takes 2 arguments here
return a
try:
test_value(1)
except ValueTooHighError as e:
print(e)
except ValueTooLowError as e:
print([Link], 'The value is:', [Link])
NZEC (non zero exit code)
o NZECas the name suggests occurs when your code is failed to
return 0.
o Most of the online coding platforms while testing gives input
separated by space and in those cases int(input()) is not able
to read the input properly and shows error like NZEC.
n = int(input())
k = int(input())
print(n," ",k)
n, k = raw_input().split(" ")
n = int(n)
k = int(k)
print(n," ",k)
Special Syntex
o special variable :__name__
o special method :__init__
Every time create an object it is allocated to new space.
print(__name__)
#2
#[Link]
print("Hi", __name__)
#[Link]
import cals;
print(__name__)
#3
class A:
def __init__(self, a):
self.a2=a
print("In init",self.a2)
A(3)
#4 Get address of the memory
class A:
pass
obj=A()
print(id(obj))
Copy
list1=[1,2,3,4]
list2=list1
list2[1]=1000
print(list1)
#both have same memory location
print(list2)
Shallow Copy
# shallow copy
list1=[1,2,3,4]
list2=[Link]()
list2[1]=1000
print(list1)
#both have diffrent memory locations
print(list2)
# shallow copy nested list
list1=[[1,2,3,4],[3,4,5,6]]
list2=[Link]()
list2[1][0]=1000
print(list1)
#becase items object is copied
print(list2)
# shallow copy nested list
list1=[[1,2,3,4],[3,4,5,6]]
list2=[Link]()
list2[1][0]=1000
[Link]([4,5,6,7])
print(list1)
print(list2)
#becase items is not copied
Deep Copy
deep copy, Create diffrent memory locations.
in 1D, shallow copy===deep copy
import copy
list1=[1,2,3,4]
list2=[Link](list1)
list2[1]=100
print(list1)
print(list2)
Shallow vs deep copying
Assignment statements do not create real copies. It only creates a
new variable with the same reference. So For 'real' copies we can
use the copy module. However, for compound/nested objects (e.g.
nested lists or dicts) and custom objects there is an important
difference between shallow and deep copying:
o shallow copies: Only one level deep. It creates a new
collection object and populates it with references to the nested
objects. This means modyfing a nested object in the copy
deeper than one level affects the original.
o deep copies: A full independent clone. It creates a new
collection object and then recursively populates it with copies of
the nested objects found in the original.
Assignment operation
list_a = [1, 2, 3, 4, 5]
list_b = list_a
list_a[0] = -10
print(list_a)
print(list_b)
Shallow copy
One level deep. Modifying on level 1 does not affect the other list.
Use [Link](), or object-specific copy functions/ copy
constructors.
import copy
list_a = [1, 2, 3, 4, 5]
list_b = [Link](list_a)
list_b[0] = -10
# not affects the other list
print(list_a)
print(list_b)
o But with nested objects, modifying on level 2 or deeper does
affect the other.
import copy
list_a = [[1, 2, 3, 4, 5], [6, 7, 8, 9, 10]]
list_b = [Link](list_a)
list_a[0][0]= -10
# affects the other!
print(list_a)
print(list_b)
Deep copies
Full independent clones. Use [Link]().
import copy
list_a = [[1, 2, 3, 4, 5], [6, 7, 8, 9, 10]]
list_b = [Link](list_a)
list_a[0][0]= -10
# not affects the other
print(list_a)
print(list_b)
Custom objects
Use copy module to get shallow or deep copies of custom objects.
class Person:
def __init__(self, name, age):
[Link] = name
[Link] = age
p1 = Person('Alex', 27)
# Only copies the reference
p2 = p1
[Link] = 28
print([Link])
print([Link])
Define pickling and unpickling.
Pickling is the process of converting Python objects, such as lists,
dicts, etc., into a character stream.
import pickle
mylist = ['a', 'b', 'c', 'd']
with open('[Link]', 'wb') as fh:
[Link](mylist, fh)
#2
import pickle
cars = ['A', 'B', 'C', 'D']
file = "[Link]"
fileobj = open(file, 'wb')
[Link](cars, fileobj)
[Link]()
# Second Part
file = "[Link]"
fileobj = open(file,'rb')
mycar = [Link](fileobj)
print(mycar)
print(type(mycar))
In the above code, list – “mylist” contains four elements (‘a’, ‘b’, ‘c’,
‘d’). We open the file in “wb” mode instead of “w” as all the operations
are done using bytes in the current working directory. A new file
named “[Link]” is created, which converts the mylist data in the
byte stream.
The process of retrieving the original Python objects from the stored
string representation, which is the reverse of the pickling process, is
called unpickling.
import pickle
pickle_off = open ("[Link]", "rb")
emp = [Link](pickle_off)
print(emp)
O/P: ['a', 'b', 'c', 'd']
Pickle Exceptions
Some of the common exceptions raised while dealing with pickle
module:
o [Link]: If the pickle object doesn’t support
pickling, this exception is raised.
o [Link]: In case the file contains bad/
corrupted data.
o EOFError: In case the end of file is detected, this exception is
raised.
Prons:
o Save complicated data.
o Easy to use, lighter and doesn’t require several lines of code.
o The pickled file generated is not easily readable and thus
provide some security.
Cons:
o Languages other than python may not able to reconstruct
pickled python objects.
o Risk of unpickling data from malicious sources.
Monkey patching
Change object behavior during run time.
o Monkey patching is the process of modifications that are done
to a class/ module during the runtime.
o This is done as Python supports changes in the behavior of the
program while ssbeing executed.
# [Link]
class X:
def func(self):
print "func() is being called"
import monkeyy
def monkey_f(self):
print "monkey_f() is being called"
# replacing address of “func” with “monkey_f”
[Link] = monkey_f
obj = monk.X() # calling
“func” whose address got replaced with “monkey_f()”
[Link]()
class Test:
def __init__(self,x):
self.a=x;
def get_data(self):
print("Some code to fetch data from database")
def f1(self):
self.get_data()
def f2(self):
self.get_data()
t1=Test(5)
# t1.f1()
# t1.f2()
def new_get_data(self):
print("Some code to fetch data from test data")
Test.get_data = new_get_data
print("After Monkey Patching")
t1.f1()
t1.f2()
Generators
o Generators are functions that can be paused and resumed,
returning an object that can be iterated over.
o They are lazy and thus produce items one at a time and only
when asked. Furthermore, we do not need to wait until all the
elements have been generated before we start to use them.
o They are much more memory efficient when dealing with large
datasets.
o A generator is defined like a normal function but use the yield
statement instead of return.
def my_generator():
yield 1
yield 2
yield 3
#2
def evenNumbers(n):
i=1
while n: #
Return keyword return value with control.
yield 2*i #
Yield return value but function paouse not control.
i+=1
n-=1
it=evenNumbers(10)
even_list=[]
while True:
try:
even_list.append(next(it))
# even_list += next(it)
except StopIteration:
break
print(even_list)
Execution of a generator function
Calling the function does not execute it. Instead, the function returns
a generator object which is used to control execution.
o Generator objects execute when next() is called. When calling
next() the first time, execution begins at the start of the function
and continues until the first yield statement.
o Subsequent calls to next() continue from the yield statement
(and loop around) until another yield is reached.
If yield is not called because of a condition or the end is reached, a
StopIteration exception is raised.
def countdown(num):
print('Starting')
while num > 0:
yield num
num -= 1
cd = countdown(3)
# this will not print 'Starting'
print(next(cd))
# this will print 'Starting' and the first value
print(next(cd))
# will print the next values
print(next(cd))
print(next(cd))
# this will raise a StopIteration
Generators save memory!
o Without a generator, the complete sequence has to be stored
in a list.
o With a generator, no additional sequence is needed to store
the numbers.
# without a generator
def firstn(n):
num, nums = 0, []
while num < n:
[Link](num)
num += 1
return nums
sum_of_first_n = sum(firstn(1000000))
print(sum_of_first_n)
import sys
print([Link](firstn(1000000)), "bytes")
# with a generator
def firstn(n):
num = 0
while num < n:
yield num
num += 1
sum_of_first_n = sum(firstn(1000000))
print(sum_of_first_n)
import sys
print([Link](firstn(1000000)), "bytes")
Another example: Fibonacci numbers
def fibonacci(limit):
a, b = 0, 1
while a < limit:
yield a
a, b = b, a + b
fib = fibonacci(30)
print(list(fib)) #
generator objects can be converted to a list
Generator expressions
o Like list, generators can be written in the same syntax except
with parenthesis instead of square brackets.
o Generator expressions are slower than list because of the
overhead of function calls.
mygenerator = (i for i in range(1000) if i % 2 == 0) #
generator expression
print([Link](mygenerator))
mylist = [i for i in range(1000) if i % 2 == 0] #
list comprehension
print([Link](mylist))
Concept behind a generator
It has to implement __iter__ and __next__ to make it iterable, keep,
and take care of a StopIteration.
class firstn:
def __init__(self, n):
self.n = n
[Link] = 0
def __iter__(self):
return self
def __next__(self):
if [Link] < self.n:
cur = [Link]
[Link] += 1
return cur
else:
raise StopIteration()
firstn_object = firstn(1000000)
print(sum(firstn_object))
Threading vs Multiprocessing
We have two common approaches to run code in parallel (achieve
multitasking)
o threads
o multiple processes
Process
A Process is an instance of a program. They are independent from
each other and do not share the same memory.
Key facts:
o A new process is started independently from the first process.
o Takes advantage of multiple CPUs and cores.
o Separate memory space.
o Memory is not shared between processes.
o One GIL (Global interpreter lock) for each process, i.e. avoids
GIL limitation.
o Great for CPU-bound processing.
o Child processes are interruptable/ killable.
o Starting a process is slower than starting a thread.
o Larger memory footprint.
o IPC (inter-process communication) is more complicated.
Threads
A thread is an entity within a process that can be scheduled for
execution. A Process can spawn multiple threads. The main
difference is that all threads within a process share the same
memory.
Key facts:
o Multiple threads can be spawned within one process.
o Memory is shared between all threads.
o Starting a thread is faster than starting a process.
o Great for I/O-bound tasks.
o Leightweight - low memory footprint.
o One GIL for all threads, i.e. threads are limited by GIL.
o Multithreading has no effect for CPU-bound tasks due to the
GIL.
o Not interruptible/ killable - be careful with memory leaks.
o increased potential for race conditions.
Threading in Python
Use the threading module.
Note: The following example usually won't benefit from multiple
threads since it is CPU-bound. It should just show the example of
how to use threads.
from time import sleep
from threading import *
class Hello(Thread):
def run(self):
for i in range(500):
print("Hello")
sleep(1)
class Hi(Thread):
def run(self):
for i in range(500):
print("Hi")
sleep(1)
t1=Hello()
t2=Hi()
[Link]()
sleep(0.2)
[Link]()
[Link]()
[Link]()
print('By')
When is Threading useful
o Useful for I/O-bound tasks when program has to talk to slow
devices like a hard drive or a network connection.
o With threading the program can use the time waiting for these
devices and do other tasks in the meantime.
Multiprocessing
Create a process with [Link](). It takes two
important arguments:
o target: a callable object (function) for this process to be
invoked when the process starts
o args: the (function) arguments for the target function. This
must be a tuple
o Start a process with [Link]().
o Call [Link]() to tell the program that it should wait for this
process to complete before it continues with the rest of the
code.
from multiprocessing import Process
import os
def sqrt():
for i in range(1000):
result = i * i
print(result)
if __name__ == "__main__":
processes = []
num = os.cpu_count() #
number of CPUs on the machine.
for i in range(num): # create
processes and asign a function for each process
process = Process(target=sqrt)
[Link](process)
for process in processes:
# start all processes
[Link]()
# wait for
all processes to finish
# block the
main programm until these processes are finished
for process in processes:
[Link]()
When is Multiprocessing useful
It is useful for CPU-bound tasks that have to do a lot of CPU
operations for a large amount of data and require a lot of computation
time. With multiprocessing we can split the data into equal parts and
do parallel computing on different CPUs.
Ex. Calculate the square numbers for all numbers from 1 to 1000000.
Divide the numbers into equal sized parts and use a process for each
subset.
GIL - Global interpreter lock
This is a mutex (or a lock) that allows only one thread to hold control
of the Python interpreter. This means that the GIL allows only one
thread to execute at a time even in a multi-threaded architecture.
Why is it needed?
It is needed because CPython's memory management is not thread-
safe. Python uses reference counting for memory management. It
means that objects created in Python have a reference count variable
that keeps track of the number of references that point to the object.
When this count reaches zero, the memory occupied by the object is
released. The problem was that this reference count variable needed
protection from race conditions where two threads increase/
decrease its value simultaneously. If this happens, it can cause either
leaked memory that is never released or incorrectly release the
memory while a reference to that object still exists.
How to avoid the GIL
o Avoid GIL by using multiprocessing instead of threading.
o Avoid the CPython implementation and use a free-threaded
Python implementation like Jython or IronPython.
o Move parts of the application out into binary extensions
modules, i.e. use Python as a wrapper for third party libraries
(e.g. in C/C++). This is the path taken by numypy and scipy.
Share data between threads
Task: Create two threads, each thread should access the current
database value, modify it, and write the new value back into the
database value. Each thread should do this operation 10 times.
from threading import Thread
import time
database_value = 0
def increase():
global database_value #
needed to modify the global value
local_copy = database_value #
get a local copy (simulate data retrieving)
local_copy += 1 #
simulate some modifying operation
[Link](0.1)
database_value = local_copy #
write the calculated new value into the global variable
if __name__ == "__main__":
print('Start value: ', database_value)
t1 = Thread(target=increase)
t2 = Thread(target=increase)
[Link]()
[Link]()
[Link]()
[Link]()
print('End value:', database_value)
print('end main')
Share data between processes
Since processes don't live in the same memory space, they do not
have access to the same (public) data. Thus, they need special
shared memory objects to share data.
Data can be stored in a shared memory variable using Value or
Array.
o Value(type, value): Create a ctypes object of type type.
Access the value with .target.
o Array(type, value): Create a ctypes array with elements of
type type. Access the values with [].
Task: Create two processes, each process should have access to a
shared variable and modify it (Only increase it repeatedly by 1 for
100 times). Create another two processes that share an array and
modify all the elements in the array.
from multiprocessing import Process, Value, Array
import time
def add_100(number):
for _ in range(100):
[Link](0.01)
[Link] += 1
def add_100_array(numbers):
for _ in range(100):
[Link](0.01)
for i in range(len(numbers)):
numbers[i] += 1
if __name__ == "__main__":
shared_number = Value('i', 0)
print('Value at beginning:', shared_number.value)
shared_array = Array('d', [0.0, 100.0, 200.0])
print('Array at beginning:', shared_array[:])
process1 = Process(target=add_100, args=(shared_number,))
process2 = Process(target=add_100, args=(shared_number,))
process3 = Process(target=add_100_array, args=(shared_array,))
process4 = Process(target=add_100_array, args=(shared_array,))
[Link]()
[Link]()
[Link]()
[Link]()
[Link]()
[Link]()
[Link]()
[Link]()
print('Value at end:', shared_number.value)
print('Array at end:', shared_array[:])
print('end main')
How to use Locks
In the above example (Share data between threads), the 2 threads
should increment the value by 1, so 2 increment operations are
performed. But why is the end value 1 and not 2?
Race condition:
A race condition happened here. A race condition occurs when two/
more threads can access shared data and they try to change it at the
same time. Because the thread scheduling algorithm can swap
between threads at any time, we don't know the order in which the
threads will attempt to access the shared data. In our case, the first
thread accesses the database_value (0) and stores it in a local copy.
It then increments it (local_copy is now 1). With our [Link]()
function that just simulates some time consuming operations, the
programm will swap to the second thread in the meantime. This will
also retrieve the current database_value (still 0) and increment the
local_copy to 1. Now both threads have a local copy with value 1, so
both will write the 1 into the global database_value. This is why the
end value is 1 and not 2.
Avoid race conditions with Locks
A lock (mutex) is a synchronization mechanism for enforcing limits on
access to a resource in an environment where there are many
threads of execution.
A Lock has two states:
o Locked
o Unlocked
If the state is locked, it does not allow other concurrent threads to
enter this code section until the state is unlocked again.
Two functions are important:
o [Link](): This will lock the state and block
o [Link](): This will unlock the state again.
N: We should always release the block again after it was acquired!.
Using Queues in Python
Queues can be used for thread-safe/ process-safe data exchanges
and data processing both in a multithreaded and a multiprocessing
environment.
The queue
A queue is a linear data structure that follows the FIFO principle.
from queue import Queue
q = Queue()
# create queue
[Link](1)
[Link](2)
[Link](3)
first = [Link]()
print(first)
Using a queue in multithreading
Operations with a queue are thread-safe.
o [Link](): Remove and return the first item. By default, it blocks
until the item is available.
o [Link](item): Puts element at the end of the queue. By default,
it blocks until a free slot is available.
o q.task_done(): Indicate that a formerly enqueued task is
complete. For each get() we should call this after we are done
with our task for this item.
o [Link](): Blocks until all items in the queue have been gotten
and proccessed (task_done() has been called for each item).
o [Link](): Return True if the queue is empty.
The following example uses a queue to exchange numbers from
0...19. Each thread invokes the worker method. Inside the infinite
loop the thread is waiting until items are available due to the blocking
[Link]() call. When items are available, they are processed and then
q.task_done() tells the queue that processing is complete.
In the main thread, 10 daemon threads are created. This means that
they automatically die when the main thread dies, and thus the
worker method and infinite loop is no longer invoked. Then the queue
is filled with items and the worker method can continue with available
items.
At the end [Link]() is necessary to block the main thread until all items
have been gotten and proccessed.
If all tasks are done, [Link]() can unblock
from threading import Thread, Lock, current_thread
from queue import Queue
def worker(q, lock):
while True:
value = [Link]()
# blocks until the item is available
# do stuff...
with lock:
print(f"in {current_thread().name} got {value}")
# prevent printing at the same time with this lock
q.task_done()
if __name__ == '__main__':
q = Queue()
num_threads = 10
lock = Lock()
for i in range(num_threads):
t = Thread(name=f"Thread{i+1}", target=worker, args=(q,
lock))
[Link] = True
# dies when the main thread dies
[Link]()
for x in range(20):
# fill the queue with items
[Link](x)
[Link]() # Blocks until all
items in the queue have been gotten and processed.
print('main done')
Daemon threads
Daemon threads are background threads that automatically die when
the main program ends. This is why the infinite loops inside the
worker methods can be exited. Without a daemon process we would
have to use a signalling mechanism such as a [Link] to
stop the worker.
Daemon thread abruptly stopped and their resources (e.g. open files
or database transactions) may not be released/ completed properly.
Use the lock as a context manager
After [Link]() we should never forget to call [Link]() to
unblock the code. We can also use a lock as a context manager,
wich will safely lock and unlock our code. It is recommended to use a
lock this way:
def add_100(number, lock):
for _ in range(100):
[Link](0.01)
with lock:
[Link] += 1
Process Pools
A process pool object controls a pool of worker processes to which
jobs can be submitted It supports asynchronous results with timeouts
and callbacks and has a parallel map implementation. It can
automatically manage the available processors and split data into
smaller chunks which can then be processed in parallel by different
processes.
o map(func, iterable[, chunksize]): This method chops the
iterable into a number of chunks which it submits to the
process pool as separate tasks. The size of these chunks can
be specified by setting chunk size to a positive integer. It blocks
until the result is ready.
o close(): Prevents any more tasks from being submitted to the
pool. Once all the tasks have been completed the worker
processes will exit.
o join(): Wait for the worker processes to exit. One must call
close() or terminate() before using join().
o apply(func, args): Call func with arguments args. It blocks
until the result is ready. func is only executed in ONE of the
workers of the pool.
Note: There are also asynchronous variants map_async() and
apply_async() that will not block. They can execute callbacks when
the results are ready.
from multiprocessing import Pool
def cube(number):
return number * number * number
if __name__ == "__main__":
numbers = range(10)
p = Pool() # by default
this allocates the maximum number of available
# processors
for this task --> os.cpu_count().
result = [Link](cube, numbers)
[Link]()
[Link]()
print(result) # result =
[[Link](cube, args=(i,)) for i in numbers].
1. Containers
o Containers are object that hold objects. They provide a way to
access the contained objects and iterate over them.
o A Counter is a subclass of dict. Therefore it is an unordered
collection where elements and their respective count are stored
as a dictionary.
o A counter is a container that stores elements as dictionary
keys, and their counts are stored as dictionary values.
Different ways to create Counter.
from collections import Counter
print(Counter(['B','B','A','B','C','A','B','B','A','C']))
# With sequence of items
print(Counter({'A':3, 'B':5, 'C':2}))
# with dictionary
print(Counter(A=3, B=5, C=2))
# with keyword arguments
#2 Updation
coun = [Link]()
# also create an empty counter
[Link](Data)
# And can be updated via update() method
3. counter’s data will be increased not replaced
o Counts can be zero and negative also.
o We can use Counter to count distinct elements of a list or other
collections.
from collections import Counter
c1 = Counter(A=4, B=3, C=10)
c2 = Counter(A=10, B=3, C=4)
[Link](c2)
print(c1)
z = ['blue', 'red', 'blue', 'yellow', 'blue', 'red']
print(Counter(z))
4. OrderedDict
o An OrderedDict is a dictionary subclass that remembers the
order that keys were first inserted. The only difference between
dict() and OrderedDict() is that.
o OrderedDict preserves the order in which the keys are inserted.
A regular dict doesn’t track the insertion order, and iterating it
gives the values in an arbitrary order.
from collections import OrderedDict
print("This is a Dict:")
d = {}
d['a'] = 1
d['b'] = 2
d['c'] = 3
d['d'] = 4
for key, value in [Link]():
print(key, value)
print("This is an Ordered Dict:")
od = OrderedDict()
od['a'] = 1
od['b'] = 2
od['c'] = 3
od['d'] = 4
for key, value in [Link]():
print(key, value)
Key value Change: If the value of a certain key is changed, the
position of the key remains unchanged in OrderedDict.
A Python program to demonstrate working of key/ value change
in OrderedDict
from collections import OrderedDict
print("Befor")
od = OrderedDict()
od['a'] = 1
od['b'] = 2
od['c'] = 3
od['d'] = 4
for key, value in [Link]():
print(key, value)
print("After:")
od['c'] = 5
for key, value in [Link]():
print(key, value)
Deletion and Re-Inserting:
Deleting and re-inserting the same key will push it to the back as
OrderedDict however maintains the order of insertion.
from collections import OrderedDict
print("Before deleting:")
od = OrderedDict()
od['a'] = 1
od['b'] = 2
od['c'] = 3
od['d'] = 4
for key, value in [Link]():
print(key, value)
print("After deleting:")
[Link]('c')
for key, value in [Link]():
print(key, value)
print("After re-inserting:")
od['c'] = 3
for key, value in [Link]():
print(key, value)
Defaultdict
o Defaultdict is a sub-class of the dict class that returns a
dictionary-like object. The functionality of both dictionaries and
defualtdict are almost same except for the fact that defualtdict
never raises a KeyError. It provides a default value for the key
that does not exists.
o when the KeyError is raised, it might become a problem. To
overcome this use Defaultdict.
from collections import defaultdict
# Function to return a default values for keys that is not present
def def_value():
return "Not Present"
d = defaultdict(def_value)
d["a"] = 1
d["b"] = 2
print(d["a"])
print(d["b"])
print(d["c"])
__missing__()
This function is used to provide the default value for the dictionary.
This function takes default_factory as an argument and if this
argument is None, a KeyError is raised otherwise it provides a default
value for the given key. This method is basically called by the
__getitem__() method of the dict class when the requested key is not
found. __getitem__() return the value returned by the __missing__().
method.
from collections import defaultdict
d = defaultdict(lambda: "Not Present")
d["a"] = 1
d["b"] = 2
#Provides the default value for the key
print(d.__missing__('a'))
print(d.__missing__('d'))
Supports a dictionary like a container called UserDict present in the
collections module. This class acts as a wrapper class around the
dictionary objects. This class is useful when one wants to create a
dictionary of their own with some modified/ new functionality. It can
be considered as a way of adding new behaviors for the dictionary.
This class takes a dictionary instance as an argument and simulates
a dictionary that is kept in a regular dictionary. The dictionary is
accessible by the data attribute of this class.
from collections import UserDict
d = {'a':1, 'b': 2, 'c': 3}
userD = UserDict(d)
#Creating an UserDict
print([Link])
create a class inherting from UserDict to implement a
customised dictionary.
from collections import UserDict
class MyDict(UserDict):
# Creating a Dictionary where deletion is not allowed
def __del__(self):
# Function to stop deleltion from dictionary
raise RuntimeError("Deletion not allowed")
def pop(self, s=None):
# Function to stop pop from dictionary
raise RuntimeError("Deletion not allowed")
def popitem(self, s=None):
# Function to stop popitem from Dictionary
raise RuntimeError("Deletion not allowed")
d = MyDict({'a': 1, 'b': 2, 'c': 3})
print(d)
[Link](1)
[Link]
This class acts as a wrapper class around the List objects. This class
is useful when one wants to create a list of their own with some
modified/ new functionality. It can be considered as a way of adding
new behaviors for the list. This class takes a list instance as an
argument and simulates a list that is kept in a regular list.
from collections import UserList
L = [1, 2, 3, 4]
userL = UserList(L)
print([Link])
[Link]
o Strings are the arrays of bytes representing Unicode
characters. However, Python does not support the character
data type. A character is a string of length one.
o This class is useful when one wants to create a string of their
own with some modified/ new functionality.
from collections import UserString
d = 12344
userS = UserString(d)
print([Link])
userS = UserString("")
# Creating an empty UserDict
print([Link])
#2
from collections import UserString
# Creating a Mutable String
class Mystring(UserString):
def append(self, s):
[Link] += s
def remove(self, s):
[Link] = [Link](s, "")
s1 = Mystring("Geeks")
print("Original String:", [Link])
[Link]("s")
print("String After Appending:", [Link])
[Link]("e")
print("String after Removing:", [Link])
Collections
Collections in Python implements specialized container datatypes
providing alternatives to Python’s general purpose built-in containers,
dict, list, set, and tuple.
The following tools exist:
o namedtuple:factory function for creating tuple subclasses with
named fields
o OrderedDict:dict subclass that remembers the order entries
were added
o Counter:dict subclass for counting hashable objects
o defaultdict:dict subclass that calls a factory function to supply
missing values
o deque:list-like container with fast appends and pops on either
end
2. namedtuple
namedtuples are easy to create, lightweight object types. They
assign meaning to each position in a tuple and allow for more
readable, self-documenting code. They can be used wherever
regular tuples are used, and they add the ability to access fields by
name instead of position index.
create a namedtuple with its class name as string and its fields as
string fields have to be separated by comma or space in the given
string.
from collections import namedtuple
Point = namedtuple('Point','x, y')
pt = Point(1, -4)
print(pt)
print(pt._fields)
print(type(pt))
print(pt.x, pt.y)
Person = namedtuple('Person','name, age')
friend = Person(name='Tom', age=25)
print([Link], [Link])
deque
A deque is a double-ended queue. It can be used to add/ remove
elements from both ends. Deques support thread safe, memory
efficient appends and pops from either side of the deque with
approximately the same O(1) performance in either direction.
various Operations on deque :
o append() :Used to insert the value in its argument to the right
end of deque.
o appendleft() :Used to insert the value in its argument to the left
end of deque.
o pop() :Used to delete an argument from the right end of deque.
o popleft() :Used to delete an argument from the left end of
deque.
o index(ele, beg, end) :Returns the first index of the value
mentioned in arguments, starting searching from beg till end
index.
o insert(i, a) :This function inserts the value mentioned in
arguments(a) at index(i) specified in arguments.
o remove() :Removes the first occurrence of value mentioned in
arguments.
o count() :Counts the number of occurrences of value mentioned
in arguments.
o extend(iterable) :Used to add multiple values at the right end
of deque. The argument passed is an iterable.
o extendleft(iterable) :Used to add multiple values at the left
end of deque. The argument passed is an iterable. Order is
reversed as a result of left appends.
o reverse() :Used to reverse order of deque elements.
o rotate() :Rotates the deque by the number specified in
arguments. If the number specified is negative, rotation occurs
to left. Else rotation is to right.
from collections import deque
d = deque()
[Link]('a')
# add elements to the right end
[Link]('c')
# add elements to the left end
print([Link]())
# remove elements from the right
print([Link]())
# remove elements from the left
[Link]()
print(d)
d = deque(['a', 'b', 'c', 'd'])
[Link](['e', 'f', 'g'])
# extend at right or left side
[Link](['h', 'i', 'j'])
# 'j' is now at the left most position
print(d)
print([Link]('h'))
[Link](1)
# rotate 1 positions to the right
[Link](-2)
# rotate 2 positions to the left
print(d)
ChainMap
encapsulates many dictionaries into one unit. ChainMap is member
of module “collections“.
from collections import ChainMap
d1 = {'a': 1, 'b': 2}
d2 = {'c': 3, 'd': 4}
d3 = {'e': 5, 'f': 6}
c = ChainMap(d1, d2, d3)
print(c)
Access Operations
o keys() :Used to display all the keys of all the dictionaries in
ChainMap.
o values() :Used to display values of all the dictionaries in
ChainMap.
o maps() :Used to display keys with corresponding values of all
the dictionaries in ChainMap.
import collections
dic1 = { 'a' : 1, 'b' : 2 }
dic2 = { 'b' : 3, 'c' : 4 }
chain = [Link](dic1, dic2)
print ([Link])
print (list([Link]()))
# printing keys using keys()
print (list([Link]()))
Note: The key named “b” exists in both dictionaries, but only first
dictionary key is taken as key value of “b”. Ordering is done as the
dictionaries are passed in function.
Manipulating Operations
o new_child() :Adds a new dictionary in the beginning of the
ChainMap.
o reversed() :Reverses the relative ordering of dictionaries in the
ChainMap.
import collections
dic1 = { 'a' : 1, 'b' : 2 }
dic2 = { 'b' : 3, 'c' : 4 }
dic3 = { 'f' : 5 }
chain = [Link](dic1, dic2)
print ([Link])
chain1 = chain.new_child(dic3)
print ([Link])
print (chain1['b'])
[Link] = reversed([Link])
print (chain1['b'])
Heap queue (heapq)
Heap data structure is mainly used to represent a priority queue.
The property of this data structure in Python is that each time the
smallest of heap element is popped(min heap). Whenever elements
are pushed/ popped, heap structure in maintained. The heap[0]
element also returns the smallest element each time.
o heapify(iterable) :Used to convert the iterable into a heap data
structure. i.e. in heap order.
o heappush(heap, ele) :Used to insert the element mentioned in
its arguments into heap. The order is adjusted, so as heap
structure is maintained.
o heappop(heap) :Used to remove and return the smallest
element from heap. The order is adjusted, so as heap structure
is maintained.
o heappushpop(heap, ele) :This function combines the
functioning of both push and pop operations in one statement,
increasing efficiency. Heap order is maintained after this
operation.
o heapreplace(heap, ele) :This function also inserts and pops
element in one statement, but it is different from above
function. In this, element is first popped, then the element is
pushed.i.e, the value larger than the pushed value can be
returned. heapreplace() returns the smallest value originally in
heap regardless of the pushed element as opposed to
heappushpop().
o nlargest(k, iterable, key = fun) :Used to return the k largest
elements from the iterable specified and satisfying the key if
mentioned.
o nsmallest(k, iterable, key = fun) :Used to return the k
smallest elements from the iterable specified and satisfying the
key if mentioned.
o Using heapify to convert list into heap.
o Using heappush() to push elements into heap.
o Using heappop() to pop smallest element.
o Using heappushpop() to push and pop items simultaneously.
o Using heapreplace() to push and pop items simultaneously.
import heapq
li = [5, 7, 9, 1, 3]
[Link](li)
[Link](li,4)
print (list(li))
print ([Link](li))
//2
li1 = [5, 7, 9, 4, 3]
li2 = [5, 7, 9, 4, 3]
[Link](li1)
print ([Link](li1, 2))
print ([Link](li2, 2))
print([Link](3, li1))
print([Link](3, li1))
1. Log Level
There are 5 different log levels indicating the serverity of events. By
default, the system logs only events with level WARNING and above.
import logging
[Link]('This is a debug message')
[Link]('This is an info message')
[Link]('This is a warning message')
[Link]('This is an error message')
[Link]('This is a critical message')
o/p:
WARNING:root:This is a warning message
ERROR:root:This is an error message
CRITICAL:root:This is a critical message
2. Logging in modules and logger hierarchy
o To create an internal logger using the __name__ global
variable.
o This will create a logger with the name of our module and
ensures no name collisions. The logging module creates a
hierarchy of loggers, starting with the root logger, and adding
the new logger to this hierarchy.
o If import our module in another module, log messages can be
associated with the correct module through the logger name.
N: Changing the basicConfig of the root logger will also affect the log
events of the other (lower) loggers in the hierarchy.
# [Link]
import logging
logger = [Link](__name__)
[Link]('HELLO')
# [Link]
import logging
[Link](level=[Link], format='%(name)s -
%(levelname)s - %(message)s')
import helper
# o/p:
# helper - INFO - HELLO
3. Propagation
o By default, all created loggers will pass the log events to the
handlers of higher loggers, in addition to any handlers attached
to the created logger.
o We can deactivate this by setting propagate = False.
# [Link]
import logging
logger = [Link](__name__)
[Link] = False
[Link]('HELLO')
# [Link]
import logging
[Link](level=[Link], format='%(name)s -
%(levelname)s - %(message)s')
import helper
No output when running [Link] since the helper module logger
does not propagate its messages to the root logger.
4. LogHandlers
Handler objects are responsible for dispatching the appropriate log
messages to the handler's specific destination.
For example can use different handlers to send log messaged to the
standard output stream, to files, via HTTP, or via Email. Typically you
configure each handler with a
o level (setLevel())
o formatter (setFormatter())
o and optionally a filter (addFilter())
import logging
logger = [Link](__name__)
stream_handler = [Link]()
# Create handlers
file_handler = [Link]('[Link]')
#
Configure level and formatter and add it to handlers
stream_handler.setLevel([Link])
# warning and above is logged to the stream
file_handler.setLevel([Link])
# error and above is logged to a file
stream_format = [Link]('%(name)s - %(levelname)s -
%(message)s')
file_format = [Link]('%(asctime)s - %(name)s -
%(levelname)s - %(message)s')
stream_handler.setFormatter(stream_format)
file_handler.setFormatter(file_format)
[Link](stream_handler)
# Add handlers to the logger
[Link](file_handler)
[Link]('This is a warning')
# logged to the stream
[Link]('This is an error')
# logged to the stream AND the file!
Example of a filter
overwrite InfoFilter() method. Only log records for which this function
evaluates to True will pass the filter.
class InfoFilter([Link]):
def filter(self, record):
return [Link] == [Link]
# Now only INFO level messages will be logged
stream_handler.addFilter(InfoFilter())
[Link](stream_handler)
5. Capture Stack traces
Logging the traceback in our exception logs can be very helpful for
trouble shooting issues. W can capture the traceback in
[Link]() by setting the exc_info = True.
import logging
try:
a = [1, 2, 3]
value = a[3]
except IndexError as e:
[Link](e)
[Link](e, exc_info=True)
o/p:
ERROR:root:list index out of range
ERROR:root:list index out of range
Traceback (most recent call last):
File "<ipython-input-6-df97a133cbe6>", line 5, in <module>
value = a[3]
IndexError: list index out of range
If don't capture the correct Exception, you can also use
the traceback.format_exc() method to log the exception.
import logging
import traceback
try:
a = [1, 2, 3]
value = a[3]
except:
[Link]("uncaught exception: %s", traceback.format_exc())
6. Rotating FileHandler
When we have a large application that logs many events to a file, and
only need to keep track of the most recent events, then use
a RotatingFileHandler that keeps the files small. When the log
reaches a certain number of bytes, it gets "rolled over". We can also
keep multiple backup log files before overwriting them.
import logging
from [Link] import RotatingFileHandler
logger = [Link](__name__)
[Link]([Link])
handler = RotatingFileHandler('[Link]', maxBytes=2000,
backupCount=5)
[Link](handler) # roll over after 2KB,
and keep backup logs [Link].1, [Link].2 ,...
for _ in range(10000):
[Link]('Hello, world!')
7. TimedRotatingFileHandler
If our application will be running for a long time, Can use a
TimedRotatingFileHandler. This will create a rotating log based on
how much time has passed. Possible time conditions.
o second (s)
o minute (m)
o hour (h)
o day (d)
o w0-w6 (weekday, 0=Monday)
o midnight
import logging
import time
from [Link] import TimedRotatingFileHandler
logger = [Link](__name__)
[Link]([Link])
# This will create a new log file every minute, and 5 backup files
with a timestamp before overwriting old logs.
handler = TimedRotatingFileHandler('timed_test.log', when='m',
interval=1, backupCount=5)
[Link](handler)
for i in range(6):
[Link]('Hello, world!')
[Link](50)
8. Logging in JSON Format
If our application generates many logs from different modules, and
especially in a microservice architecture, it can be challenging to
locate the important logs for our analysis.
Therefore, log our messages in JSON format, and send them to a
centralized log management system. Then can analyze log records.
import logging
from pythonjsonlogger import jsonlogger
logger = [Link]()
logHandler = [Link]()
formatter = [Link]()
[Link](formatter)
[Link](logHandler)
9. Context managers and the 'with' statement
Context managers are a great tool for resource management. They
allow to allocate and release resources precisely when want to.
Ex. with open() statemtent:
with open('[Link]', 'w') as f:
[Link]('some todo...')
This will open a file and makes sure to automatically close it after
program execution leaves the context of the with statement. It also
handles exceptions and makes sure to properly close the file even in
case of an exception.
Examples of context managers
o Open and close files
o open and close database connections
o Acquire and release locks:
10. Implementing a context manager as a class
To support the with statement for our own classes, we have to
implement the __enter__ and __exit__ methods. Python calls
__enter__ when execution enters the context of the with statement.
Here the resource should be acquired and returned. When execution
leaves the context again, __exit__ is called and the resource is freed
up.
class ManagedFile:
def __init__(self, filename):
print('init', filename)
[Link] = filename
def __enter__(self):
print('enter')
[Link] = open([Link], 'w')
return [Link]
def __exit__(self, exc_type, exc_value, exc_traceback):
if [Link]:
[Link]()
print('exit')
with ManagedFile('[Link]') as f:
print('doing stuff...')
[Link]('some todo...')
11. Implementing a context manager as a generator
Instead of writing a class, we can also write a generator function and
decorate it with [Link] decorator. Then we
can also call the function using a with statement.
For this approach, the function must yield the resource in a try
statement, and all the content of the __exit__ method to free up the
resource goes now inside the corresponding finally statement.
from contextlib import contextmanager
@contextmanager
def open_managed_file(filename):
f = open(filename, 'w')
try:
yield f
finally:
[Link]()
with open_managed_file('[Link]') as f:
[Link]('some todo...')
The generator first acquires the resource. Then temporarily suspends
its own execution and yields the resource so it can be used by the
caller. When the caller leaves the with context, the generator
continues to execute and frees up the resource in the finally
statement.
Password validation Without using Regex
def password_check(passwd):
SpecialSym = ['$', '@', '#', '%']
val = True
if len(passwd) < 6:
print('length should be 6')
val = False
if len(passwd) > 20:
print('length should not > 20')
val = False
if not any([Link]() for char in passwd):
print('Password should have at least one numeral')
val = False
if not any([Link]() for char in passwd):
print('Password should have at least one uppercase letter')
val = False
if not any([Link]() for char in passwd):
print('Password should have at least one lowercase letter')
val = False
if not any(char in SpecialSym for char in passwd):
print('Password should have at least one of the symbols $@#')
val = False
if val:
return val
def main():
passwd = 'Geek12@'
if (password_check(passwd)):
print("Password is valid")
else:
print("Invalid Password !!")
if __name__ == '__main__':
main()
Using regex
import re
def main():
passwd = 'Geek12@'
reg = "^(?=.*[a-z])(?=.*[A-Z])(?=.*d)(?=.*[@$!%*#?&])[A-Za-
zd@$!#%*?&]{6,20}$"
pat = [Link](reg)
# compiling regex
mat = [Link](pat, passwd)
# searching regex
if mat:
print("Password is valid.")
else:
print("Password invalid !!")
if __name__ == '__main__':
main()
Password Generator
import random
letters = ['a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k',
'l', 'm', 'n', 'o', 'p', 'q', 'r', 's', 't', 'u',
'v', 'w', 'x', 'y', 'z', 'A', 'B', 'C', 'D', 'E', 'F', 'G',
'H', 'I', 'J', 'K', 'L', 'M', 'N', 'O', 'P', 'Q', 'R',
'S', 'T', 'U', 'V', 'W', 'X', 'Y', 'Z']
numbers = ['0', '1', '2', '3', '4', '5', '6', '7', '8', '9']
symbols = ['!', '#', '$', '%', '&', '(', ')', '*', '+']
print("Welcome to the PyPassword Generator!")
nr_letters = int(input("How many letters would you like in your
password?: "))
nr_symbols = int(input(f"How many symbols would you like?: "))
nr_numbers = int(input(f"How many numbers would you like?: "))
#Eazy Level
password = ""
for char in range(1, nr_letters + 1):
password += [Link](letters)
for char in range(1, nr_symbols + 1):
password += [Link](symbols)
for char in range(1, nr_numbers + 1):
password += [Link](numbers)
print(password)
#Hard Level
password_list = []
for char in range(1, nr_letters + 1):
password_list.append([Link](letters))
for char in range(1, nr_symbols + 1):
password_list += [Link](symbols)
for char in range(1, nr_numbers + 1):
password_list += [Link](numbers)
[Link](password_list)
#print(password_list)
password = ""
for char in password_list:
password += char
print(f"Your password is: {password}")
Searching an occurrence of pattern
o [Link](): either returns None (if the pattern doesn’t match),
or a [Link] that contains information about the
matching part of the string. This method stops after the first
match, so this is best suited for testing a regular expression
more than extracting data.
o [Link](): Matching a Pattern with Text.
o [Link](): Finding all occurrences of a pattern. Return all
non-overlapping matches of pattern in string.
import re
regex = r"([a-zA-Z]+) (d+)"
match = [Link](regex, "I was born on June 24")
if match != None:
print ("Match at index %s, %s" % ([Link](), [Link]()))
print ("Full match: %s" % ([Link](0)))
print ("Month: %s" % ([Link](1)))
print ("Day: %s" % ([Link](2)))
else:
print ("The regex pattern does not match.")
#2
def findMonthAndDate(string):
regex = r"([a-zA-Z]+) (d+)"
match = [Link](regex, string)
if match == None:
print("Not a valid date")
return
print("Given Data: %s" % ([Link]()))
print("Month: %s" % ([Link](1)))
print("Day: %s" % ([Link](2)))
findMonthAndDate("Jun 24")
print("")
findMonthAndDate("I was born on June 24")
#3
string = """Hello my Number is 123456789 and
my friend's number is 987654321"""
regex = 'd+'
match = [Link](regex, string)
print(match)
Syntex
o Function compile(): Regular expressions are compiled into
pattern objects, which have methods for various operations
such as searching for pattern matches or performing string
substitutions.
o compile(): creates regular expression character class [a-e],
which is equivalent to [abcde].
o findall(): searches for the Regular Expression and return a list
upon finding.
o Set class [s,.] will match any whitespace character, ‘,’, or,’.’ .
o :d is equivalent to [0-9].
o :d+ match a group on [0-9], group of one or greater size.
o w: equivalent to [a-zA-Z0-9_].
o w+: matches to group of alphanumeric character.
o W: matches to non alphanumeric characters.
o W+: denotes Non-Alphanumeric Characters or group of
characters.
o ub: matches the string at "Subject" and "Uber".
o escape(): returns a string with BackSlash '', before every Non-
Alphanumeric Character.
o *: replaces the no. of occurrence of a character.
[] Represent a character class
^ Matches the beginning
$ Matches the end
. Matches any character except newline
? Matches zero or one occurrence.
| Means OR (Matches with any of the characters separated by it.
* Any number of occurrences (including 0 occurrences)
+ One or more occurrences
{} Indicate number of occurrences of a preceding RE to match.
() Enclose a group of REs
#2
# class [abcde] will match with string with 'a', 'b', 'c', 'd', 'e'.
p = [Link]('[a-e]')
#3
print([Link]("Aye, said Mr. Gibenson Stark"))
#4
p = [Link]('d')
print([Link]("I went to him at 11 A.M. on 4th July 1886"))
p = [Link]('d+')
print([Link]("I went to him at 11 A.M. on 4th July 1886"))
#5
p = [Link]('w')
print([Link]("He said * in some_lang."))
p = [Link]('w+')
print([Link]("I went to him at 11 A.M., he said *** in
some_language."))
p = [Link]('W')
print([Link]("he said *** in some_language."))
#6
p = [Link]('ab*')
print([Link]("ababbaabbb"))
#7
from re import split
print(split('W+', 'Words, words , Words'))
print(split('W+', "Word's words Words"))
print(split('W+', 'On 12th Jan 2016, at 11:02 AM'))
print(split('d+', 'On 12th Jan 2016, at 11:02 AM'))
#Splitting occurs at '12', '2016', '11', '02' only
#8
Function sub()
print([Link]('ub', '~*' , 'Subject has Uber booked already', flags =
[Link]))
print([Link]('ub', '~*' , 'Subject has Uber booked already')) #
Case Sensitivity, 'Ub' in "Uber", will not be reaplced.
print([Link]('ub', '~*' , 'Subject', count=1, flags = [Link]))
# count=1, maximum times replacement occurs is 1.
print([Link](r'sANDs', ' & ', 'Baked', flags=[Link])) #
'r' before the patter denotes RE, s is for start
#and end of a String.
#9
Function subn()
print([Link]('ub', '~*' , 'Subject booked already'))
t = [Link]('ub', '~*' , 'Subject booked already', flags =
[Link])
print(t)
print(len(t))
print(t[0])
#10
Function escape()
print([Link]("This is Awseome even 1 AM"))
print([Link]("I Asked what is this [a-9], he said ^WoW"))
What do you understand by Tkinter?
Tkinter is an in-built Python module that is used to create GUI
applications. It is Python’s standard toolkit for GUI development.
Python provides various options for developing GUIs. Most important
are listed below.
o Tkinter − Tkinter is the Python interface to the Tk GUI toolkit
shipped with Python.
o wxPython − Is an open-source Python interface for
wxWindows.
o JPython − Is a Python port for Java which gives Python scripts
seamless access to Java class libraries on the local machine
1. Button
import tkinter as tk
r = [Link]()
[Link]('Counting Seconds')
button = [Link](r, text='Stop', width=25, command=[Link])
[Link]()
[Link]()
#
from tkinter import *
root = Tk()
frame = Frame(root)
[Link]()
bottomframe = Frame(root)
[Link](side=BOTTOM)
redbutton = Button(frame, text='Red', fg='red')
[Link](side=LEFT)
greenbutton = Button(frame, text='Brown', fg='brown')
[Link](side=LEFT)
bluebutton = Button(frame, text='Blue', fg='blue')
[Link](side=LEFT)
blackbutton = Button(bottomframe, text='Black', fg='black')
[Link](side=BOTTOM)
[Link]()
2. Checkbox
from tkinter import *
master = Tk()
var1 = IntVar()
Checkbutton(master, text='male', variable=var1).grid(row=0, sticky=W)
var2 = IntVar()
Checkbutton(master, text='female', variable=var2).grid(row=1,
sticky=W)
mainloop()
3. Input Fields
from tkinter import *
master = Tk()
Label(master, text='First Name').grid(row=0)
Label(master, text='Last Name').grid(row=1)
e1 = Entry(master)
e2 = Entry(master)
[Link](row=0, column=1)
[Link](row=1, column=1)
mainloop()
# Display text
from tkinter import *
root = Tk()
w = Label(root, text='[Link]!')
[Link]()
[Link]()
# Number
from tkinter import *
master = Tk()
w = Spinbox(master, from_ = 0, to = 10)
[Link]()
mainloop()
4. Menu
from tkinter import *
root = Tk()
menu = Menu(root)
[Link](menu=menu)
filemenu = Menu(menu)
menu.add_cascade(label='File', menu=filemenu)
filemenu.add_command(label='New')
filemenu.add_command(label='Open...')
filemenu.add_separator()
filemenu.add_command(label='Exit', command=[Link])
helpmenu = Menu(menu)
menu.add_cascade(label='Help', menu=helpmenu)
helpmenu.add_command(label='About')
mainloop()
5. Radio
from tkinter import *
root = Tk()
v = IntVar()
Radiobutton(root, text='GfG', variable=v, value=1).pack(anchor=W)
Radiobutton(root, text='MIT', variable=v, value=2).pack(anchor=W)
mainloop()
6. Scale
from tkinter import *
master = Tk()
w = Scale(master, from_=0, to=42)
[Link]()
w = Scale(master, from_=0, to=200, orient=HORIZONTAL)
[Link]()
mainloop()