%lsmagic
!ls | grep ipy
# expression that returns something
12345
# that something was assigned to "_"
_
# Error example: division by 0
1/0
Note: Some of the cells in this notebook create such errors as you will see once you try them. Notice them carefully to see how python errors are written.
# Following import is for printing in python 3.x format
# ignore this line for now
from __future__ import print_function
print("print")
print("ends")
print("with")
print("newline")
print("\nunless", end=" ") # ends with a space instead of newline
print("differently specified")
print("\nprint","can","accept","multiple","arguments") # sep with a separator instead of a space
print("\nprint","can","accept","multiple","arguments", sep="\_/")
type(print)
help(print)
False,None,True,and,as,assert,break,class,continue,def,del,elif,else,except,finally,for,
from,global,if,import,in,is,lambda,nonlocal,not,or,pass,raise,return,try,while,with,yield
# cascade assignments
x = y = z = 10
# augmented assignments
x/=2
y+=1
print("x =",x,type(x))
print("y =",y,type(x))
print("z =",z,type(x))
print("\"A\"", "is a", type("A"))
print("\'A\'", "is also a", type("A"))
print("in other words, \"A\" is 'A' - >","A" is 'A')
print("\"\"\"\nmulti\nline\nstring\n\"\"\"", "is also a", type("""\nmulti\nline\nstring\n"""))
print(1,"is a", type(1))
print(1.,"is a", type(1.))
print(1.+1j,"is a",type(1.+1j))
print("True","is a",type(True))
print(None,"is a",type(None))
print("\nyou should use \\ to print special characters \" or \'")
print("\nstrings defined by \" understands special character ' without \\ ")
print('strings defined by \' understands special character " without \\' )
# to comment this line
s = '''triple-quoted
strings
are
multiline
'''
'''
triple-quoted strings that are not docstrings (first thing in a class/function/module) are ignored
and can be used
as multiline comments
'''
print(s)
print("What means that python is DYNAMICALLY typed\n")
myVar = 1
print("myVar =",myVar,"\n -> myVar names a",type(myVar))
myVar = "myVar"
print("myVar =",myVar,"\n -> now it names a",type(myVar))
myVar = None
print("myVar =",myVar,"\n -> now it names a",type(myVar))
print("\n#reassign objects to print and type \nprint=10 \ntype=-1 \nprint,type")
print = 10
type = -1
print, type
print(1)
# force reset of all user defined names, included print and type
%reset -f
# now print and type are back to work
print("back to work", type("back to work"))
print("implicit conversion between numeric literals is allowed, if that makes sense")
print("(12.+1j)+3 -> ",(12.+1j)+3)
print("\nimplicit conversion from string to a numeric literal returns an error")
print("'12'+3 \n -> returns")
'12'+3
print("\n#Operations between numbers")
print("\n# summation with float -> float","\ntype(1+1.)")
print(type(1+1.))
print("\n# division with integers -> float","\ntype(3/2)")
print(type(3/2))
print("\n# operations between parentheses are executed earlier ","\n4/(2+2)")
print(4/(2+2))
print("\n# exponentiation (default right associative)","\n2**3",", 2**3**2",", 2**(3**2)",", (2**3)**2")
print(2**3, ",", 2**3**2, ",", 2**(3**2), ",", (2**3)**2)
print("\n# floor division ","\n9.5/2",", 9.5//2",", 9.1//2",", 9.9//2")
print(9.5/2, ",", 9.5//2, ",", 9.1//2, ",", 9.9//2)
print("\n# modulo ",", 9.5%2",", 9.1%2",", 9.9%2")
print(9.5%2, ",", 9.1%2, ",", 9.9%2)
x = True
y = False
print("\nx=True","y=False")
print ("\nx or y =",x or y,"\nx and y =",x and y,"\nnot x =", not x)
print("\n0>0 =",0>0, "\n0>=0 =",0>=0,"\n0<0 =",0>0, "\n0<=0 =",0>=0, "\n0==0 =",0==0, "\n0!=0 =",0!=0)
print("\ntype(5.) is float =",type(5.) is float, "\ntype(5.) is not float =",type(5.) is not float)
print("\nconverting numeric literals to other numeric literals")
print("int(-4.) ->",int(-4.),type(int(-4.)))
print("float(-4) ->",float(-4),type(float(-4)))
print("complex(-4) ->",complex(-4),type(complex(-4)))
print("complex type cannot be converted neither to int...")
int(4.+0j)
print("...nor to float")
float(4.+0j)
print("\nconverting strings to numbers")
print("int(\"3\") ->",int("3"),type(int("3")))
print("float(\"3.\") ->",float("3."),type(float("3.")))
print("complex(\"3.\") ->",complex("3."),type(complex("3.")))
print("float(\"3\") ->",float("3"),type(float("3")))
print("float(\"3.\") ->",float("3."),type(float("3.")))
print("complex(\"3\") ->",complex("3"),type(complex("3")))
print("complex(\"3.\") ->",complex("3."),type(complex("3.")))
print("complex(\"3+0j\") ->",complex("3+0j"),type(complex("3+0j")))
print("\nconverting numbers to strings")
print("str(3) ->",str(3),type(str(3)))
print("str(3.) ->",str(3.),type(str(3.)))
print("str(3+3j) ->",str(3+3j),type(str(3+3j)))
print("\nwhen the conversion numeric to string fails: \n")
try:
print("int(\"3.\") ->",int("3."),type(int("3.")))
except ValueError:
print("int(\"3.\")\n ->","error: invalid literal for int() with base 10: '3.'")
print()
try:
print("float(\"3+0j\") ->",float("3+0j"),type(float("3+0j")))
except ValueError:
print("float(\"3+0j\")\n ->","error: could not convert string to float: '3+0j'")
print("print integers in binary format")
print("bin(40) ->",bin(40),type(bin(40)))
print("bin(40%2**4) ->",bin(40%2**4))
print("bin(40%2**6) ->",bin(40%2**6))
print("bin(40%2**32) ->",bin(40%2**32))
print("\nbinary format make sense only for integers\n")
try:
print("bin(0.) ->",bin(0.))
except:
print("bin(0.)\n ->","'float' object cannot be interpreted as an integer")
print()
try:
print("bin(0j) ->",bin(0j))
except:
print("bin(0j)\n ->","'complex' object cannot be interpreted as an integer")
print("\nabs(-4.5) ->",abs(-4.5),type(abs(-4.5)))
print("complex(-4+1j).conjugate() ->",complex(-4+1j).conjugate())
print("divmod(10,3) -> ",divmod(10,3))
print(" -> (10//3,10%3) -> ",(10//3,10%3))
print("pow(10,3) -> ",pow(10,3))
print(" -> (10**3) -> ",(10**3))
print('round(1234.1234,4) ->',round(1234.1234,4))
print('round(1234.1234,3) ->',round(1234.1234,3))
print('round(1234.1234,2) ->',round(1234.1234,2))
print('round(1234.1234,1) ->',round(1234.1234,1))
print('round(1234.1234,0) ->',round(1234.1234,0))
print('round(1234.1234,-1) ->',round(1234.1234,-1))
print('round(1234.1234,-2) ->',round(1234.1234,-2))
print('round(1234.1234,-3) ->',round(1234.1234,-3))
print('round(1234.1234,-4) ->',round(1234.1234,-4))
name = input("name: ")
age = input("age: ")
print(name,"is",age,"years old")
thisVal = "\nthis expression just prints a string\n"
expression = input("expression (e.g., print(thisVal)): ")
eval(expression)
import <module-name> [as <alias>]
from <module-name> import <submodule>
from <module-name> import *
import math
print("\nimport math","# include math"\
"\n%reset -f", "# cleans the environment")
%reset -f
print("\nprint(dir())", "# math should not be listed")
print(dir())
print("\nimport math", "")
import math
print("\nprint(dir())", "# now math is listed")
print(dir())
print("\nprint(dir(math))")
print(dir(math))
#dir(math)
print("math.__name__ ->",math.__name__)
print("math.sin(math.pi/2) ->",math.sin(math.pi/2),"\n")
import math as m
print("import math as m")
print("m.__name__ ->",m.__name__)
print("m.sin(m.pi/2) ->",m.sin(m.pi/2),"\n")
from math import sin
print("from math import sin")
print("sin(3.14/2) ->",sin(3.14/2),"\n")
from math import * # now I know pi!
print("from math import * # now I know pi!")
print("pi is",pi)
t = (1,1.,"str",("another","tuple"))
v1,v2,v3,v4 = t
print("\nsimultaneous assignment")
print("t = (1,1.,\"str\",(\"another\",\"tuple\"))",type(t))
print("\nsimultaneous assignment")
print("v1,v2,v3,v4 = t")
print("-->\nv1 =",v1,type(v1),"\nv2 =",v2,type(v2),"\nv3 =",v3,type(v3),"\nv4 =",v4,type(v4))
print("tuples are immutable")
t[0] = 2
from collections import namedtuple
contact = namedtuple("Contact", "Name Surname Email Phone")
myContact = contact("J","R","jr-mail","jr-phone")
name,surname,email,phone=myContact
print(myContact,"is a",type(myContact))
print(name,surname,email,phone)
print("# assign nametuple needs all fields")
anotherContact = contact('Name')
squares = [1, 4, 9, 16, 24] # 24 should be 25
print("squares ->", squares)
print("length of square is", squares.__len__())
print("\n# indexing returns the item")
print("squares[0] ->",squares[0],type(squares[0]))
print("squares[-1] -> ",squares[-1],type(squares[-1]))
print("\n# slicing returns a list of items")
print("squares[2:] ->",squares[2:],type(squares[2:])) # list of idx 2,3,...,len(list)
print("squares[:3] ->",squares[:3],type(squares[:3])) # list of idx 0,1,2
print("squares[0:1] ->",squares[0:1],type(squares[0:1]))
print("squares[-3:] -> ",squares[-3:],type(squares[-3:])) # list of last three elements
squares = [1, 4, 9, 16, 24] # 24 should be 25
print("\n# new values can be assigned both using indexing and slicing")
print("squares ->",squares)
squares[4] = 25
print("squares[4] = 25 ")
print("squares ->",squares)
print("squares[0:2] = [-1,-1]")
squares[0:2] = [-1,-1]
print("squares ->",squares)
print("\n# NB: assignment does not need consistency between lengths of slices")
print("squares[0:1] = ['what?',squares[0]]")
squares[0:1] = ['what?',squares[0]]
print("squares ->", squares)
print("squares[0:3] ->", squares[0:3])
print("squares[0:3] = [1,4]")
squares[0:3] = [1,4]
print("squares ->", squares)
squares = [1, 4, 9, 16, 25]
other_squares = [36,49,64]
print("\n# lists can be concatenated")
print("squares -> ",squares)
print("other_squares -> ",other_squares)
print("squares+other_squares -> ",squares+other_squares)
print("\n# new values can be appended to list")
print("squares -> ",squares)
print("squares.append(36)")
squares.append(36)
print("squares -> ",squares)
print("\n# values can be removed just by slicing")
print("squares[5:6] = []")
squares[-3:] = []
print("squares -> ",squares)
print("\n# clear a list replacing with empty list")
print("squares[:] = []")
squares[:] = []
print("squares -> ",squares)
print("\n# nested list")
print("l = [['a','b'],[1,2]]")
l = [['a','b'],[1,2]]
print("l[0][1] ->",l[0][1])
print("l[1][0] ->",l[1][0])
deep copy: the target contains the same values of the assigned object (i.e., they do not have the same reference)
python copies the reference
module copy contains the implementation for deep copy
%reset -f
import copy
l_1 = [1,2,'3']
l_2 = copy.copy(l_1) # shallow copy
l_3 = copy.deepcopy(l_1) # deep copy
l_2[0] = 'changes!'
l_3[0] = 'changes!'
print(l_1)
print(l_2, "<- shallow copy")
print(l_3, "<- deep copy")
%reset -f
import copy
a1 = [[1, 2, 3], [4, 5, 6]]
a2 = copy.copy(a1) # shallow copy
print("before assignments")
print("a1 =",a1)
print("a2 =",a2)
a2[0][0] = 'one'
print("after assignments")
print("a1 =",a1)
print("a2 =",a2)
print("Note: ")
print("aX[0][0] ==",\
a1[0][0],'==',\
a2[0][0])
print("aX[1][1] ==",\
a1[1][1],'==',\
a2[1][1])
%reset -f
import copy
a1 = [[1, 2, 3], [4, 5, 6]]
a2 = copy.deepcopy(a1) # deep copy
print("before assignments")
print("a1 =",a1)
print("a2 =",a2)
a2[0][0] = 'one'
a2[1][1] = 'five'
print("after assignments")
print("a1 =",a1)
print("a2 =",a2)
print("Note: ")
print("aX[0][0] ->",\
a1[0][0],'!=',\
a2[0][0])
print("aX[1][1] ->",\
a1[1][1],'!=',\
a2[1][1])
%reset -f
# append, extend
l = []
l.append(1)
l.append(2)
l2 = [0]
l2.extend(l)
print('l ->',l)
print('l2 ->',l2)
%reset -f
# insert, remove, pop, clear
l = [0,1,2]
l.insert(2,1.5)
l.insert(4,2.5)
l.insert(4,3.5)
print('l ->',l)
l.remove(2.5)
print('l.remove(2.5) ->\n\t l =',l)
el = l.pop()
print('el = l.pop() ->\n\tl =',l, '\n\tel =',el)
%reset -f
# copy,sort,count,reverse,index
l = [0,1,0,1,1,2,2,2,3,1,1,2,2,2,2]
lc = l # lc = l.copy() works only in python 3.X
lc[0] = -1
print('l -> ',l)
print('lc ->',lc)
l.sort()
print('\nl.sort()\nl ->',l)
print('\nl.count(2) is',l.count(2))
l.reverse()
print('\nl.reverse()\nl ->',l)
print('\nfirst zero at idx',l.index(0))
print('l[12:] ->',l[12:])
set_1 = set([1,2,1,2,3])
set_2 = set([4,5,6,1,7])
print("set_1 ->","{1,2,1,2,3} # 1 and 2 are repeated")
print("set_2 ->","{4,5,6,1,7} # 1 is in both ")
print("\n# use of 'in' keyword")
print("3 in set_1 ->",3 in set_1)
print("\n#set difference: elements in one but not in the other")
print("set_1-set_2 ->",set_1-set_2)
print("set_2-set_1 ->",set_2-set_1)
print("\n#set union and intersection")
print("set_1|set_2 ->","set_2|set_1 ->",set_1|set_2)
print("set_1&set_2 ->","set_2&set_1 ->",set_1&set_2)
print("\n#set symmetric difference == set difference between set union and set intersection")
print("set_1^set_2 ->","set_2^set_1 ->",set_1^set_2)
print("(set_1|set_2)-(set_1&set_2) ->",(set_1|set_2)-(set_1&set_2))
print("\n#super/subset test")
print("set_1>=set_2 ->",set_1>=set_2)
print("set_1<=set_2 ->",set_1<=set_2)
print("{1,2,3}<={1,2,3,4,5,6,7} ->",{1,2,3}<={1,2,3,4,5,6,7})
print("{1,2,3,4,5,6,7}>={1,2,3} ->",{1,2,3,4,5,6,7}>={1,2,3})
print("# sets cannot contain unhashable types")
set_1 = set([set(['a'])])
print("\n# frozen sets are hashable",\
"\n# -> can be elements of set",\
"\n# -> can be keys of dictionaries")
set_1 = set([frozenset('a'),frozenset('b')])
print(set_1)
%reset -f
# using {}
address_book={'Anoop':1,'Vasily':2}
print("\naddress_book ->",\
address_book,"\n ->",\
type(address_book))
print("'Jens' not in address_book' ->",\
'Jens' not in address_book)
# using dict(list_of_tuples)
mensa_dishes=dict([('first','pasta'),\
('second','salade'),\
('dessert','apple')])
print("\nmensa_dishes ->",\
mensa_dishes,"\n ->",\
type(address_book))
print("'first' in address_book' ->",\
'first' in mensa_dishes)
from collections import OrderedDict
data = {'c':3,'a':1,'b':2,'d':4}
ordered_data = OrderedDict(sorted(data.items()))
print('\n# dictionary vs ordered dictionary')
print('\ndata is', type(data),'\n -> ',data)
print('\nordered_data is', type(ordered_data),'\n ->',ordered_data)
%reset -f
address_book={'Anoop':1,'Vasily':2}
print("address_book =",address_book)
print("\n# delete key")
print("'Anoop' in address_book ->",'Anoop' in address_book)
print("del address_book['Anoop']")
del address_book['Anoop']
print("address_book ->",address_book)
print("'Anoop' in address_book ->",'Anoop' in address_book)
print("\n# add new key")
print("address_book['Jens'] = 3")
address_book['Jens'] = 3
print("address_book ->",address_book)
%reset -f
week = {'mon':1,'tue':2,'wed':3,'thu':4,'fri':5,'sat':6,'sun':7}
print('\nlist(week)\n->',list(week))
print('\nsorted(week)\n->', sorted(week))
print('\nsorted(week, key=week.get)\n->',sorted(week, key=week.get))
%reset -f
week = {'mon':1,'tue':2,'wed':3,'thu':4,'fri':5,'sat':6,'sun':7}
print("\n#update value for a key")
print("week.get('_8thday',8) ->",\
week.get('_8thday',8)) # week['_8thday'] does not exist
week.setdefault('_8thday',8)
print("\nweek.setdefault('_8thday',8)")
print("week\n ->",week)
print("\n#update value for a given key")
print("week['_8thday'] ->",week['_8thday'])
week.update({'_8thday':None})
print("week.update({'_8thday':None})\nweek['_8thday'] ->",week['_8thday'])
%reset -f
week = {'mon':1,'tue':2,'wed':3,'thu':4,'fri':5,'sat':6,'sun':7}
print("\nweek\n ->",week)
_9thday = week.pop('_9thday','8_days_already_too_much')
print("_9thday = week.pop('_9thday','8_days_already_too_much')"+\
"\n_9thday ->",_9thday)
print("\nweek\n ->",week)
_8thday = week.pop('_8thday','8_days_already_too_much')
print("_8thday = week.pop('_8thday','8_days_already_too_much')"+\
"\n_8thday ->",_8thday)
print("week\n ->",week)
print("\nweek\n ->",week)
a_day = week.popitem()
print("a_day = week.popitem() ->",a_day)
print("week\n ->",week)
if <condition>:
<body_1>
elif <another_condition>:
<body_2>
else:
<body_default>
# is there an item equal to value?
if <value> in <group_of_items>
# none items are equal to value?
if <value> not in <group_of_items>
if True: print("if can be inline")
if 5>4:
print("5 is bigger than 4")
if 4<5:
print("because 4 is smaller than 5")
else:
print("I would be confused otherwise")
if 0:
print("this should not be printed")
elif None:
print("this should not be printed")
elif (-1.+2j):
print("0 and None means False\n any other value is True ")
%reset -f
t = (3,4,5) # tuple
l = [3,4,5] # list
s = {3,4,5} # set
d = {3:1,4:2,5:3} # dictionary
v = 1
print("'in'/'not in' work on any iterable")
if v not in t:
print("if v not in t: -> True if none is equal to v")
if v not in l:
print("if v not in l: -> True if none is equal to v")
if v not in s:
print("if v not in s: -> True if none is equal to v")
if v not in d:
print("if v not in d: -> True if none is equal to v")
for <var> in <set_of_values>:
<body>
if <condition>:
continue
<something_that_runs_if_condition_not_True>
while <true_condition>:
<body>
if <another_condition>:
break
print("range(2) is",type(range(2)),"\n")
print("\nfor loop example")
for i in range(2):
print(i)
print("\nsame behavior using while loop")
i = 0
while i in range(2):
print(i)
i+=1
for i in range(5):
if i is 0:
continue # 0 is neither odd nor even
if i%2 is 0:
print(i,"is even")
continue # an even number is not odd
print(i,"is odd") # if i is even, this expression is not run
if True:
pass
print('no syntax error')
# use range with start,stop and step
for i in range(-5,7,2):
print(i,end=",")# -5,-3,-1,1,3,5,
print()
for i in range(-5,7,2):
if i is 3:
print(i,"== 3")
elif i == -1:
print(i,"== -1")
else:
pass # not decided yet
# pass is handy even if it does nothing
%reset -f
myList1 = ['1st_el','2nd_el','3rd_el']
myList2 = [111,222,333]
# myList1
for i in myList1:
print(i,end=", ")
print()
# reversed myList1
for i in reversed(myList1):
print(i,end=", ")
print('\n')
# zipped lists (with same size)
for i,j in zip(myList1,myList2):
print("[",i,",",j,"]", end=", ")
print()
# zipped lists (with different size)
myList2.pop()
for i,j in zip(myList1,myList2):
print("[",i,",",j,"]", end=", ")
%reset -f
myList1 = ['1st_el','2nd_el','3rd_el']
for i,j in enumerate(myList1):
print("[",i,",",j,"]", end=", ")
print('\n')
unorderedList = ['2nd_el','1st_el','3rd_el']
print(unorderedList)
for i in sorted(myList1):
print(i,end=", ")
with <expression> [as <variable>]:
<with-body-block>
with open('README.md', 'r') as f:
print(f.readline()) # read first line of the file README.md
try:
<try-body-block>
except <Exception-name> as <alias>:
<except-body-block>
print("# let's catch some common exceptions\n")
to_execute = ['1/0',\
'4+unknown_var',\
'"2"+2',\
"int('s')",
"print 1"]
for i in to_execute:
try:
print(i)
eval(i)
print("I am not going to be printed")
except (ZeroDivisionError,NameError,TypeError,ValueError) as err:
print(err.__class__,":",err)
except SyntaxError as err:
print("'print 1' was not caught because it causes SyntaxError")
print(err.__class__,":",err)
finally: # should be at the end of try statement
# useful to make sure all resources are released
# even if an exception occurs
# even if no exception was caught
print("\t---last but not least, finally is executed---")
x = -1
try:
assert x>=0, 'x is negative'
except AssertionError as err:
print(err)
try:
raise Exception(1,[2],{'3':3}) # an exception can be raised
# with any argument
except Exception as err:
print(type(err))
print(err.args)
print(err) # print its arguments
a,b,c = err.args
print(a,b,c)
try:
a,b,c = err
except TypeError as err:
print(err)
def <function_name>(<parameter_list>):
'''docstring'''
<body>
return <result>
print_returns = print()
print("print() returns ",print_returns)
def f0():
def g0():
return "g0 is scoped"
return g0()
try:
g0()
except NameError as err:
print(err)
print("\n",f0(),sep="")
def EUR_to_USD(eur):
'''
calculate conversion from EUR to USD
'''
usd_for_1eur = 1.17
return eur*usd_for_1eur
print("10 EUR means",EUR_to_USD(10),"USD")
print("\n---------------\n")
help(EUR_to_USD)
def EUR_to_many(eur):
'''
calculate conversion from EUR to different currencies
'''
usd_for_1eur = 1.17
yen_for_1eur = 0.0075
gbp_for_1eur = 1.13
return (eur*usd_for_1eur,\
eur*yen_for_1eur,\
eur*gbp_for_1eur)
currencies = EUR_to_many(10) # tuple
USD,YEN,GBP = currencies # unpack tuple
print("10 EUR means",USD,"USD\n\t",\
"or",YEN,"YEN\n\t",\
"or",GBP,"GBP")
print("EUR_to_many returns",type(currencies))
print("\n---------------\n")
help(EUR_to_many) # docstring
def foo(par1=None,par2=None):
res = None
if par1 is 1:
res = par1
elif par2 is 2:
res = par2
if par1 is 1 and par2 is 2:
res = "what a surprise!"
return res
# order of positional args is important
print("foo(par1=1) ->",foo(par1=1),\
"which equals foo(1) ->",foo(1))
print("foo(par2=2) ->",foo(par2=2),\
"which is not foo(2) ->",foo(2))
# order of keyword args is not important
print("foo(par1=1,par2=2) ->",\
foo(par1=1,par2=2))
print("foo(par2=2,par1=1) ->",\
foo(par2=2,par1=1))
def foo(*positional, **keywords):
print("Positional:", positional, end='\t')
print("Keywords:", keywords)
foo('1st', '2nd', '3rd')
foo(par1='1st', par2='2nd', par3='3rd')
foo('1st', par2='2nd', par3='3rd')
# #Uncomment the following lines in python 3.x
# foo(par1='1st_key',*'1st_pos', par2='2nd_key')
# foo(par1='1st_key',*['1st_pos'], par2='2nd_key',*['2st_pos','3rd_pos'])
def weighted_value(value):
return lambda weight,bias: value*weight+bias # Please note: returns a lambda function
f1 = weighted_value(1)
f10 = weighted_value(10)
print(f1(1.5,-1)) # weight=1.5, bias=-1: (1.5*1)-1 == 0.5
print(f1(weight=0,bias=-1)) # supports keyword arguments (0*1)-1 == -1
print(f10(0,-2)) # (0*10)-2 == -2
print(f10(1.5,100)) # (1.5*10)+100 == 115
pairs = [(1, 'one'), (2, 'two'), (3, 'three'), (4, 'four')]
pairs.sort(key=lambda pair: pair[0])
print(pairs)
pairs.sort(key=lambda pair: pair[1])
print(pairs)