Scope Of Variables
File: "global1.py"
x1 = 10
def function1() :
#global x1
x1 = 100
def outer():
x1 = 'old'
print ( x1 )
def changer():
nonlocal x1;
x1 = 'new'
#print( x1 )
changer()
print( x1 )
function1()
#outer()
print( x1 )
Output:
10
We call "function1" but the statement "x1=100" creates a local variable called "x1" to the function "function1" . By setting "x1=100" we do not change the global variable. In order to change the global variable we use the word "global" .
File: "global2.py"
x1 = 10
def function1() :
global x1
x1 = 100
def outer():
x1 = 'old'
print ( x1 )
def changer():
nonlocal x1;
x1 = 'new'
#print( x1 )
changer()
print( x1 )
function1()
#outer()
print( x1 )
Output:
100
We can have a function inside a function ( nested functions ) . The "outer" function has a variable called "x1" . The inner function cannot change the outer variable. If we do :
File: "global3.py"
x1 = 10
def function1() :
global x1
x1 = 100
def outer():
x1 = 'old'
print ( x1 )
def changer():
#nonlocal x1;
x1 = 'new'
#print( x1 )
changer()
print( x1 )
#function1()
outer()
#print( x1 )
Output:
old
old
For the function "changer" to change the variable in "outer" it has to use the "nolocal" declaration.
File: "global4.py"
x1 = 10
def function1() :
global x1
x1 = 100
def outer():
x1 = 'old'
print ( x1 )
def changer():
nonlocal x1;
x1 = 'new'
#print( x1 )
changer()
print( x1 )
#function1()
outer()
print( x1 )
Output:
old
new
10
With the "nonlocal" keyword the change "x1="new" " is done to the variable in the "outer" function.
Notice the below will give an error:
File: "global5.py"
x1 = 10
def function1() :
global x1
x1 = 100
def outer():
global x1
x1 = 'old'
print ( x1 )
def changer():
nonlocal x1;
x1 = 'new'
#print( x1 )
changer()
print( x1 )
#function1()
outer()
print( x1 )
Output:
[amittal@hills Global]$ python3 global5.py
File "global5.py", line 13
nonlocal x1;
^
SyntaxError: no binding for nonlocal 'x1' found
Now the system cannot find the variable "x1" in the "outer" function because the "x1" refers to the global variable in the outer function.
A generator is like a function but stores it's state. It uses the "yield" instead of return and control stops at the statement after "yield" till the function is called again. If there are no more yields then the function throws an Exception.
File: "gen3.py"
def simple_generator_function():
print( "Step1" )
yield 1
print( "Step2" )
yield 2
print( "Step3" )
yield 3
print( "Step4" )
gen1 = simple_generator_function( )
print( next( gen1 ) )
print( next( gen1 ) )
print( next( gen1 ) )
print( "Next1" )
print( next( gen1 ) )
#print( "Next2" )
#next( gen1 )
print( "Next3" )
#Use it as an iterator
#for x1 in gen1 :
# print( x1 )
Finding primes in a range.
File: gen_prime.py
def getPrimes( limit1 ):
i1=2
n1 = 2
while( True and n1 < limit1 ):
prime=True
for a1 in range(2, n1):
if(n1%a1 == 0):
prime=False
break
if(prime):
yield n1
n1 = n1 + 1
genObj = getPrimes(100)
#for x1 in range(100) :
# print( next( genObj ) )
class Primes :
def __init__( self, limit ) :
self.limit = limit
self.currentNo = 2
def isPrime(self, number1 ) :
prime = True
for a1 in range( 2, number1 ):
if( number1 % a1 == 0):
prime=False
break
return prime
def getNextPrime(self ) :
while True :
if self.isPrime( self.currentNo ) :
self.result = self.currentNo
self.currentNo = self.currentNo + 1
return self.result
if ( self.currentNo > self.limit ) :
raise StopIteration
self.currentNo = self.currentNo + 1
primesObj = Primes(100)
for i1 in range( 100 ) :
print( primesObj.getNextPrime() )
1)
Write a generator to generate powers of 2 in a range:
File: power2.py
def power2( number1 ) :
gen1 = power2(4 )
for x1 in gen1 :
print( x1 )
Output:
2
4
8
16
2)
Write a generator to give the Factorials in the range.
def factorial( number1 ) :
gen1 = factorial(4 )
for x1 in gen1 :
print( x1 )
Output:
1
2
6
24
3)
Fibonaaci Series
def fib( number1 ) :
fib1 = 1
fib2 = 1
for x1 in range( 1 , number1+1 ) :
if x1 == 1 or x1 == 2 :
yield 1
else:
result = fib1 + fib2
yield ( result )
fib1 = fib2
fib2 = result
gen1 = fib(6 )
for x1 in gen1 :
print( x1 )
1)
def power2( number1 ) :
for x1 in range( 1, number1+1 ) :
yield 2 ** x1
gen1 = power2(4 )
for x1 in gen1 :
print( x1 )
#We can also do
gen1 = power2(4 )
print( next(gen1) )
print( next(gen1) )
print( next(gen1) )
2)
def factorial( number1 ) :
for x1 in range( 1, number1+1 ) :
result = 1
for y1 in range( 1 , x1+1 ) :
result = result * y1
yield result
gen1 = factorial(4 )
for x1 in gen1 :
print( x1 )
3)
def fib( number1 ) :
fib1 = 1
fib2 = 1
for x1 in range( 1 , number1+1 ) :
if x1 == 1 or x1 == 2 :
yield 1
else:
result = fib1 + fib2
yield ( result )
fib1 = fib2
fib2 = result
gen1 = fib(6 )
for x1 in gen1 :
print( x1 )
Modulus means remainder. So 5 mod 3 is 2 because 5 = 3*1 + 2 . Similarly 10 mod 3 is 1 .
Say we have 2 numbers x1 and y1 . We want to find the modulus of (x1 * y1 ) mod z1 .
As an example ( 5 * 4 ) mod 3 . That is 20 mod 3 which is 2 .
Let us try writing the numbers in terms of remainder.
( 3 + 2 ) * ( 3 +1 ) mod 3
Expanding the terms gives us :
( 3*3 + 3*1 + 2*3 + 2*1 ) mod 3
To find the modulus we divide the expression by 3 and notice that anything multiplied by 3 goes away .
( 3*3 + 3*1 + 2*3 + 2*1 )
---------------------------------- = 2 ( the last term )
mod 3
( 3 + 2 ) * ( 3 +1 ) mod 3
Is the same as:
( 5 mod 3 ) * ( 4 mod 3 )
---------------------------------
mod 3
If our numbers are ( 5 * 4 ) modulus 3 . We can also write this as:
( 3 + 2 ) * 5
= ( 3 * 5 + 2* 5 )
If we do modulus on the above term we see that only the term ( 2* 5 ) is relevant in order to calculate the modulus.
X1 * X2 * X3 modulus 3
X1 modulus 3 * ( x2 * X3 ) modulus 3
Also note that
5 * 5 * 5 modulus 3
is the same as
( 3+2 ) * ( 3+2 ) * ( 3+2 )
2 * 2 * 2 modulus 3
We can use this strategy to find a more efficient way of solving the problem of doing x1 to the power y1 mod z1 .
Let's say we have to do 2 to the power 5 modulus 3 .
2 * 2 *2 * 2 * 2 mod 3 = 32 mod 3 = 2
First Step
2 mod 3 is 2
Second Step
2 * 2 We store only the modulus and not the product:
4 mod 3 = 1
result = 1
Third Step
Now we are at the next power.
1 * 2 mod 3 = 2
result = 2
Fourth Step
2 * 2 mod 3 = 1
result = 1
Fifth Step
1 * 2 = 2 mod 3 = 2 is the final result
result = 2
We are storing the modulus every time instead of storing the product. That way our numbers don't become very large when the power is calculated.
Fill in the code for below functions. Some of the code has already been written out for you.
1)
File: "power1.py"
#Write 2 functions for calculating x to the power y
#One function should be recursive and the other iterative
def powerIterative( x1 , y1 ) :
#To do
return ( result )
def powerRecursive( x1 , y1 ) :
if y1 == 1 :
return x1
return( x1 * powerRecursive( x1, y1-1 ) )
#Inefficient way. Use powerIterative to find x1 power y1 and
# then do modulus d1
def findMod( x1 , y1 , d1 ) :
p1 = powerIterative( x1 , y1 )
return( p1 % d1 )
#Efficient Way . Do modulus at each step in the iteration
def findModEff( x1 , y1 , d1 ) :
return result
#Test Cases
print("2 to the power 3" , powerIterative( 2 , 3 ), "\n" )
print("4 to the power 3" , powerRecursive( 4 , 3 ) , "\n" )
print( "findMod 5 to the power 2 mod 3" , findMod( 5 , 2 , 3 ) , "\n" )
print( "findMod 2 to the power 5 mod 3" , findMod( 2 , 5 , 13 ) , "\n" )
print( "findMod 5 to the power 2 mod 3" , findModEff ( 5 , 2 , 3 ) , "\n" )
print("findMod 2 to the power 5 mod 3" , findModEff ( 2 , 5 , 13 ), "\n" )
The output should be:
2 to the power 3 8
4 to the power 3 64
findMod 5 to the power 2 mod 3 1
findMod 2 to the power 5 mod 3 6
findMod 5 to the power 2 mod 3 1
findMod 2 to the power 5 mod 3 6
1)
#Write 2 functions for calculating x to the power y
#One function should be recursive and the other iterative
def powerIterative( x1 , y1 ) :
result = 1
for i1 in range( y1 ) :
result = result * x1
return ( result )
def powerRecursive( x1 , y1 ) :
if y1 == 1 :
return x1
return( x1 * powerRecursive( x1, y1-1 ) )
#print( powerRecursive( 2 ,3 ) )
#Inefficient way. Use powerIterative to find x1 power y1 and
# then do modulus d1
def findMod( x1 , y1 , d1 ) :
p1 = powerIterative( x1 , y1 )
return( p1 % d1 )
#Efficient Way . Do modulus at each step in the iteration
def findModEff( x1 , y1 , d1 ) :
mod1 = x1 % d1
result = 1 ;
for x1 in range(0, y1 ):
result = result * mod1
result = result % d1
#print( "temp:" , temp , " x1: " , x1 )
return result
#Test Cases
print("2 to the power 3" , powerIterative( 2 , 3 ), "\n" )
print("4 to the power 3" , powerRecursive( 4 , 3 ) , "\n" )
print( "findMod 5 to the power 2 mod 3" , findMod( 5 , 2 , 3 ) , "\n" )
print( "findMod 2 to the power 5 mod 3" , findMod( 2 , 5 , 13 ) , "\n" )
print( "findMod 5 to the power 2 mod 3" , findModEff ( 5 , 2 , 3 ) , "\n" )
print("findMod 2 to the power 5 mod 3" , findModEff ( 2 , 5 , 13 ), "\n" )