def count_digits(n):
return len(str(n))Module I - Day 3
Python Foundation
Nov-Jan 2025 batch, Vikrant Patil
Date: 26 Nov 2025
Click here for All Notes
login to https://traininghub.vikrant.dev/ with your username and create a notebook with name module1-day3.ipynb
© Vikrant Patil
count_digits(2**100)31
def sum_naturals(n):
"""this function sums first n natural numbers
"""
return sum(range(1, n+1))help(sum_naturals)Help on function sum_naturals in module __main__:
sum_naturals(n)
this function sums first n natural numbers
sum_naturals(10)55
nums = [1, 2, 3]
[1, 2, 3, 3, 2, 1]
nums[::-1] # this will reverse the list
nums + nums[::-1]def symmetric_join(items):
return items + items[::-1]symmetric_join([1, 2, 3])[1, 2, 3, 3, 2, 1]
palindrom is some text/list that reads same from both sides
def is_palindrom(text):
return text == text[::-1] # == is for comparing if they are equalis_palindrom("madam")True
is_palindrom("sir")False
conditions
if condition:
do something
else:
alternative path
onle one of the blocks will be executed even if multiple are true. If multiple conditions are true in if/elif statements then first true statement will exucute its code block
if codition1:
procedure1
elif condition2:
procedure2
elif condition3:
procedure3
else:
alternative
x, y = 42, 45x == y # check if x is equal to yFalse
x != y # check if x is not equal to yTrue
x > y # check if x > yFalse
x < yTrue
x >= yFalse
x <= yTrue
text = "Python programming class""Python" in textTrue
"java" not in textTrue
x == y and "Python" in text False
(x == y) and ("Python" in text) False
x == y or "Python" in text True
def mymax(a, b):
if a > b:
return a
else:
return bmymax(5, 6)6
problem - Write a function max3 which will find maximum number from given three numbers.
>>> max3(3, 5, 8)
8
max2 = mymax # alias created for a function
def max3(x, y, z):
m = max2(x, y)
return max2(m, z)
def max3(x, y, z):
return max2(max2(x, y), z)
def max3_(x, y, z):
if x > y and x > z:
return x
elif y > z and y > x:
return y
else:
return zmax3(5, 45, 3)45
def max5(a, b, c, d, e):
return max3(max3(a, b, c), d, e)min([1, 2, 3, 4])1
min(1, 2, 3)1
max(1, 3, 5)5
global vs local
Namespace and Functions
Everytime we call a function. With entry to function, it creates it’s own name space. And in that name space it creates names for function parameters. These names point to same location from where the pararameters are passed.
Scope of Variables
Variables defined at top level of program where main script starts are global variables. At this level globals and locals are same. Once we call a function, as soon as function execution starts, all variables created there are local to that function. Variables passed as argument to function are passed by reference. it means they refer to same object, which is passed to function. Only difference is that it is called by a different name inside the function. This is because with every function call a new namespace is created. And variables in function reside in this newly created namespace. As soon as function call is over, namespace created with function call is also deleted, so all variables in it.
Scope rules - Any name the statement refers, is looked in local scope first. - if name is not there is local scope, global scope is chacked for reading - if local name exists, then there is no way we can get global with same name
z = 10
def func(x, y):
return x + y + z # Whenever you come across a variable - first it will look for local namaspace - if it is in local name space use it - if it is not there local namespace look into global namespace
Problem
What will this print?::
x = 10
def foo():
x = 20 # assignemnt operator will create a local variable
foo()
print(x)
Problem
What will this print?
x = 10
def foo():
print(x)
foo()
Problem
What will this print?::
x = 10
def foo():
x = x + 1
foo()
print(x)
Problem
What will this print?::
x = [1, 1, 1]
def appendzero(y): y = y + [1]
appendzero(x) print(x)
Problem
What will this print?::
x = [1, 1, 1]
def appendzero(y): y.append(0)
appendzero(x) print(x)
x = 10
def foo():
x = 20 # assignemnt operator will create a local variable
foo()
print(x) # this statement is outside the function... will be executed in global namaspace10
x = 10
def foo():
print(x) # there is no local variable with name x in foo! so it will take x from global
foo()10
x = 10
def foo():
x = x + 1 # x is also local and global
foo()
print(x)--------------------------------------------------------------------------- UnboundLocalError Traceback (most recent call last) Cell In[37], line 6 3 def foo(): 4 x = x + 1 # x is also local and global ----> 6 foo() 7 print(x) Cell In[37], line 4, in foo() 3 def foo(): ----> 4 x = x + 1 UnboundLocalError: cannot access local variable 'x' where it is not associated with a value
x = 10
def foo():
print(x + 1) # x is global
foo()
print(x)11
10
x = 10
def foo():
global x # because if this statement x is modified in global context
x = x + 1
foo()
print(x)11
x = [1, 1, 1]
def appendzero(a):
a.append(0)
appendzero(x)
print(x)[1, 1, 1, 0]
x = [1, 1, 1]
def appendone():
x = x + [1]
appendone()
print(x)--------------------------------------------------------------------------- UnboundLocalError Traceback (most recent call last) Cell In[43], line 6 3 def appendone(): 4 x = x + [1] ----> 6 appendone() 7 print(x) Cell In[43], line 4, in appendone() 3 def appendone(): ----> 4 x = x + [1] UnboundLocalError: cannot access local variable 'x' where it is not associated with a value
x = [1, 1, 1]
def appendone(a):
a.append(1)
appendone(x)
print(x)[1, 1, 1, 1]
Style guide for functions
def twice(x):
print(2*x)twice(5)10
def double(y):
return 2*ytwice(5) # this is a print10
double(5) # this is a return10
x = double(5)x10
y = twice(5)10
print(y)None
def fourtimes(x):
return double(double(x))fourtimes(5)20
def fourtimes_(x):
twice(twice(x))twice(None)--------------------------------------------------------------------------- TypeError Traceback (most recent call last) Cell In[60], line 1 ----> 1 twice(None) Cell In[46], line 2, in twice(x) 1 def twice(x): ----> 2 print(2*x) TypeError: unsupported operand type(s) for *: 'int' and 'NoneType'
fourtimes_(5)10
--------------------------------------------------------------------------- TypeError Traceback (most recent call last) Cell In[61], line 1 ----> 1 fourtimes_(5) Cell In[59], line 2, in fourtimes_(x) 1 def fourtimes_(x): ----> 2 twice(twice(x)) Cell In[46], line 2, in twice(x) 1 def twice(x): ----> 2 print(2*x) TypeError: unsupported operand type(s) for *: 'int' and 'NoneType'
twice(twice(5))
10
twice(None)10
--------------------------------------------------------------------------- TypeError Traceback (most recent call last) Cell In[63], line 1 ----> 1 twice(twice(5)) 2 10 3 twice(None) Cell In[46], line 2, in twice(x) 1 def twice(x): ----> 2 print(2*x) TypeError: unsupported operand type(s) for *: 'int' and 'NoneType'
double(double(5))20
Guidelines for writing functions
- A function should behave as a perfect black box. it has definite inputs and definte outputs
- it works only on given inputs.
- A reusable function returns the computed value
- A reusable function does not make use of gloabl variables.
- A reusable function takes all the required inputs as arguments.
About naming - give meaningful names to variables and function, you can combine multiple words together with _ to make meaningful name - Write code to read first and then execute
more about functions
- functions are first class objects in python
- it means functions are treated same as variables
def sumof(func, x, y):
return func(x) + func(y)def square(x):
return x*xsumof(square, 3, 4)25
def poly(x):
return x**2 + 2*x + 1sumof(poly, 3, 4)41
def sumofsquares(x, y):
return x**x + y**y
def sumofpoly(x, y):
return x**2 + 2*x + 1 + y**2 + 2*y + 1max([1, 2, 3, 4, 5, 6])6
words = ["one", "two", "three", "four", "five", "six"]max(words) # dictionary order'two'
sorted(words) # sorted is built in function which will do comparison of text by dictionary order['five', 'four', 'one', 'six', 'three', 'two']
min(words)'five'
max(words, key=len)'three'
sorted(words, key=len) # sort by length['one', 'two', 'six', 'four', 'five', 'three']
records = [
("TATA", 200.0, 5.5),
("INFY", 2000.0, -5),
("RELIANCE", 1505.5, 50.0),
("HCL", 1200, 70.5)
]records[('TATA', 200.0, 5.5),
('INFY', 2000.0, -5),
('RELIANCE', 1505.5, 50.0),
('HCL', 1200, 70.5)]
pairs = [(1,2), (34,5), (65, 20), (23,87), (2, 45), (1, 5), (65,12)]pairs[(1, 2), (34, 5), (65, 20), (23, 87), (2, 45), (1, 5), (65, 12)]
sorted(pairs)[(1, 2), (1, 5), (2, 45), (23, 87), (34, 5), (65, 12), (65, 20)]
sorted(records) # sorted dictionary order[('HCL', 1200, 70.5),
('INFY', 2000.0, -5),
('RELIANCE', 1505.5, 50.0),
('TATA', 200.0, 5.5)]
def get_value(r):
return r[1]records[0]('TATA', 200.0, 5.5)
get_value(records[0])200.0
sorted(records, key=get_value)[('TATA', 200.0, 5.5),
('HCL', 1200, 70.5),
('RELIANCE', 1505.5, 50.0),
('INFY', 2000.0, -5)]
def get_gain(r):
return r[2]sorted(records, key=get_gain)[('INFY', 2000.0, -5),
('TATA', 200.0, 5.5),
('RELIANCE', 1505.5, 50.0),
('HCL', 1200, 70.5)]
sorted(records, key=get_value, reverse=True)[('INFY', 2000.0, -5),
('RELIANCE', 1505.5, 50.0),
('HCL', 1200, 70.5),
('TATA', 200.0, 5.5)]
sorted(records, key=get_value)[('TATA', 200.0, 5.5),
('HCL', 1200, 70.5),
('RELIANCE', 1505.5, 50.0),
('INFY', 2000.0, -5)]
help(sorted)Help on built-in function sorted in module builtins:
sorted(iterable, /, *, key=None, reverse=False)
Return a new list containing all items from the iterable in ascending order.
A custom key function can be supplied to customize the sort order, and the
reverse flag can be set to request the result in descending order.
sorted(records, get_gain, True)--------------------------------------------------------------------------- TypeError Traceback (most recent call last) Cell In[99], line 1 ----> 1 sorted(records, get_gain, True) TypeError: sorted expected 1 argument, got 3
sorted(records, key=get_gain, reverse=True)[('HCL', 1200, 70.5),
('RELIANCE', 1505.5, 50.0),
('TATA', 200.0, 5.5),
('INFY', 2000.0, -5)]
function arguments
def cylinder_volume(radius, height):
return 3.14 * radius**2 * height cylinder_volume(2, 1) # call function with positional argument12.56
cylinder_volume(1, 2) # the order is important!6.28
cylinder_volume(height=1, radius=2) # named arguments12.56
def cylinder_volume(radius, height=1): # default argument
return 3.14 * radius**2 * height cylinder_volume(3) # height by default taken as 128.26
cylinder_volume(3, height=2)56.52
def cylinder_volume(height=1, radius): # default argument must be given after non-default argument
return 3.14 * radius**2 * height Cell In[109], line 1 def cylinder_volume(height=1, radius): # default argument must be given after non-default argument ^ SyntaxError: parameter without a default follows parameter with a default
def func(nondefault1, nondefault2, default1=1, default=2):
pass
# nondefault1, nondefault2 these are positinal arguments if we pass only values while calling
# if you pass function parameters with name while calling it is called named argumentHomework
- Write a function
voice_of_wildwhich takes an argument of species name and prints the word associated with that species’s voice. Make use use of conditions to do this. Your function should support following species and their voice as given below
=========== ====================================
species voice
=========== ====================================
bird chirp
bat echolocation
cat meow
dog bark
duck quack
any other Not supported
=========== ====================================
- \(n^{th}\) Fibonacci number is a number which can be defined as given here. \(F_{n} = F_{n-1} + F_{n-2}\) Write a function
fibonacciwhich computes \(n^{th}\) fibonacci number. if n is 0 or 1 it returns 1 else it calls back same function with argumentsn-1andn-2(When a function calls itself it is called as recursion). Do not forget to put condition for n == 0 or n == 1 , otherwise you might end up with infinite recursion.