Jan 16-20, 2023 Vikrant Patil
All notes are available online at https://notes.pipal.in/2023/arcesium_finop_jan/
© Pipal Academy LLP
Write a python script tail.py which mimics unix command tail. The command will show last few lines of a file. It takes filename as argument and optional argument -n to specify how many lines to display. By default it displays last 5 lines of the file.
$ python tail.py --help
Usage: tail.py [OPTIONS] FILENAME
Arguments:
FILENAME [required]
Options:
-n INTEGER Number of lines to display [default: 5]
--install-completion [bash|zsh|fish|powershell|pwsh]
Install completion for the specified shell.
--show-completion [bash|zsh|fish|powershell|pwsh]
Show completion for the specified shell, to
copy it or customize the installation.
--help Show this message and exit.
$ python tail.py zen.txt
Now is better than never.
Although never is often better than *right* now.
If the implementation is hard to explain, it's a bad idea.
If the implementation is easy to explain, it may be a good idea.
Namespaces are one honking great idea -- let's do more of those!
lines = """line1
line2
line3
line4
line5
line6""".split("\n")
lines
['line1', 'line2', 'line3', 'line4', 'line5', 'line6']
def head(lines, n):
for i, line in enumerate(lines):
if i < n:
print(line) # take care of \n if lines are coming from file
head(lines, 3)
line1 line2 line3
def head(lines, n):
for line in lines[:n]:
print(line) # take care of \n if lines are coming from file
head(lines, 4)
line1 line2 line3 line4
lines[-3::] # last three items
['line4', 'line5', 'line6']
def tail(filename, n):
window = []
with open(filename) as f:
for i in range(n): # get first n lines
window.append(f.readline().strip())
for line in f: # go over remaining lines
window.pop(0)
window.append(line.strip())
for line in window:
if line: ##??? what will happen if file has lines less than n?
print(line)
help(lines.pop)
Help on built-in function pop:
pop(index=-1, /) method of builtins.list instance
Remove and return item at index (default last).
Raises IndexError if list is empty or index is out of range.
tail("poem.txt", 4)
Although never is often better than *right* now. If the implementation is hard to explain, it's a bad idea. If the implementation is easy to explain, it may be a good idea. Namespaces are one honking great idea -- let's do more of those!
%%file tail.py
import typer
def tail(filename:str, n:int=typer.Option(5, "-n", help="Number of lines")):
window = []
with open(filename) as f:
for i in range(n): # get first n lines
window.append(f.readline().strip())
for line in f: # go over remaining lines
window.pop(0)
window.append(line.strip())
for line in window:
if line: ##??? what will happen if file has lines less than n?
print(line)
if __name__ == "__main__":
typer.run(tail)
Overwriting tail.py
!python tail.py --help
Usage: tail.py [OPTIONS] FILENAME Arguments: FILENAME [required] Options: -n INTEGER Number of lines [default: 5] --help Show this message and exit.
Write a script print_triangle.py to print a triangle of made of character * and of base n
python print_triangle.py 5
*
* *
* * *
* * * *
* * * * *
def triangle(n):
return ["*"*i for i in range(1, n+1)]
triangle(5)
['*', '**', '***', '****', '*****']
"any text data".center(50)
' any text data '
def pretty_print(triangle):
for line in triangle:
print(line.center(len(triangle)))
pretty_print(triangle(5))
* ** *** **** *****
def pretty_print(triangle):
n = len(triangle)
pagewidth = n + n-1
for line in triangle:
l = " ".join(list(line))
print(l.center(pagewidth))
pretty_print(triangle(5))
* * * * * * * * * * * * * * *
%%file akash_print_triangle.py
import sys
n = int(sys.argv[1])
for i in range(n):
print(" " * (n - i - 1) + "* " * (i + 1))
Writing akash_print_triangle.py
!python akash_print_triangle.py 5
* * * * * * * * * * * * * * *
def merge_lists(a,b):
new_list=[]
new_list = [item for sublist in zip(a,b) for item in sublist]
return new_list
merge_lists([1, 2, 3, 4], list("abcd"))
[1, 'a', 2, 'b', 3, 'c', 4, 'd']
a,b = [1, 2, 3, 4], list("abcd")
a
[1, 2, 3, 4]
b
['a', 'b', 'c', 'd']
[sublist for sublist in zip(a,b)]
[(1, 'a'), (2, 'b'), (3, 'c'), (4, 'd')]
[item for sublist in zip(a,b) for item in sublist]
[1, 'a', 2, 'b', 3, 'c', 4, 'd']
[item for item in sublist for sublist in zip(a,b)]
--------------------------------------------------------------------------- NameError Traceback (most recent call last) Cell In[53], line 1 ----> 1 [item for item in sublist for sublist in zip(a,b)] NameError: name 'sublist' is not defined
import time
class Timer:
def __init__(self):
self.start_time = None
self.end_time= None
def start(self):
self.start_time = time.time()
def stop(self, n):
time.sleep(n)
self.end_time = time.time()
def time_elapsed(self):
print(self.end_time-self.start_time)
timer = Timer()
timer.start()
timer.stop(2)
timer.time_elapsed()
2.001695156097412
import time
def run_for_n_seconds(n):
time.sleep(n)
print("boom... done!")
run_for_n_seconds(5)
boom... done!
t = Timer()
t.start()
tail("poem.txt",5)
run_for_n_seconds(5)
# do some more stuff
t.stop()
t.time_exapsed()
Now is better than never. Although never is often better than *right* now. If the implementation is hard to explain, it's a bad idea. If the implementation is easy to explain, it may be a good idea. Namespaces are one honking great idea -- let's do more of those! boom... done!
--------------------------------------------------------------------------- TypeError Traceback (most recent call last) Cell In[60], line 8 5 run_for_n_seconds(5) 6 # do some more stuff ----> 8 t.stop() 10 t.time_exapsed() TypeError: Timer.stop() missing 1 required positional argument: 'n'
["eat", "ate"]
"eat"
"eta"
"aet"
"tea"
"who" w-1, h-1 o-1
"eat" e=1, a=1, t=1
"tea" e=1, a=1, t=1
"23312" ->
"22133" ->
sorted("eat") # signature of an anagram!
['a', 'e', 't']
sorted("ate")
['a', 'e', 't']
sorted("tea")
['a', 'e', 't']
def SUMIFS(sum_list, criteria_list, condition):
if condition == "": #this will never be the condition
operator = "=="
value = ""
elif condition == "<>":
operator = "!="
value = ""
else:
operator = condition[:2]
value = int(condition[2:])
total = 0
for i in range(len(sum_list)):
if eval(str(criteria_list[i]) + operator + str(value)):
total += sum_list[i]
return total
Implement excel function SUMIFS as a function in python. SUMIFS(sum_list, criteria_list, condition). Here first argument is the list on which sum will be performed. Second argument is the list on which condition is checked, and third argument is condition as a string , as in excel.
possible values of condition are
< less than
<= less than or equal to
> greater than
>= greater than or equal to
<> not equal to
empty means equal
Sample run is shown below. For simplicity assume that all the data consists of integers.
>>> d = [1,2,3,4,5,4,4,5]
>>> a = [10,20,30,40,50,40,40,50]
>>> SUMIFS(d, a, "<40")
6
>>> SUMIFS(d, a, ">=40")
22
>>> SUMIFS(d, a, "40")
12
>>> SUMIFS(d, a, "<>40")
16
d = [1,2,3,4,5,4,4,5]
a = [10,20,30,40,50,40,40,50]
SUMIFS(d, a, "<40")
6
SUMIFS(d, a, ">40")
10
SUMIFS(d, a," 40")
Traceback (most recent call last): File ~/usr/local/default/lib/python3.10/site-packages/IPython/core/interactiveshell.py:3433 in run_code exec(code_obj, self.user_global_ns, self.user_ns) Cell In[74], line 1 SUMIFS(d, a," 40") Cell In[68], line 13 in SUMIFS if eval(str(criteria_list[i]) + operator + str(value)): File <string>:1 10 40 ^ SyntaxError: invalid syntax
import operator
def extract_num_op(condstrng):
if condstrng.startswith("<>"):
op = operator.ne
num = int(condstrng[2:])
elif condstrng.startswith("<="):
op = operator.le
num = int(condstrng[2:])
elif condstrng.startswith(">="):
op = operator.ge
num = int(condstrng[2:])
elif condstrng.startswith("<"):
op = operator.lt
num = int(condstrng[2:])
elif condstrng.startswith(">"):
op = operator.gt
num = int(condstrng[2:])
else:
op = operator.eq
num = int(condstrng)
return op, num
def SUMIFS(sum_list, criterion_list, condstring):
op, num = extract_num_op(condstring)
s = 0
for a, b in zip(sum_list, criterion_list):
if op(b, num):
s += a
return s
SUMIFS(d, a, ">=40")
22
SUMIFS(d, a, "40")
12
d
[1, 2, 3, 4, 5, 4, 4, 5]