Core Python Concepts - FLM
Core Python Concepts - FLM
Dachuri Chaitanya
M.Tech (IIT Roorkee), AI Expert
Mob: +91 9701 37 97 97, YouTube Channel: python.trainer.helper
Instagram: python.trainer.helper , Facebook: python.trainer.helper
WhatsApp Channel: https://whatsapp.com/channel/0029Va9Tj7j0AgW8byyzl81j
Trainer Introduction
• Post graduate from IIT Roorkee
• 60+ Batches, 4800+ students, 75000+ Weekly Tests, 9450+ Mock Interviews….
• Spend min. 2-6 hrs per day • Do not take long leaves
• See absent class recordings ASAP • Do not take food before class
• Gains more than enough knowledge & hands on required for IT Job.
Django sklearn,
pytest, numpy, pygame,
DB asyncio, Kivy, numpy,
pandas, Tensorflow,
HTML Lettuce BeeWare pandas, panda3D
Diesel matplotlib Math
CSS matplotlib
ML, DL Algor…
React AI, pybrain
sklearn,
Tensorflow
ML Algor…
DL Algor..
Math AI Developer Games
Web Developer Networking Mobile App Developer
Developer Developer
</>
System Programming
pybrain
sklearn Automation
Testing
tkinter
numpy, pandas
GUIs
Numeric & Scientific
Programming
3 4
Software code
package
PYTHON P
mycode.py or V Output • Low level, platform independent representation of
Python M source code.
• Executes much faster
Interpreter
Software code
Source code byte code
package
1. Reads line by line of source code (mycode.py)
mycode.pyc Output
2. Generates byte code for every line (mycode.pyc) mycode.py PVM
Using windows command prompt 1) Python installation path must be set in PATH system variable.
2) Source code (mycode.py) must be available in CWD: C:\Users\Lenovo
python mycode.py
or
Executes the python D:\code\mycode.py To get o/p using this command
• Extensible
Ex: Ex:
size = 782 percentage = 84.23
print(int(23.43)) prints 23 print(float(size)) prints 782.0
Ex:
hNum = 0x02AF print(hNum) Prints equivalent integer
value 687
Ex:
octNum = 0O01745 print(octNum) Prints equivalent integer
value 997
Ex:
binNum = 0b01001 print(binNum) Prints equivalent integer
value 9
bin(9)
D. Chaitanya Reddy (IIT Roorkee Alumni, AI Expert)
Complex Numbers
• Format: realpart + imginarypart
• Real part is optional.
• Imaginary part is terminated with a j or J
• Also be created using built in function: complex(real, img)
created using complex(real,
img) built in function
Ex:
clxNum = 3.0+4.0j cNum2 = complex(2, 7)
Imaginary
Imaginary Real part part
Real part part
decimal.getcontext().prec = 3
x-y Fraction(-1, 3)
prints
decimal.Decimal('1')/decimal.Deci
mal('7') Decimal('0.143') print(x-y) -1/3
x*y Fraction(2, 9)
prints
Can use Integer, float values also
Ex:
True + 4 5
prints
True == 1 True
prints
k = bool(1)
print(k) True
prints
Remainder results
O x / y Division: True 6) Floor Division always rounds fractional remainders down to their floor
r
d x // y Division: Floor (closest number below true value), regardless of types. Result depends on
e the types.
r −x Negation
x ** y Power (exponentiation) Ex: 10//4
D. Chaitanya Reddy (IIT Roorkee Alumni, AI Expert)
results
2, -10//4.0 results
-3.0, -10//4 results
-3
Highest
Lowest
Operators for Numbers
O Operator Meaning 7) All bit wise operations shall be performed on equivalent binary
p numbers and results to integer value.
e x < y, x <= y, Magnitude comparison, Set
r x > y, x >= y subset, superset Ex: x = 1 # 1 decimal is 0001 in bits
a
t x == y, x != y Value equality operators x << 2 prints 4 # shift left 2 bits: 0100 and prints resulted integer.
o
r
x | y Bitwise OR, Set Union x | 2 prints
3 # bitwise OR (either bit=1): 0011
x ^ y Bitwise XOR
P x & 1 prints
1 # bitwise AND (both bits=1): 0001
r x & y Bitwise AND, Set Intersection
e
c x << y, x >> y Bitwise SHIFT left/right
e 8) Other built-in numeric functions: pow, abs, sum, min, max, …
x + y Addition
d
e x – y Subtraction, Set difference Ex: pow(2, 4) 16, abs(-42.0) 42.0
n
c x * y Multiplication sum((2, 0, 4)) 6, min(3, 1, 2, 4) 1
e
x % y Remainder
9) Other built-in numeric modules: math, random, round, …
O x / y Division: True
r
d x // y Division: Floor Ex: math.floor(-2.567) -3,
e math.trunc(-2.567) -2
r −x Negation math.sqrt(144), math.pi, math.e
x ** y random.randint(1,
D. Chaitanya Reddy (IIT Roorkee Alumni,
Power (exponentiation)
AI Expert) 10) 5
Highest
Strings 0 1 2 3 4 5
Strings
String methods Escape chars
String method Interpretation Escape Meaning
'a {0} parrot'.format(S1) String formatting method \\ Single backslash
print(new_name)
Output:
MMaahheesshh BBaabbuu D. Chaitanya Reddy (IIT Roorkee Alumni, AI Expert)
Lists
• An ordered (insertion order)
collection of arbitrary objects. List creation techniques:
• Accessed by offset/index, i.e., List Version Interpretation
supports sequence operations. L = [] or L = list() Empty list
• Mutable: can grow and shrink on L = [3, 'a', 5.4, {}, [4, 5]] List with 5 items with nesting
demand. L = list('spam') List of an iterable's items
• Iterable: can traverse through all L = list(range(-4, 4)) List of successive integers
elements Sequence operations
List basic operations:
• Nestable: can create list of list of Operation Interpretation
list, so on. L[i], L[-i], L[i][j], Index, index of index, slicing
• Heterogeneous: can contain any L[i:j], L[-i:]
L1 + L2
type of object as elements. Concatenate
L * 3 Repeat
0 1 2 3 4 5
len(L) Length/size of list
'hi' 5 3.4 [7, 'No'] 0b100 True Results new list,
NO in place
3 Roorkee
D. Chaitanya Reddy (IIT in L Alumni, AI Expert) Element existence test/finding changes
-6 -5 -4 -3 -2 -1
Contd…
List methods
Lists
List method Interpretation
L.append(4) Add new element at last.
print(new_list)
Output:
[2, 'hihi', 6.8, [6, 7, D.6, 7]]
Chaitanya Reddy (IIT Roorkee Alumni, AI Expert)
Tuples
• An ordered (insertion order) collection of Lists vs Tuples
arbitrary objects.
Tuple creation techniques:
• Accessed by offset/index, i.e., supports
sequence operations. Tuple Version Interpretation
T = (), T = tuple() Empty tuple
• Immutable: fixed length. Applicable only
to top level. Not to it's content. T = (4, ) T = (4) Tuple with single element
• Iterable: can traverse through all elements T = (0, 3.4, 'hi', ('bye', 1)) Tuple with 4 elements and
nested tuple
• Nestable: can create tuple of tuple of T = tuple('spam') Tuple of items in an Iterable.
tuple, so on.
Tuple basic operations: Sequence operations
• Heterogeneous: can contain any type of
object as elements. Operation Interpretation
T[i], T[-i], T[i][j], Index, index of index, slicing
T[i:j], T[-i:]
T.index(el) Get the first index (from left) of element el. len(T) Length/size of tuple Results new
tuple, NO in
T.count(el) Get the count of element el. 'spam'
D. Chaitanya Reddy (IIT in T AI Expert)
Roorkee Alumni, Element existence test/finding place changes
1) Easy and Quick to write code.
Iterating Tuple Sequence 2) 10x Faster in execution.
3) Saves memory
my_tup = (1, 'hi', 3.4, [6, 7])
Using for loop: Using List Comprehension:
my_tup = (1, 'hi', 3.4, [6, 7])
new_list = [] tuple([el*2 for el in my_tup])
New_tup = ()
for el in my_tup: Output:
new_list.append(el*2)
(2, 'hihi', 6.8, [6, 7, 6, 7])
new_tup = tuple(new_list)
print(new_tup)
Output:
(2, 'hihi', 6.8, [6, 7, D.6, 7])
Chaitanya Reddy (IIT Roorkee Alumni, AI Expert)
Sets
• An unordered collection of Ex:
unique and immutable objects.
s=set() #empty set
• Designed to support operations
s1=set([1, 7.4, True, 5]) #built-in call for set creation
corresponding to mathematical
s2={1, 'sp', 7, 'e'} #new literal for set creation
set theory.
s3=set('spa')
• Mutable: can grow and shrink on s3 prints
{'s', 'a', 'p'}
demand. s3.add('h'), s3 prints
{'s', 'h', 'a', 'p'}
• Iterable: can traverse through all s1&s2 prints
{1}
elements s1|s2 prints
{1, 5, 7, 7.4, 'e', 'sp'}
• All standard operations of Set s1-s2 prints {5, 7.4}
uses efficient algorithms and s1^s2 prints {5, 7, 7.4, 'e', 'sp'}
hence faster results s1.union(s2) is same as s1|s2
Mutable: value of the object can't be changed in place. s1.intersection(s2) is same as s1&s2
D. Chaitanya Reddy (IIT Roorkee Alumni, AI Expert)
Immutable: In place changes are allowed for object's value.
Contd…
Sets
• Frozenset: Same as Set behavior except immutable in nature.
Ex: s4 = frozenset([1, 5.6, 'rt', True])
• Can be used to isolate differences in other iterables like lists, strings, etc…
• More suitable for large datasets if order does not matter to get faster results
Output:
{0, 2, 10, 14, 16, 18, -2}
• Iterable: can traverse through all E = {'CTO': {'name': 'Mahesh', 'age':40}} Nested dictionary
elements D = dict(name='Mahesh', age=40) Alternate creation
• Nestable: can contain dict in another D = dict([('name', 'Mahesh'), ('age', 40)]) Creation with key value pairs in list
dict. D = dict(zip(keylist, valuelist)) Creation with zipped key value lists
• Heterogeneous: can contain any type of D = dict.fromkeys(['name', 'age'], default?) Creation using keys and values with
objects. default (or None).
E['CTO']['age'] Indexing (fetching element) by keys in nested Value: any arbitrary object is allowed for value.
dictionaries.
'age' in D key existence test/finding
D.keys() Dictionary keys view - Views also retain original order of dictionary.
D.values() Dictionary values view
- Reflect future changes to the dictionary
- Support set operations.
D.items() Dictionary items (key, value pairs) view
Output:
print(my_dict)
{'name': 'Allu Arjun', 'profression': 'hero',
'age': 35, 'location': 'hyd'}
Output:
{'name': 'Allu Arjun', 'profression': 'hero',
'age': 35, 'location': 'hyd'}
Output:
name Allu Arjun
profression hero
location hyd D. Chaitanya Reddy (IIT Roorkee Alumni, AI Expert)
Named Tuples
• Same as tuple, except accessible by offset/index and/or keys.
>>> bob = ('Bob', 40.5, ['dev', 'mgr']) #tuple record
('Bob', 40.5)
D. Chaitanya Reddy (IIT Roorkee Alumni, AI Expert)
Files
• To deal with files (reading, writing files)
• The built-in open function creates a python file object.
File methods (contd…)
• Serves as a link to a file residing on the machine.
File method Interpretation
File creation techniques: op.writelines(aList) Write all line strings in a list into
File creation Interpretation file.
op = open(r'C:\temp\spam.txt', 'w') Create output file ('w' means write) op.close() Manual closing file.
ip = open('ham.txt', 'r') Create input file ('r' means read). op.flush() Flush output buffer into disk.
Ip = open(r'C:\temp\ham.txt') Default mode is 'r'
any_file.seek(N) Change file position to offset N for
next operation.
File methods
File processing modes:
File method Interpretation
mode Interpretation mode Interpretation
aStr = ip.read() Read entire file into a string
r Read a text file. wb Write to binary file.
aStr = ip.read(N) Read upto next N chars (or bytes) into a string
w Write to a text file. + Read & write to
aStr = ip.readline() Read next line (including \n newline) into a string same file.
a Append text to end
aList = ip.readlines() Read entire file into list of strings (with \n) of file.
D. Chaitanya Reddy rb Read binary file.
op.write(aStr) Write a string of chars (or bytes) into file.(IIT Roorkee Alumni, AI Expert)
Storing objects in Files
My Data.txt
Any Single Object Same Single Object Pickle (using pickle module)
List List
Store / Save . Retrieve
object
object
.
.
Shelve (using shelve module)
MyDa
ta.bak
Pickle (using pickle module)
multiple
multiple Store / Save
MyDa
Retrieve
objects,
ta.bat
objects access by
their key
MyDat
a.dir Shelve (using shelve module)
Corrected code
1 my_file = open(r'C:\temp\hello.txt', 'r')
try:
2 for line in my_file:
print(line[:5])
finally:
3
my_file.close()
D. Chaitanya Reddy (IIT Roorkee Alumni, AI Expert)
Built-in Types Summary
Object Type Category Ordered? Mutable? Nestable? Elements Unique?
All Numbers Numeric N/A No N/A N/A
Strings Sequence Yes No N/A No
Lists Sequence Yes Yes Yes No
Dictionaries Mapping Yes (from 3.7) Yes Yes No
Tuples Sequence Yes No Yes No
Sets Set No Yes No Yes
Frozen sets Set No No Yes Yes
Files Extension N/A N/A N/A N/A
bytearray Sequence Yes Yes N/A No
bytearray
List
frozenset
Numbers
Dictionary Set
String Tuple
x = 42
1
a value. x 42
X 'spam' 0
type designator
float
a 3
int
x = {3, 9, 1} x X 42 0
str
object.
float
y = 3764 y 3764
int
b = a a 3 2
Definition:
If multiple variables are referring to same object (reference counter
header field value of the object is greater than ONE), then they shall
be called as shared reference x = 3764 x 3764
y = x y
str
spam 1
int
x is y prints True
a 3 1
print(L)
[0] [1] [2]
['a', [1, 2, 3], 'b']
'a' X 'b' 2
D = {'k': X, 'y': 2}
References
print(D)
1 2 3
{'k': [1, 2, 3], 'y': 2}
D. Chaitanya
1 Reddy (IIT'hi'
Roorkee Alumni,
3 AI Expert)
Contd…
References vs Copies
X = [1, 2, 3] [0] [1] [2] [x] [y]
[0] [1] [2]
L = ['a', X[:], 'b']
X L D
D = {'x': X.copy(), 'y': 2}
Copies [0] [1] [2] [0] [1] [2]
print(L) 'a' 'b'
1 2 3 2
['a', [1, 2, 3], 'b']
print(D)
1 2 3 1 2 3
{'x': [1, 2, 3], 'y': 2}
X[1] = 'hi' [0] [1] [2] [0] [1] [2] [x] [y]
print(X) X L D
[1, 'hi', 3]
print(D)
1 2 3 1 2 3
D. Chaitanya Reddy (IIT Roorkee Alumni, AI Expert)
{'x': [1, 2, 3], 'y': 2}
Statements, Expressions & Syntax
Compound statements
Ex:
if x > 100:
if name == 'python':
print(name)
else:
print(x)
Ex: _spam, spam, Spam_1 • Have 2 leading and trailing underscores (__X__) are
1_spam, spam$, @#! system defined names that have special meaning to
interpreter.
• Case sensitive.
Ex: SPam is not same as spam • Begin with 2 underscores and do not end with 2 more
self is NOT a reserved
(__X) are localized (mangled) to enclosing classes.
• Reserved words are off-limit. word in python.
Ex:
if x or y<6:
print(name)
else:
print(x)
if–elif–else
if-elif-else statement: Ex:
if name=='python':
syntax
if SOME_CONDITION: print('beginning')
single or multi line code elif name=='machine':
elif SOME_CONDITION: Can be print('season-1')
zero/single/multi
single or multi line code elif blocks elif name=='deep':
else:
single or multi line code
print('season-2')
else:
print('conclusion')
If a single line code present in any block,
the below syntax also allowed:
Ex:
if SOME_CONDITION: single line code if name=='python': print('beginning')
elif SOME_CONDITION: elif name=='machine':
single or multi line code print('season-1')
else: single line code elif name=='deep': print('season-2')
D. Chaitanya Reddy (IIT Roorkee Alumni, AI Expert)
else: print('conclusion')
Contd…
if–elif–else
Nested if statement: Nested if-else statement:
if–elif–else
Nested if-elif-else statement: if-else ternary expression:
if x > 100:
if name=='python': if year<2022:
Output:
• continue – jumps to the x = 10
8 6 4 2 0
while x:
start of the closest x -= 1
enclosing loop (to the if x%2!=0: continue
loop's first line) print(x, end=' ')
x = 10 Output:
• pass – does nothing at all. while x: Infinite loop
It's an empty statement pass
placeholder. x = y // 2 Output:
while x > 1: Prints whether the
if y % x == 0: y is prime or not.
print(y, 'has factor', x)
• Loop else – runs if and break
only if loop is exited x -= 1
else: Reddy (IIT Roorkee Alumni, AI Expert)
D. Chaitanya
normally. print(y, 'is prime')
for loop
1. Any object that support iteration
protocol or indexing protocol. Ex3:
2. Loop iterates for every element in
object irrespective of any condition. movie = 'bahubali'
Syntax: for ch in movie: print(ch, end=' ')
for var in OBJECT: #assign OBJECT elements to var
for loop
Ex6: Ex9:
L1 = [(1, 2, 3), (7, 14, 21), (-2, -4, -6)] D1 = {'a': 2, 'b': 4, 'c': 16}
for (a, b, c) in L1: print(a, b, c, sep='>>') for k in D1:
print(k, D1[k], sep='=>', end=', ')
Output:
1 >> 2 >> 3 for k in list(D1.keys()):
7 >> 14 >> 21 print(k, D1[k], sep='=>', end=', ')
-2 >> -4 >> -6
Ex7: for (k, vl) in list(D1.items()):
L2 = [((1, 2), 3), ((7, 14), 21), ((-2, -4), -6)] print(k, vl, sep='=>', end=', ')
for ((a, b), c) in L2: print(a, b, c, end=', ')
Output: a=>2, b=>4, c=>16
Output:
Ex10:
1 2 3, 7 14 21, -2 -4 -6,
for line in open('c:\tmp\info.txt').readlines():
Ex8: print(line)
L3 = [(1, 2, 3, 4), (10, 20, 30, 40)]
for T in L3: for line in open('c:\tmp\info.txt', 'r'):
print(T[-1], T[1:], T[0:2], end=', ') print(line)
Ex:
items = ['aaa', 111, (4, 5), 2.01] 1. Like for loop, nesting also applicable as it is to
tests = [(4, 5), 3.14] while loop too.
for key in tests:
for item in tests: 2. All the examples shown for for loop can also
if item==key: achieved with while loop, but needs more lines of
print(key, 'was found') code.
break
else: 3. for loop has many benefits (comprehensions, manual
print(key, 'not found!') indexing, etc…) compared to while loop in python.
Output:
(4, 5) was found
3.14 not found!
[0, 1, 2, 3, 4] Output: a c e g i k
X = 'spam'
>>>list(range(2, 5))
for ix in range(len(X)):
[2, 3, 4] S = 'abcdefghijk'
X = X[ix:] + X[:ix]
>>>list(range(0, 10, 2)) for ch in S[::2]:
print(X, end=' ')
[0, 2, 4, 6, 8] print(S[id], end=' ')
Output: spam, pams mspa amsp
>>>list(range(-3, 3)) Output: a c e g i k
single or multi line code #loop body (1, 11, 1), (2, 12, 4), (3, 13, 9)
else: #optinal else F=open(r'C:\tmp\info.txt') #call iter() directly
single or multi line code #run if and only if loop exit normally.
F.__next__(), F.__next__(), F.__next__()
Iteration Protocol: A Protocol that supports iter() and 1st line of file, 2nd line of file, 3rd line of file
__next__() calls.
Calling __next__() method raises a built-in exception:
Indexing Protocol: A Protocol that supports indexing StopIteration after the last element in iterable object.
operation like [0], [4].
M = map(math.pow, [1, 2, 3], [2, 3, 3])
R=range(-5, 0) Supporting indexing operation
IM = iter(M)
R[1], R[0], R[4] Output:-4, -5, -1 IM.__next__(), IM.__next__(), IM.__next__()
Can still create iterable object that
Manual (1.0, 8.0, 27.0)
supports indexing operation
I=iter(R) iterations
IM.__next__()
I.__next__(), I.__next__(), I.__next__() Raises
D. Chaitanya Reddy (IIT Roorkee StopIteration
Alumni, AI Expert) error
Protocols Summary
Indexing Protocol Iteration Protocol
• Protocol that supports indexing operation. • Protocol that supports iter() or __iter__()
and __next__() calls.
• Elements can be accessed using notation obj[0], • Elements can be accessed using __next__()
obj[1], etc… method call.
• Can access element at any index at any time. • Should start accessing the elements from the start and
access sequentially only using __next__() call.
• Elements in object can be accessed multiple times • Elements in object can be accessed only once. Need
without reinitializing object. to reinitialize the object to access elements again.
• Accessing elements with wrong index results into • Calling __next__() after fetching the last element
IndexError. results into StopIteration Error.
Set Comprehension:
print({int(math.pow(x, 3)/math.sqrt(x)) for x in range(1, 20)})
Output: {1024, 1, 129, 5, 15, 401, 32, 1573, 1191, 181, 55, 316, 88, 733, 1374, 609, 871, 498, 243}
Dictionary Comprehension:
print({x: int(math.pow(x, 3)/math.sqrt(x)) for x in range(1, 20)})
Output: {1: 1, 2: 5, 3: 15, 4: 32, 5: 55, 6: 88, 7: 129, 8: 181, 9: 243, 10: 316, 11: 401, 12: 498, 13: 609,
14: 733, 15: 871, 16: 1024, 17: 1191, 18: 1374, 19: 1573}
bahubali-2016
RRR-2023
With files: Pushpa-2022
[('2023' in line, line[:5]) for line in open(r'C:\tmp\test.txt')]
Output: [(False, 'bahub'), (True, 'RRR-2'), (False, 'Pushp'), (False, 'Avata')] Avatar-2021
Comprehensions
List with for loop with if condition: List Comprehension with if filter:
L = list(range(0, 20)) print([x/2 for x in range(0, 20) if x%2==0])
LN = []
for x in L:
if x%2==0:
val = x/2
LN.append(val)
print(LN)
Output: [0.0, 1.0, 2.0, 3.0, 4.0, 5.0, 6.0, 7.0, 8.0, 9.0]
Set Comprehension:
print({int(math.pow(x, 3)/math.sqrt(x)) for x in range(1, 20) if x%2==0})
Output: {32, 1024, 5, 498, 181, 88, 316, 733, 1374}
Dictionary Comprehension:
print({x: int(math.pow(x, 3)/math.sqrt(x)) for x in range(1, 20) if x%2==0})
Output: {2: 5, 4: 32, 6: 88, 8: 181, 10: 316, 12: 498, 14: 733, 16: 1024, 18: 1374}
bahubali-2016
RRR-2023
With Files: Pushpa-2022
[('2023' in line, line[:5]) for line in open(r'C:\tmp\test.txt') if '???' not in line] Avatar-2021
Output: [(False, 'bahub'), (True, 'RRR-2'), (False, 'Pushp'), (False, 'Avata')] Pushpa2-???
Comprehensions
Nested loops: List Comprehension with nesting:
res = [] print([x+y for x in 'abc' for y in 'lmn'])
for x in 'abc':
for y in 'lmn':
res.append(x+y)
print(res)
Output: ['al', 'am', 'an', 'bl', 'bm', 'bn', 'cl', 'cm', 'cn']
Functions
Syntax: • defs are also nothing but objects.
def name(arg1, arg2, ……, argN): def get_square(num):
... return num**2
... 1. Returning value is optional. sq_val = get_square(4)
2. If NO return means, return None
return value 3. Just return means, return None
my_func = get_square
result = my_func(5)
• defs can be nested
X = 99
• Can attach arbitrary attributes to defs to store some
def f1(): information.
name = 'Ganesh'
def f2():
get_square.last_num = 5
print('Hello World!')
get_square.list_nums = []
f2()
get_square.list_nums.append(4)
get_square.list_nums.append(5)
f1()
def func():
name = 'Suresh' # Local(function) scope name : a different variable
print(name)
print(name)
func()
def bunc():
info = 'Suresh' # Local(function) scope name : a different variable
def junc():
age = 56
print(age)
print(info)
junc()
print(name)
D. Chaitanya Reddy (IIT Roorkee Alumni, AI Expert)
bunc()
Contd…
gun()
• Matching by argument name – keyword args Output: ()
• Collect arbitrarily many positional or keyword args o/p: {'a':1, 'b':6, 'c':0, 'd':-7}
sun(a=1, b=6, c=0, d=-7)
o/p:{'a':7, 'b':-3}
sun(a=7, b=-3)
• Pass arbitrarily many positional or keyword args
sun()
Output: {}
• Args must be passed by name – keyword only args
def sun(**args): sun(a=1)
print(args) o/p: {'a':1}
Output: 1 2 3 4
• Args must be passed by name – keyword only cat(1, *(2,), c=3, **{'d':4}) # same as calling cat(1, 2, d=4, c=3)
D. Chaitanya Reddy (IIT Roorkee Alumni, AI
1 Expert)
args Output: 2 3 4
Contd…
Matching Args, Passing Args
1. All args appear after * in function header are keyword only args.
• Matching by position from left to right – 2. keyword only args must be present before **args and after *args, when both are
present in function header.
positional args 3. Arg name appearing before *args are default args, not keyword only args.
veg(7, -1, 9)
Output: 7 -1 9 Type error: veg() takes 1
positional arg, but given 3
veg(c=9, a=7, b=-1)
• Matching by argument name – keyword args
mysum([1, 2, 3, 4, 5])
Ouput: 15
def normsum(L):
sum = 0
for val in L: sum += el
return sum
normsum([1, 2, 3, 4, 5])
Ouput: 15
Ouput:
spam!
ham!
def enjoy(ch, i, d): def enjoy(ch: 'name', i: (1, 10), d: float) -> str:
return ch*i + str(d) return ch*i + str(d)
enjoy.__annotations__
Output: {'ch': 'name', 'i': (1, 10), 'd': float, 'return': str}
#Annotations for default values, return multiple values
def enjoy(ch: 'name' = 'Ganesh', i: (1, for arg in enjoy.__annotations__:
10) = 3, d: float) -> tuple[str, float] | print(arg, '=>', enjoy.__annotations__[arg], end='|')
None : Output: ch => name|i => (1, 10)|d => <class 'float'>|return =>
return ch*i + str(d), d**i <class
D. Chaitanya Reddy 'str'>|
(IIT Roorkee Alumni, AI Expert)
Anonymous Functions: lambda
• lambda is an unnamed function and often used to inline a function definition.
• lambda is an expression, not a statement LEGB scope rules applies
here too
• lambda's body is a single expression, not a block of statements.
def knights():
• lambda always return a function object title = 'sir'
action = (lambda x: title + ' ' + x)
• lambda can be nested too. return action
Syntax: act = knights()
lambda arg1, arg2, …, argN: expression using args msg = act('robin')
msg
Ex: Output: sir robin
import time
start = time.process_time()
for i in range(10000000):
• timeit module k = i+1
gets CPU execution time
end = time.process_time()
in seconds
exe_time = end - start
print(exe_time)
Output: 1.9375
• datetime module
D. Chaitanya Reddy (IIT Roorkee Alumni, AI Expert)
Execution Time - timeit Namespace which the
stmt code belongs to.
• time.time() import timeit
def addition():
print('addition:', sum(range(100000)))
• time.process_time() Output:
addition: 4999950000
addition: 4999950000
addition: 4999950000
Avg time for executing
addition: 4999950000
stmt 5 times
addition: 4999950000
0.03184490000001006 Default runs: 7, default
loop count: 10,000
• timeit module import timeit
%timeit [x for x in range(1000)]
Output: 164.9 µs ± 7.15 µs per loop (mean ± std. dev. of 7 runs, 10,000 loops each)
import timeit
%timeit –r4 –n15 [x for x in range(1000)]
• datetime module
Output: 97.2 µs ± Reddy
D. Chaitanya 3.55(IITµs perAlumni,
Roorkee loopAI(mean
Expert) ± std. dev. of 4 runs, 15 loops each)
Execution Time - timeit
• time.time()
• time.process_time()
import datetime
start = datetime.datetime.now()
for i in range(10000000):
k = i+1
• timeit module
end = datetime.datetime.now()
exe_time = end-start
print(exe_time)
Output:
gets value in time format : HH:MM:SS
0:00:02.004631
• datetime module
D. Chaitanya Reddy (IIT Roorkee Alumni, AI Expert)
Modules
Definition: It's just a .py extension file (source file) that contains the some python statements
• Code reuse
import c c.py
def spam(text):
print(text, 'spam') module is not imported any If the module is being
where in the same program and imported by some other
c.py may also imports some other modules of the same
import b modules of the same program. program.
def hello(text):
print('welcome', text)
a.py
import b
b.spam('hi') # prints “hi spam” D. Chaitanya Reddy (IIT Roorkee Alumni, AI Expert)
How import works?
First time import
Save module object into
Find Module file Compile to byte code Run byte code sys.modules
1 2 3 4
Find Compile Run Save
Further import
1 2
Fetch Run
Running programs
HD (top level script file path)
Home Directory / Current Working Directory.
Working in interactive sessions CWD
PYTHONPATH directories (if set).
• All directories in sequence in all 5 components nothing but Module Search Path and saved
in sys.path
• Python finds module file in the directory listed in the sys.path list from left to right.
• If 2 files (with different allowed extensions, ex: b.py, b.so) present in the
different directories of module search path (sys.path), python picks up the first
match found.
• If 2 files (with different allowed extensions, ex: b.py, b.so) present in the same
directory, Python picks up any one randomly.
D. Chaitanya Reddy (IIT Roorkee Alumni, AI Expert)
Contd…
import statement from statement from * statement from . statement from .. statement
import module1
from module1 import * from . import module1
module1.printer('hi')
print(count, purpose) print(module1.count)
print(module1.count)
printer(getCount()) print(module1.purpose)
val = module1.getCount()
setCount(112) printer(module1.getCount())
. from module1 import printer
. module1.setCount(112)
. printer('hi')
. .
. from .. import module1
. .
. print(module1.count)
. .
. print(module1.purpose)
printer(module1.getCount())
from module1 import printer, count module1.setCount(112)
printer('hi') from .module1 import count, .
print(count) purpose .
printer(count) print(count) .
D. Chaitanya Reddy (IIT Roorkee Alumni, AI Expert) print(purpose) .
More about import
Import happens only once Reload modules
from importlib import reload
simple.py reload(simple) # load module (run module file) and saves in sys.modules table
print(simple.spam)
print('hello') .
.
spam = 1 .`
First Import
x = 1
y = [1, 2] import small # get module name (from doesn't)
small.x = 42 # changes x in loaded module in sys.modules
from small import x, y # copy two names out from small module.
x = 42
y[0] = 42
# changes local x only.
# changes shared mutable in place
import and from equivalence
from module import name1, name2
Is equivalent to this
statement sequence
import small # get module name (from doesn't)
print(small.x) # prints 1.
print(small.y) # prints [42, 2] import module
name1 = module.name1
name2 = module.name2
del module
from M import func from moduleC import * # copies all names from moduleC to current module
O.py
import M, N # Get the whole modules, not their names
M.func() # calls func in M module
N.func() # Also, calls func from N module. No overwriting.
O.py O.py
from M import func as mfunc # Rename uniquely with 'as' import M as m # Rename uniquely with 'as'
from N import func as nfunc import N as n # Rename uniquely with 'as'
mfunc(); nfunc() # calls one or the other m.func(); n.func() # calls one or the other
D. Chaitanya Reddy (IIT Roorkee Alumni, AI Expert)
Module fundamentals
module1.py
count = 100
Module naming also should follow the same
rules outlined for normal variables:
purpose = 'manage count'
✓ Syntax: (underscore or letter) + (any number
of letters, digits or underscores)
def printer(x): ✓ Case sensitive.
print(x) ✓ Reserved words are off-limits.
Only count, printer, getCount, setCount
are attributes (names associated with this
module object (module1)) as they are def getCount():
defined at the top level of this module. return count This file is nothing
but a module.
def setCount(value):
global count But, confirm is not an attribute of
All names of a module can be accessed count = value this module (module1) as this
using __dict__ in built attribute of every function is not defined at top
module object. This always returns a def confirm(): level of this module, rather
dictionary. print('count updated!') nested in another function.
print(list(module1.__dict__.keys()))
__all__ is a list contains string names of module. Names present in this list only can be
imported with from * statement.
alls.py someother.py another.py
a, _b, c, _d = 1, 2, 3, 4 from alls import * import alls
__all__ = ['a', '_d'] print(a, _d) print(alls.a, alls._d, alls.c, alls._b)
Output: 1, 4 Output: 1, 4, 3, 2
print(c)
Output: Name Error: name 'c' is not defined
def manage():
print('Hey, join us!.')
newmodule.py newmodule.py
def designer(): puppy = dog() Every Instance inherits (gets access) class
print('Cool, its my first day.') tommy = dog() attributes
tiger = dog()
def manage():
print('Hey, join us!.')
Dog class dog:
A new class 'dog'. This class is
print(tiger)
Output: <mymodule.dog object at 0x000001FB90A5AE30>
also an object of module
name = None print(tiger.name, tiger.color, tiger.breed, tiger.age)
Identified by Behavior Attributes of dog class
color = None Output: None None None 0
breed = None (class attributes)
name eat() age = 0
Accessing Dog class attributes using any
color bark() def eat(self): instance created from Dog class
print('wrote code logic on how to eat.')
methods of dog
breed sleep() def bark(self): class
print('wrote code/logic on how to bark.')
age play() Dog class puppy
def sleep(self, ihours):
print('wrote code/logic on how to sleep.')
- name (None) tommy
def play(self, location): - color (None)
print('wrote code/logic on how to play.') - breed (None)
D. Chaitanya Reddy (IIT Roorkee Alumni, AI Expert) - age (0) tiger
Contd…
def manage():
print('Hey, join us!.') print(my_dog)
Output: <mymodule.dog object at 0x000001FB90A5AE30>
class dog:
Dog print(my_dog.name, my_dog.color, my_dog.age,
name = None my_dog.breed)
color = None Output: tiger white 4 None
breed = None
Identified by Behavior age = 0 With these assignment to attributes of self, a
name eat() def set_details(self, dname, dcol, dage):
new set of these 3 attributes gets created and
self.name, self.color = dname, dcol
updated to values in instance (instance attributes),
color bark() self.age = dage not the class
print(x.name, y.name)
Output: Bob Bob
Modules Classes
• Are created with python files (.py files) • Are created with class statements
• Form the top level in python program • Always live with in module
structure
D. Chaitanya Reddy (IIT Roorkee Alumni, AI Expert)
Contd…
Summary
• Each class statement generates a new class object
• Instances are automatically linked to the classes from which they are created
def eat(self):
print('wrote code logic on how to eat.') print(tom.name, dog.name)
Attribute search logic: Output: tommy None
1. Accessing using instance
def bark(self):
print('wrote code/logic on how to bark.')
2 Search in Class print(tig.name, dog.age, dog.breed)
def sleep(self, ihours): Output: tiger 0 Boxer
print('wrote code/logic on how to sleep.')
Search logic directly search in
def play(self, location):
print('wrote code/logic on how to play.')
class as we accessing the
1 Search in instance attribute using class name.
tom
Dog class name (tommy) tig
color (black) name (tiger)
- name (None) print(dog.vaccinated)
age (5) color (grey) 2. Accessing using class
- color (None) Output: Error!!!
vaccinated age (6) Instance attributes can't
- breed (Boxer) (No) status (missing)
be accessed by class.
- age (0) vaccinated 1 Search in Class
(Yes)D. Chaitanya Reddy (IIT Roorkee Alumni, AI Expert)
Contd…
Classes basics, coding classes
class dog: tom = dog('tommy', 'black', 5, 'No')
name = None
tom.sleep(5) python converts internally to dog.sleep(tom, 5)
color = None
Output: wrote code/logic on how to sleep.
breed = 'Boxer'
age = 0
def eat(self): Python provided Class attributes Python provided Instance attributes
print('wrote code logic on how to eat.') dir(dog) dir(tom) tom.__dict__
['__class__', ['__class__', Output: {'name':
def bark(self): '__dict__', '__dict__', 'tommy', 'color':
print('wrote code/logic on how to bark.') . dir shows attributes & . 'black', 'age': 5,
. methods present in . 'vaccinated': 'No'}
def sleep(self, ihours): . class/object also inherited .
print('wrote code/logic on how to sleep.') 'age', 'age',
attributes, methods. __dict__ of an instance
'bark', 'bark',
def play(self, location): 'play', 'play', shows only attributes
print('wrote code/logic on how to play.') 'sleep'] 'sleep' present in that instance
'vaccinated'] only, does not show
• Logically, Methods provides behavior for instance dog.__name__
tom.__class__
attributes present in the
Output: 'dog' instance's class.
objects __main__.dog
dog.__module__
• Technically, methods work same as functions with a Output: mymodule tom.__class__.__name__
difference that, methods first argument always Output: 'dog'
instance object. dog.__dict__
D. Chaitanya Reddy (IIT Roorkee Alumni, AI Expert)
Output: <check in notebook> tom.__module__
Output: mymodule
Contd…
• The first argument in method should be • They are simple functions in class • The first argument in method should be
always instance (self) always class object.
• No extra argument required to pass.
• Can be called using instance or class name • By default, can be called using class name
• By default, can be called using class name only. Ex: methods.cmeth(methods, 15)
only.
class methods: • Need to declare it as classmethod if we
need to call it using instance as class
def imeth(self, x): • Need to declare it as static method if method.
print([self, x]) we need to call it using instance too.
• 2 ways to declare method as class
@staticmethod • 2 ways to declare method as static.
def smeth(x):
print([x]) cl_obj = methods()
Function cl_obj.imeth(5)
@classmethod Output: [<__main__.methods object at 0x0000021F2D1640D0>, 5]
def cmeth(cls, x):
decorators If not defined as classmethod,
then, this must be the syntax.
print([cls, x]) methods.imeth(cl_obj, 5)
Output: [<__main__.methods object at 0x0000021F2D1640D0>, 5]
#smeth = staticmethod(smeth) When declared as classmethod, can be
#cmeth = classmethod(cmeth) methods.smeth(7) methods.cmeth(methods, 9) accessible as class method with instance as well
Output: [7] [<class '__main__.methods'>, 9] as class (with syntax change)
If not defined as classmethod, still no
error by accessing it with object. But, python
obj2 = methods() obj3 = methods() will consider it as instance method while
No error only if method is obj2.smeth(7) obj3.cmeth(9)
D. Chaitanya Reddy (IIT Roorkee Alumni, AI Expert) / methdos.cmeth(9) accessing with instance.
declared as staticmethod Output: [7] Output: [<class '__main__.methods'>, 9]
Function decorators
Definition : A sort of runtime declaration about the function that follows. User defined function decorators
Syntax: starts with @ and followed by a class tracer:
class C: function name (meta function)
class C: def __init__(self, func):
@staticmethod Syntax has same effect as self.calls = 0
def meth(x): def meth(x): self.func = func
print([x]) print([x])
def __call__(self, *args):
meth = staticmethod(meth) self.calls += 1
print(self.calls, self.func.__name__)
return self.func(*args)
• Inheritance allow us to make changes in derived/inherited classes (also called subclasses), instead of changing existing
classes in place.
• The super classes are listed in parentheses in a class header. Ex: class Secondclass(Firstclass):
class Myclass(Firstclass, Seconclass):
• Instances inherits (get access) attributes from all accessible classes (class tree hierarchy)
sue.giveRaise(0.1)
print(sue.name, sue.job, sue.pay, sue.lastName())
D. Chaitanya Reddy (IIT Roorkee Alumni, AI Expert)
Output: Sue Jones dev 110000 Jones
Contd…
Ex4:
OOP – Inheritance Ex6:
x = Super() class Super:
class A: x.method()
name = 'Mahesh' def method(self):
Output: in Super.method print('in Super.method') # default behavior
def method(self):
print('I am in method of Class A') def delegate(self):
x = Sub() self.action() # expected to be defined
x.method()
class B: Output:
name = 'Suresh' class Inheritor(Super): pass # inherit methods from Super
starting Sub.method
age = 40 in Super.method
def hello(self, msg): class Replacer(Super): # Replace method completely
ending Sub.method def method(self):
print(msg)
Inheriting Multiple print('in Replacer.method')
class C(A, B): Extender...
super classes
weight = 56.34 starting Extender.method class Extender(Super): # Extend method behavior
def sendoff(self): in Super.method def method(self):
print('Bye ', self.name) ending Extender.method print('starting Extender.method')
Super.method(self)
c = C() print('ending Extender.method')
c.sendoff() x = Provider()
Output: Bye Mahesh x.delegate() class Provider(Super): # Fill in a required method
Output: def action(self):
Ex5: Provider... print('in Provider.action')
in Provider.action
class Super: for klass in (Inheritor, Replacer, Extender):
def method(self): print('\n' + klass.__name__ + '...')
print('in Super.method') klass().method()
Can call super class method
class Sub(Super): output:
using it's class name.
def method(self): Inheritor...
print('starting Sub.method') in Super.method
Super.method(self)
print('ending Sub.method') Replacer...
D. Chaitanya Reddy (IIT Roorkee Alumni, AI Expert)
in Replacer.method
Contd…
OOP – Inheritance
Ex7: selftest()
classtree.py output:
def classtree(cls, indent): Tree of <__main__.selftest.<locals>.B object at 0x00000198ECF2E5E0>
print('.'*indent + cls.__name__) ...B
List all classes
for supercls in cls.__bases__: ......A
inherited from
classtree(supercls, indent+3) .........object
Tree of <__main__.selftest.<locals>.F object at 0x00000198ECF2E5E0>
def instancetree(inst): ...F
print('Tree of %s' % inst) ......D
classtree(inst.__class__, 3) .........B
............A
...............object
def selftest(): A .........C
class A: pass ............A
class B(A): pass ...............object
class C(A): pass ......E
class D(B,C): pass .........object
class E: pass
class F(D,E): pass
B C
instancetree(B()) object class is super class for all classes if
instancetree(F()) they are not inherited
D E
class Sub(Super):
def action(self):
print('it is fine now.')
y = Sub()
y.delegate()
D. Chaitanya Reddy (IIT Roorkee Alumni, AI Expert)
Ouput: it is fine now.
OOP – Composition, Aggregation
Definition: a capability of an object nesting in Definition: a capability of an object has list of other objects in it.
another object. class Employee:
def __init__(self, name, job=None, pay=0):
class Employee: self.name = name
def __init__(self, name, job=None, pay=0): self.job = job
self.name = name self.pay = pay
self.job = job def giveRaise(self, percent):
self.pay = pay self.pay = int(self.pay * (1 + percent))
class Manager(Employee):
def __init__(self, name, pay):
Employee.__init__(self, name, 'mgr', pay)
print(x.get_square(5))
Output: 25 • Classes can also overload built-in operations such as printing, function
calls, attribute access, etc…
print(x[4]) -- ????
print(x+4) -- ???
print(x-2) -- ???
print(x) -- ???
print(len(x)) -- ??? • Overloading makes class instances act more like built-in types
print(5 in x) -- ???
print(bool(x)) -- ???
O
O O O
A
A A B A
B
B D
B C C D
C
C
D
D
D
1 Search in Class
Class tree
D. Chaitanya Reddy (IIT Roorkee Alumni, AI Expert)
Contd…
Inheritance search logic
Search logic name: DFLR
Ex: X = E()
Attribute search logic:
1. Accessing using instance
(Depth First Left Right) class A: attr=1 X.attr
Search in super Class class B: attr=2 Output: 1
3 (goes all level up of tree) class C(A): pass
class D(B): attr=4 Search Order
E
2. Accessing using class A B
Non Diamond shape class
Search in super Classes tree. DFLR is applicable.
2 (goes all level up of tree)
X
C D
1 Search in Class
E
D. Chaitanya Reddy (IIT Roorkee Alumni, AI Expert)
Contd…
Inheritance search logic
Ex:
class A: attr=1 X = F() K = D() print(F.__mro__)
class B(A): pass X.attr K.attr Output:
class C(A): attr=3 Output: 3 Output: 3 (__main__.F,
class D(B,C): pass __main__.D,
class E: attr=5 __main__.B,
Search Order Search Order
class F(D,E): pass __main__.C,
Object __main__.A,
class C C
O __main__.E,
object)
A B B
print(D.__mro__)
Output:
Diamond shape class tree. D D (__main__.D,
B C MRO is applicable.
__main__.B,
__main__.C,
F K __main__.A,
D E object)
Non Diamond shape class
tree. DFLR is applicable.
X
F D. Chaitanya Reddy (IIT Roorkee Alumni, AI Expert)
Class tree
Advanced Classes: Pseudo private class attribute
class C1:
def meth1(self): self.X = 88
• Name Mangling: Any attribute or method starts with “__” and not end with “__”
def meth2(self): print(self.X) are automatically expanded to include the name of enclosing class at their front.
class C2:
def metha(self): self.X = 99 class spam:
def methb(self): print(self.X) __X = 88
I = C3() print(I._spam__X)
print(I.X) Output: 88
Output: 111
I.meth1() • Name mangling happens only for names (attributes/methods) appear inside a
print(I.X) class statement's code.
Output: 88
I.metha() • Name mangling is applicable for both class attributes and method and instance
print(I.X) attributes which are prefixed with “__”
Output: 99
• These mangled names sometimes misleadingly called as private attributes. But,
they are really not private attributes. Hence, named as Pseudo private attributes.
• There is no restriction for other classes to access these attributes and hence
these are not really private attributes.
D. Chaitanya Reddy (IIT Roorkee Alumni, AI Expert)
• The purpose of these mangled attributes is to avoid attribute name collisions
Contd…
Advanced Classes: Pseudo private class attribute
class C1:
def set_value(self, val): self.__X = val
def get_value(self): return self.__X
Mangled names are NOT required use to
class C2: access the attributes inside the class's
def set_info(self, info): self.__X = info statement.
def get_info(self): return self.__X
I = C3()
I._C1__X = 25
I._C2__X = 42 One way of accessing the pseudo private
Mangled names are required to access the attributes from outside of the class.
attributes outside the class's statement.
J = C3()
J.set_value(55)
J.set_info(10)
Other way of accessing the pseudo private
attributes from outside of the class. Encapsulation
print(J.get_value(), J.get_info())
Output: 55, 10
D. Chaitanya Reddy (IIT Roorkee Alumni, AI Expert)
Advanced Classes: Attribute namespaces, Slots
Attribute Namespaces: __dict__ Slots: Technique that allows us to assign a sequence/list of
class limiter:
Attribute name space attribute names (in string format) to a special __slots__
min_age = 34
Class attributes
class attribute in order to limit and fix the attributes
count_limit = 100 present in every instance of class.
max_age = 72
Every instance of limiter class must have only 3 attribute with
def __init__(self, username, designation): these names in it with no default values assigned.
Ex:
self.name = username But, limiter class allows
self.job = designation Instance attributes attributes (class attributes)
class limiter:
__slots__ = ['age', 'name', 'job'] with any names.
print(limiter.__dict__) Shows list of all class attributes (both user defined and
Output: python default provided def __init__(self, username, designation):
{'__module__': '__main__', 'min_age': 34, 'count_limit': 100, self.name = username
'max_age': 72, '__init__': <function limiter.__init__ at self.job = designation
0x0000019AD7DDA040>, '__dict__': <attribute '__dict__' of
'limiter' objects>, '__weakref__': <attribute '__weakref__' of
x = limiter('mahesh', 'developer')
'limiter' objects>, '__doc__': None}
print(x.name, x.job)
Output: mahesh developer
Shows list of all x instance attributes
x = limiter('mahesh', 'developer') Instance attribute must also assigned with some value
print(x.__dict__) print(x.age) before being used/referenced.
Output: {'name': 'mahesh', 'job': 'developer'} AttributeError : age
Another Instance attribute.
y = limiter('david', 'manager') Shows list of all y instance attributes x.age = 40
print(y.__dict__) print(x.age)
Output: {'name': 'david', 'job': 'manager'} Output: 40
Instance can’t have any attribute apart from defined slot
attributes: age, name, job.
x.city
D. Chaitanya Reddy (IIT Roorkee = Expert)
Alumni, AI 'Mumbai'
Attribute Error: 'limiter' object has no attribute 'city'.
Contd…
Advanced Classes: Attribute namespaces, Slots
class slotful: class slotdic:
__slots__ = ['age', 'name', 'job'] __slots__ = ['age', 'name', 'job', '__dict__']
lmt = 120 lmt = 120
ret_age = 75 ret_age = 75
print(x.__slots__)
Output: ['age', 'name', 'job'] print(z.__slots__)
Output: ['age', 'name', 'job', '__dict__']
D. Chaitanya Reddy (IIT Roorkee Alumni, AI Expert)
Contd…
Advanced Classes: Attribute namespaces, Slots
class E: Slots Usage Rules
__slots__ = ['age', 'name']
• Slots in sub classes are meaningless when absent in its super
class D(E): classes.
__slots__ = ['job', '__dict__']
class C: pass
class D(C): __slots__ = ['a']
X = D()
X.age = 45; X.name='Ramesh'; X.job = 'anchor'
X = D()
X.a = 1; X.b = 2
print(E.__dict__) print(X.__dict__)
Output: {'__module__': '__main__', '__slots__': ['age', Output: {'b': 2}
'name'], 'age': <member 'age' of 'E' objects>, 'name':
<member 'name' of 'E' objects>, '__doc__': None}
print(D.__dict__) • Slots in super classes are meaningless when absent in its sub
Output: {'__module__': '__main__', '__slots__': ['job', classes
'__dict__'], 'job': <member 'job' of 'D' objects>,
'__dict__': <attribute '__dict__' of 'D' objects>, class C: __slots__ = ['a']
'__doc__': None} class D(C): pass
print(X.__dict__) X = D()
Output: {'age': 45, 'name': 'Ramesh', 'job': 'anchor'} X.a = 1; X.b = 2
print(X.__dict__)
Output: {'b': 2}
print(E.__slots__)
Output: ['age', 'name']
print(D.__slots__) or print(X.__slots__)
Output: ['job', '__dict__'] D. Chaitanya Reddy (IIT Roorkee Alumni, AI Expert)
Contd…
Advanced Classes: Attribute namespaces, Slots
Slots Usage Rules Benefits of Slots
• Overriding Slots in sub classes makes super classes slots • Python reserves just enough space in each instance to
meaningless. hold a value for each slot attribute along with
class C: __slots__ = ['a'] inherited attributes in the super class.
class D(C): __slots__ = ['a']
X = D()
X.a = 1
print(X.__slots__) • Helps python to mange the memory very effectively.
Output: ['a']
X = D()
2) If the class have multiple super
X = C()
X.act() X.act() classes, always use class names to
Output: Output: call specific super class method.
spam B
D. Chaitanya Reddy (IIT Roorkee Alumni, AI Expert)
eggs C
Exceptions
x = 'spam'
try:
fetcher(x, 4)
except IndexError:
print('got Index err')
statement(s) # always run this code in any case. Must exists if try does not have at least one exception handler.
D. Chaitanya Reddy (IIT Roorkee Alumni, AI Expert)
Can be optional if try have at least one exception handler.
Contd…
Exceptions
Ex:
def action(x, y):
print(x + y)
try:
action([1, 2, 3], 6)
except NameError:
print('got Name Error')
Output:
except TypeError as err:
got Type Error.
print('got Type Error.')
taking care of clean up code here
except:
After the try statement.
print('got some different error')
else:
print('No Error found.')
finally:
print('taking care of clean up code here')
print('After the try statement.')
matching
No
except block
The finally block gets executed always: No
finally found?
block Yes
1) Exception occurred and handled
exists?
2) Exception occurred and not handled Execute matched
Yes except block code
3) No exception occurred.
Execute finally to terminate the
4) New exception triggered in except
blocks/else blocks if present. block code raised exception
End of the try Skip the rest of the code after try
statement. Starts statement and the unterminated
No Exception Yes
executing code after exception propagated to caller
exists? D. Chaitanya Reddy (IIT Roorkee Alumni, AI Expert)
the try statement. code/top level code.
Exceptions – Examples
Ex1:
def action(x, y):
print(x + y)
try:
print('try block starting...')
action([1, 2, 3], 6)
print('try block ending...')
Output:
except NameError: print('got Name Error')
try block starting...
except (IndexError, KeyError):
got Type or ZeroDivError.
print('got Index or key error')
taking care of clean up code here
except AttributeError as var:
After the try statement.
print('got Attr Error:', var)
except (TypeError, ZeroDivisionError) as err:
print('got Type or ZeroDivError.')
except: print('got some different error')
else: print('No Error found.')
finally:
print('taking care of clean up code here')
print('After the try statement.') TypeError: can only concatenate list (not "int") to list
Output: Output:
all clean up code here taking care of clean up code here
---------------------------------------------------- -----------------------------------------------------------------
TypeError Traceback (most recent call last) TypeError Traceback (most recent call last)
Input In [12], in <cell line: 1>() Input In [14], in <cell line: 1>()
----> 1 action('mahesh', 4) This is implicit exception ----> 1 action('mahesh', 4)
and we do not have any
Input In [11], in action(x, y) control on the message in Input In [13], in action(x, y)
1 def action(x, y): implicit exceptions. 1 def action(x, y):
2 try: 2 try:
----> 3 print(x + y) ----> 3 if isinstance(x, str): raise TypeError('you are tyring to
4 except (AttributeError) as err: add wrong types. please fix it.') This exception raised
5 print('got Type Error.') 4 else: print(x + y) explicitly and hence have
5 except (AttributeError) as err: full control on the message.
TypeError: can only concatenate str (not "int") to str
D. Chaitanya Reddy (IIT Roorkee Alumni, AI Expert)
TypeError: you are tyring to add wrong types. please fix it.
Contd…
Exceptions – raise statement
Output:
class MyExc(Exception): pass
taking care of clean up code here
def action(x, y): ---------------------------------------------------------------
MyExc Traceback (most recent call last)
try: We can raise exception Input In [18], in <cell line: 11>()
belongs to any exception 8 print('got Type Error.')
if isinstance(x, str): class. 9 finally: print('taking care of clean up code here')
raise MyExc('Error found. Fix it.') ---> 11 action('mahesh', 4)
else: print(x + y)
Input In [18], in action(x, y)
except (AttributeError) as err: 3 try:
4 if isinstance(x, str):
print('got Type Error.') ----> 5 raise MyExc(Error found. Fix it.')
finally: print('taking care of clean up code here') 6 else: print(x + y)
7 except (AttributeError) as err:
raise MyExc(Error found. Fix it.') exc = MyExc(Error found. Fix it.')
else: print(x + y) raise exc
except MyExc as err: else: print(x + y)
print('got Type Error.') except MyExc as err:
finally: print('all clean up code here') print('got Type Error.')
finally: print('all clean up code here')
action('mahesh', 4)
action('mahesh', 4)
Output:
got Type Error. Output:
all clean up code here got Type Error.
all clean up code here
action(-2, 4) action('mahesh', 4)
Output: Output:
('negative number', -2, 'addition') ('not a number', 'mahesh', 'addition')
negative number -2 mahesh addition
print('propagating Type Error.') TypeError: can only concatenate str (not "int") to str
raise MyExc('Bad Types') from err
The above exception was the direct cause of the following exception:
finally: print('all clean up code here')
MyExc Traceback (most recent call last)
Input In [28], in <cell line: 11>()
action('mahesh', 4) 8 raise MyExc('Bad Types') from err
9 finally: print('all clean up code here')
---> 11 action('mahesh', 4)
finally: print('all clean up code here') ZeroDivisionError Traceback (most recent call last)
Input In [29], in <cell line: 11>()
8 res = 100/cnt
action('mahesh', 4) 9 finally: print('all clean up code here')
---> 11 action('mahesh', 4)
Exception
Superclass for all numeric errors Superclass for all indexing errors
for both sequences, mappings