Module 1 - Day 4

%load_problem numeric_value
Problem: Numeric Value

In financial terms a negative balance is represented with round barackets around the number instead of - sign. Write a function numeric_value which returns actual numeric value. For example a value "(1234)" should get -1234 as numeric value. while "34" will get value as 34

>>> numeric_value("(1234)")
-1234
>>> numeric_value("42")
42

You can verify your solution using:

%verify_problem numeric_value

# your code here
def numeric_value(textnum):
    replaced = textnum.replace("(", "-").replace(")", "")
    return int(replaced)
"(2323)".replace("(", "-")
'-2323)'
"(2323)".replace("(", "-").replace(")", "")
'-2323'
converted = "(2323)".replace("(", "-").replace(")", "")
int(converted)
-2323
%verify_problem numeric_value
βœ“ numeric_value("(42)")
βœ“ numeric_value("42")
πŸŽ‰ Congratulations! You have successfully solved problem numeric_value!!
%load_problem reverse_digits
Problem: Reverse Digits

Write a function reverse_digits which will reverse digits of multi digit number and return new formed integer.

>>> reverse_digits(1234)
4321

You can verify your solution using:

%verify_problem reverse_digits

# your code here


x = 34545
name = "vikrant"
name[:] # copy
'vikrant'
name[::-1] # reverse
'tnarkiv'
l = [1, 2, 3, 4, 5]
l.reverse() # this will reverse in place
l
[5, 4, 3, 2, 1]
str(x) # either make use slicing logic or covert this text into list and them make use list method
'34545'
y = str(x)
y[::-1]
'54543'
z = list(y)
z.reverse()
z
['5', '4', '5', '4', '3']
"".join(z)
'54543'
def reverse_digits(num):
    strnum = str(num)
    reversed = strnum[::-1]
    return int(reversed)
%verify_problem reverse_digits
βœ“ reverse_digits(4223)
βœ“ reverse_digits(1)
βœ“ reverse_digits(123)
πŸŽ‰ Congratulations! You have successfully solved problem reverse_digits!!
def reverse_digits(num):
    strnum = str(num)
    digits = list(strnum)
    digits.reverse()
    return int("".join(digits))
%verify_problem reverse_digits
βœ“ reverse_digits(4223)
βœ“ reverse_digits(1)
βœ“ reverse_digits(123)
πŸŽ‰ Congratulations! You have successfully solved problem reverse_digits!!

Scope of variables

X = 10

def foo():
    X = 20

foo()
print(X)
10
del X
print(X)
NameError: name 'X' is not defined
X = 10

def foo():
    # X is not defined in function locally, but is there globaly
    print(X)

foo()
10

If a function refers to a variable X

  • It looks if X is there in local namespace (function’s namespace).
  • If X is in local namespace it will make use of it.
  • If X is not available in local namespace, then it will ask global namespace, do you have it and I will use it for reading purpose!
del X
X = 10

def foo():
    X = 20
    print("print from function X =",X)
    
foo()
print("print from gloabl X =",X)
print from function X = 20
print from gloabl X = 10
del X
X = 10 

def foo():
    X = X + 1 # conflict is ... you want to create local variable and also use global variable


foo()
print(X)
UnboundLocalError: local variable 'X' referenced before assignment
l
[5, 4, 3, 2, 1]
l.append(0)
l
[5, 4, 3, 2, 1, 0]
X = [1, 1, 1]

def appendzero(a):
    a.append(0) 

appendzero(X)
print(X)
[1, 1, 1, 0]

Why this has happened?

  • arguments are referering to same object which is there in global namespace
  • only name with which it was refered in function is different
i = 10
i.add(1) # this method is not supperted in ints
AttributeError: 'int' object has no attribute 'add'
y = 10

def addone(x): 
    x = x + 1 # the assignment operator make x local

addone(y) # the argument has connected x and y to same location
print(y)
10

[1, 1, 2] + [0, 0, 0] # concatenates the lists
[1, 1, 2, 0, 0, 0]
X = [1, 1, 1]

def appendzero(a):
    a = a + [0] # the operation is same but it will not modify a in place
    # this will create new a in localnamespace

appendzero(X)
print(X)
[1, 1, 1]
l
[5, 4, 3, 2, 1, 0]
l.insert(2, -3)
l
[5, 4, -3, 3, 2, 1, 0]
ones = [1, 1, 1, 1]
ones.index(1) # this returns
0
ones.append(0) # this does not return ... any function or method that does not return , actually returns None
ones
[1, 1, 1, 1, 0]
None.append(0)
AttributeError: 'NoneType' object has no attribute 'append'
ones.insert(2, -1) # does not return, modifies the list in place
ones.insert(2, -1).append(1)
AttributeError: 'NoneType' object has no attribute 'append'

Function Arguments

Named Arguments

def compute_cylinder_volume(radius, height):
    return 3.14*radius**2*height
    
compute_cylinder_volume(1, 10) # I want to compute volume of a cylinder with radius one and height 10
31.400000000000002
compute_cylinder_volume(10, 1) # by mistake I changed the order of arguments
314.0
compute_cylinder_volume(radius=1, height=10) # named arguments
31.400000000000002
compute_cylinder_volume(height=10, radius=1) # in named arguments order does not matter
31.400000000000002

Default argument

def simple_interest(principle , time_years, rate_of_interest):
    return P*N*R/100
def simple_interest(principle , time_years, rate_of_interest=3): 
    return principle*time_years*rate_of_interest/100
simple_interest(50000, 5) # even if I do not give rate_of_interest.. it will take default
7500.0
simple_interest(50000, 5, 3.5) # you can also pass value of argument
8750.0
simple_interest(10000, rate_of_interest=3.5, time_years=5) # condition is once you start giving named everything after that has to be named!
1750.0
simple_interest(principle=10000, 5, 3.5)
SyntaxError: positional argument follows keyword argument (3140448538.py, line 1)

Condition is

  • positional arguments should be first and then named arguments
  • positional arguments can not be after named arguments
def simple_interest(principle , rate_of_interest=3, time_years): 
    return principle*time_years*rate_of_interest/100
SyntaxError: non-default argument follows default argument (1681493531.py, line 1)

Functions as arguments

len(l)
7
def square(x):
    return x*x

def cube(x):
    return x**3

def sum_of_squares(a, b):
    return square(a) + square(b)

def diagonal_of_right_angled_tr(side1, side2):
    return sum_of_squares(side1, side2)**0.5
diagonal_of_right_angled_tr(3, 4)
5.0
del X
del foo
X
NameError: name 'X' is not defined
X = 10
X
10
foo
NameError: name 'foo' is not defined
def foo():
    pass
foo
<function __main__.foo()>
def say_hello(name):
    print("Hello", name)
say_hello("arcesium")
Hello arcesium
Y = X
Y
10
greetings = say_hello # functions are just like ordinary variables
greetings
<function __main__.say_hello(name)>
greetings("arcesium")
Hello arcesium
a = 42
square(a)
1764
def sumof(a, b, property):
    return property(a) + property(b)
sumof(4, 5, square)
41
sumof(4, 5, cube)
189
nums = [4324, 45435, 1, 32, 234, 3, 5]
max(nums) # max know how to compare two numbers!
45435
words = ["one", "two", "three", "four", "five", "six"]
max(words) # fort by alphabetical order and whatever is at last 
'two'
max(words, key=len) # found a word with max length
'three'
#max(entries_from_database_for_tonight, key=boold_pressure)
words
['one', 'two', 'three', 'four', 'five', 'six']
len('one')
3
len('two')
3
len('three')
5
def myown_len(w):
    print("finding length of", w)
    return len(w)
max(words, key=myown_len) # this will call myown_len on every item from the list
finding length of one
finding length of two
finding length of three
finding length of four
finding length of five
finding length of six
'three'
(1, 2, 3, 4) # tuple
(1, 2, 3, 4)
t = (1, 2, 3, 4)
t[0]
1
t[3]
4
t.append(0)
AttributeError: 'tuple' object has no attribute 'append'
ones
[1, 1, -1, -1, 1, 1, 0]
ones[3] = 0
ones
[1, 1, -1, 0, 1, 1, 0]
t[0] = -1 # tuples are immutable objects
TypeError: 'tuple' object does not support item assignment

Lets have a list of records. Every record has stock name, price and today’s gain


records = [("TATA", 200.0, 5.5),
           ("INFY", 2000.0, -5),
           ("RELIANCE", 1505.5, 50.0),
           ("HCL", 1200, 70.5)]
           
max(records) # this will sort by first columns, name and return the last
('TATA', 200.0, 5.5)
r = ('TATA', 200.0, 5.5)
r[0] # name
'TATA'
r[1] # price
200.0
r[2] # gain
5.5

I am interested in finding a record with max price

def get_price(record):
    return record[1] # will return price
max(records, key=get_price) # max price
('INFY', 2000.0, -5)
min(records, key=get_price) # min price
('TATA', 200.0, 5.5)
max(records, get_price) # it named only argument
TypeError: '>' not supported between instances of 'function' and 'list'
max(records, key=get_price)
('INFY', 2000.0, -5)
min(records, key=get_price)
('TATA', 200.0, 5.5)
sorted(records, key=get_price)
[('TATA', 200.0, 5.5),
 ('HCL', 1200, 70.5),
 ('RELIANCE', 1505.5, 50.0),
 ('INFY', 2000.0, -5)]
copy_of_records = records[:]
copy_of_records.sort(key=get_price)
copy_of_records # sorted by price
[('TATA', 200.0, 5.5),
 ('HCL', 1200, 70.5),
 ('RELIANCE', 1505.5, 50.0),
 ('INFY', 2000.0, -5)]
records
[('TATA', 200.0, 5.5),
 ('INFY', 2000.0, -5),
 ('RELIANCE', 1505.5, 50.0),
 ('HCL', 1200, 70.5)]
def get_gain(record):
    return record[2]
copy_of_records.sort(key=get_gain)
copy_of_records # sorted by gain
[('INFY', 2000.0, -5),
 ('TATA', 200.0, 5.5),
 ('RELIANCE', 1505.5, 50.0),
 ('HCL', 1200, 70.5)]
nums = [123, 576, 899, 900, -910]
max(nums, key=square)
-910
words
['one', 'two', 'three', 'four', 'five', 'six']
records
[('TATA', 200.0, 5.5),
 ('INFY', 2000.0, -5),
 ('RELIANCE', 1505.5, 50.0),
 ('HCL', 1200, 70.5)]
get_price(records[0])
200.0
get_price(records[1])
2000.0
record = records[0]
record
('TATA', 200.0, 5.5)
words
['one', 'two', 'three', 'four', 'five', 'six']
records
[('TATA', 200.0, 5.5),
 ('INFY', 2000.0, -5),
 ('RELIANCE', 1505.5, 50.0),
 ('HCL', 1200, 70.5)]
record
('TATA', 200.0, 5.5)
record[1]
200.0
some_entries = [(5, 6, 7),
                (5, 2, 3),
                (5, 8, 9), 
                (5, 3, 4)]
max(some_entries) # will look into 2nd column because there is tie in first one
(5, 8, 9)
some_entries = [(5, 6, 7),
                (5, 2, 3),
                (5, 8, 9), 
                (5, 8, 4)]
max(some_entries) # will look into 3rd column also because there is tie in 1st and 2nd col
(5, 8, 9)
max(some_entries[1])
some_entries[1]
(5, 2, 3)
max((5, 2, 3))
5
def get_price(record):
    return record[1] # will return price
    
max(records, key=get_price)
('INFY', 2000.0, -5)
max(records[1], key=get_price)
TypeError: 'float' object is not subscriptable
max(('INFY', 2000.0, -5), key=get_price)
TypeError: 'float' object is not subscriptable
get_price("INFY")
'N'
get_price(2000.0)
TypeError: 'float' object is not subscriptable

Looping machanism

We can loop over any collection by using for loop

classroom = ['chaittanya' , 'shuddhatma', 'rohit', 'simran', 'alex']
# here student is loop variable, it will get values from the collection one by one automatically. 
# classroom is any collection on which you want to 
for student in classroom:
    print("Hello", student)
    print(student.capitalize())
    # after this line execution goes back to first line of for loop body
Hello chaittanya
Chaittanya
Hello shuddhatma
Shuddhatma
Hello rohit
Rohit
Hello simran
Simran
Hello alex
Alex
for c in "this text as a collection":
    print(c)
t
h
i
s
 
t
e
x
t
 
a
s
 
a
 
c
o
l
l
e
c
t
i
o
n

print function by default prints \n (newline) after every print call

for c in "this text as a collection":
    print(c, end=",")
t,h,i,s, ,t,e,x,t, ,a,s, ,a, ,c,o,l,l,e,c,t,i,o,n,
for record in records:
    print("Name:", record[0], "price:", record[1])
Name: TATA price: 200.0
Name: INFY price: 2000.0
Name: RELIANCE price: 1505.5
Name: HCL price: 1200
sqrs = [] # empty
for i in range(10): 
    sqrs.append(i**2)
sqrs
[0, 1, 4, 9, 16, 25, 36, 49, 64, 81]
def compute_squares(nums):
    sqr = []
    for i in nums:
        sqr.append(i*i)
    return sqr
compute_squares([1, 3, 2, 5, 2, 8, 3])
[1, 9, 4, 25, 4, 64, 9]
def squares(nums):
    sqr = []
    for i in nums:
        sqr.append(i*i)
        return sqr # the momenet return statement is executed function call is over
squares([2, 3, 4, 5, 64, 3])
[4]
squares([5, 3, 4, 5, 64, 3])
[25]

problem

  • Write your own function called mysum. It takes list of numbers and finds sum of those
  • Write your own function called product. it takes list of numbers and returns product of all numbers from the list
sum([1, 2, 3, 4, 5])
15
def mysum(nums):
    return sum(nums) # not like this! make use of for loop to compute from scratch
1 1 1 2 3 4
0
0+1 = 1
1+1 = 2
2+1 = 3
3+2 = 5
.
.
def mysum(nums):
    s = 0
    for n in nums:
        s = s + n

    return s
mysum(range(10)) # sum of 0 to 9
45
mysum(range(101)) # sum of first 100 number
5050
def product(nums):
    p = 1
    for i in nums:
        p = p * i

    return p
product([1, 2, 3, 4])
24
def factorial(n):
    return product(range(1, n+1)) # this will create number from 1 to n
factorial(5)
120
factorial(10)
3628800
def factorial1(n):
    p = 1
    for i in range(1, n+1):
        p = p * i
    return p
  • Pythonish way of programming is DRY (Do not repeate yourself!) .
  • Make use of already written function built new ones

while loop

1 1 2 3 5 8 13 21 …. you add last two Fibonacci numbers to get next one

print fibonacci numbers less than n

def print_fib(n):
    """Print fibonnaci numbers less than n
    """
    curr, prev = 1, 1

    while prev < n: # after while put a condition.
        print(prev, end=",")
        curr, prev = prev+curr, curr
print_fib(20)
1,1,2,3,5,8,13,
print_fib(50)
1,1,2,3,5,8,13,21,34,
2 < 3
True
3 > 2
True
"name".startswith("n")
True

Conditions

"name".startswith("v")
False
"hel" in "hello"
True
"one" in words # check if "one" is in the list words
True
words
['one', 'two', 'three', 'four', 'five', 'six']
data = {"x":1, "y": 2}
"x" in data
True
x, y = 10, 30
x > y
False
x >= y
False
x == y
False
x != y
True
"name" == words[0]
False
def mymax(a, b):
    if a >= b:
        return a
    else:
        return b
mymax(2, 3)
3
if "hel" in "cell":
    pass
elif "cel" in "hell":
    pass
elif "del" in "bell":
    pass
else:
    pass
def countdown(n):
    i = n
    while i >0:
        print(i)
        i = i - 1 # if you forget to change the variable on which condition is dependent... while loop will execute forever
countdown(5)
5
4
3
2
1