Basic Types#
Zen of Python#
import this
The Zen of Python, by Tim Peters
Beautiful is better than ugly.
Explicit is better than implicit.
Simple is better than complex.
Complex is better than complicated.
Flat is better than nested.
Sparse is better than dense.
Readability counts.
Special cases aren't special enough to break the rules.
Although practicality beats purity.
Errors should never pass silently.
Unless explicitly silenced.
In the face of ambiguity, refuse the temptation to guess.
There should be one-- and preferably only one --obvious way to do it.
Although that way may not be obvious at first unless you're Dutch.
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!
Numeric types: int, float, complex#
# int, float, complex
1, 1.0, 1j
(1, 1.0, 1j)
1j ** 2
(-1+0j)
Variables#
clicks = 2
clicks, type(clicks)
(2, int)
# type(object) == object's class
1
2
3 # cell outputs the value of the last expression
3
# variable binding
temperature = 10
temperature = 20
temperature = 36.6 # dynamic typing
temperature, type(temperature)
(36.6, float)
shows = 50
s4ows
---------------------------------------------------------------------------
NameError Traceback (most recent call last)
Cell In[9], line 1
----> 1 s4ows
NameError: name 's4ows' is not defined
a = 2
b = 10
c = b / a
c
5.0
int, float, arithmetic operations#
5 + 6 * (7 - 1) / 3
17.0
2**10
1024
# int / int = float
clicks = 12
shows = 10
ctr = clicks / shows
type(ctr), ctr
(float, 1.2)
Remark. CTR = Clicks-through rate is a popular metric: a ratio showing how often people who see your ad or free product listing end up clicking it.
clicks // shows # integer division
1
clicks % shows # remainder
2
value = 1
value += 1
value *= 2
value /= 2
value **= 3
value
8.0
Bit operations#
1 << 10, 8 >> 3
(1024, 1)
bin(8)
'0b1000'
0b100 ^ 0b010, 0b011 | 0b010, 0b011 & 0b010
(6, 3, 2)
bin(1023)
'0b1111111111'
Logical type: bool#
True, False
(True, False)
type(True), type(False)
(bool, bool)
not True, not False
(False, True)
True and False, True or False, (2 < 3)
(False, True, True)
# Q: What's the result?
False == False != True
# How it works:
(False == False) and (False != True)
# A more obvious example:
2 < 3 < 5 < 7 < 11
(2 < 3) and (3 < 5) and (5 < 7) and (7 < 11)
String type: str#
greeting = "Hello"
goodbye = 'Bye-bye'
poem = """
В сто сорок солнц закат пылал,
в июль катилось лето,
была жара,
жара плыла -
на даче было это.
Пригорок Пушкино горбил
Акуловой горою,
а низ горы -
деревней был,
кривился крыш корою.
"""
type(greeting), type(goodbye), type(poem)
(str, str, str)
Functions#
def square(x):
return x*x
square(3.14)
9.8596
square(1j)
(-1+0j)
square('123')
---------------------------------------------------------------------------
TypeError Traceback (most recent call last)
Cell In[31], line 1
----> 1 square('123')
Cell In[28], line 2, in square(x)
1 def square(x):
----> 2 return x*x
TypeError: can't multiply sequence by non-int of type 'str'
square(int("123"))
15129
from math import factorial
factorial(5)
120
factorial(20)
2432902008176640000
factorial(100)
93326215443944152681699238856266700490715968264381621468592963895217599993229915608941463976156518286253697920827223758251185210916864000000000000000000000000
%%time
factorial(1000)
CPU times: user 61 µs, sys: 1 µs, total: 62 µs
Wall time: 68.9 µs
402387260077093773543702433923003985719374864210714632543799910429938512398629020592044208486969404800479988610197196058631666872994808558901323829669944590997424504087073759918823627727188732519779505950995276120874975462497043601418278094646496291056393887437886487337119181045825783647849977012476632889835955735432513185323958463075557409114262417474349347553428646576611667797396668820291207379143853719588249808126867838374559731746136085379534524221586593201928090878297308431392844403281231558611036976801357304216168747609675871348312025478589320767169132448426236131412508780208000261683151027341827977704784635868170164365024153691398281264810213092761244896359928705114964975419909342221566832572080821333186116811553615836546984046708975602900950537616475847728421889679646244945160765353408198901385442487984959953319101723355556602139450399736280750137837615307127761926849034352625200015888535147331611702103968175921510907788019393178114194545257223865541461062892187960223838971476088506276862967146674697562911234082439208160153780889893964518263243671616762179168909779911903754031274622289988005195444414282012187361745992642956581746628302955570299024324153181617210465832036786906117260158783520751516284225540265170483304226143974286933061690897968482590125458327168226458066526769958652682272807075781391858178889652208164348344825993266043367660176999612831860788386150279465955131156552036093988180612138558600301435694527224206344631797460594682573103790084024432438465657245014402821885252470935190620929023136493273497565513958720559654228749774011413346962715422845862377387538230483865688976461927383814900140767310446640259899490222221765904339901886018566526485061799702356193897017860040811889729918311021171229845901641921068884387121855646124960798722908519296819372388642614839657382291123125024186649353143970137428531926649875337218940694281434118520158014123344828015051399694290153483077644569099073152433278288269864602789864321139083506217095002597389863554277196742822248757586765752344220207573630569498825087968928162753848863396909959826280956121450994871701244516461260379029309120889086942028510640182154399457156805941872748998094254742173582401063677404595741785160829230135358081840096996372524230560855903700624271243416909004153690105933983835777939410970027753472000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000
# A very inefficient recursive example, for education purposes only.
def my_factorial(n):
if n == 0:
return 1
return n*my_factorial(n-1)
my_factorial(5)
120
my_factorial(10)
3628800
%%time
my_factorial(10000)
---------------------------------------------------------------------------
RecursionError Traceback (most recent call last)
File <timed eval>:1
Cell In[37], line 5, in my_factorial(n)
3 if n == 0:
4 return 1
----> 5 return n*my_factorial(n-1)
Cell In[37], line 5, in my_factorial(n)
3 if n == 0:
4 return 1
----> 5 return n*my_factorial(n-1)
[... skipping similar frames: my_factorial at line 5 (2967 times)]
Cell In[37], line 5, in my_factorial(n)
3 if n == 0:
4 return 1
----> 5 return n*my_factorial(n-1)
RecursionError: maximum recursion depth exceeded
Function print#
print(poem)
В сто сорок солнц закат пылал,
в июль катилось лето,
была жара,
жара плыла -
на даче было это.
Пригорок Пушкино горбил
Акуловой горою,
а низ горы -
деревней был,
кривился крыш корою.
print(65536)
print(10, 20, '123', True, False)
65536
10 20 123 True False
print('2', '+', 2)
print('2', '+', 2, sep=' ', end='!')
2 + 2
2 + 2!
# Q: What will happen?
print("Hello" + ", world!")
print('Two' * 5)
print("Two" + 2)
Hello, world!
TwoTwoTwoTwoTwo
---------------------------------------------------------------------------
TypeError Traceback (most recent call last)
Cell In[48], line 4
2 print("Hello" + ", world!")
3 print('Two' * 5)
----> 4 print("Two" + 2)
TypeError: can only concatenate str (not "int") to str
Slices and substrings#
string = "Hello, world!"
character = string[10] # NB: indexing starts from 0
sub_string = string[7:10]
type(string), type(character), type(sub_string)
(str, str, str)
character, sub_string
('l', 'wor')
'закат' in poem
True
string = "Hello, world!"
print(string[-1])
print(string[0])
!
H
print(string[:5])
print(string[7:])
print(string[4:8])
print(string[-5:-1])
Hello
world!
o, w
orld
string[:]
'Hello, world!'
print(string[::2]) # every second symbol
print(string[::-1]) # reversed string
Hlo ol!
!dlrow ,olleH
String literals#
(
'Hello'
', world'
'!'
)
'Hello, world!'
'123' + \
'456' + \
'789'
'123456789'
'123' \
'456' \
'789'
'123456789'
Dict#
birthdays = {'Alice': 123, 'Bob': '1999-12-31'}
birthdays['Sam'] = '1972-01-01'
birthdays['Sam']
'1972-01-01'
birthdays
{'Alice': 123, 'Bob': '1999-12-31', 'Sam': '1972-01-01'}
birthdays.get(123) is None
True
birthdays.get('Alice')
123
del birthdays['Bob']
birthdays
{'Alice': 123, 'Sam': '1972-01-01'}
'Alice' in birthdays
True
'Bob' in birthdays
False
empty_dict = dict()
len(empty_dict)
0
empty_dict
{}
len(birthdays)
2
List#
math_names = ['sin', 'cos', 'rot', 'div']
type(math_names)
list
len(math_names)
4
'cos' in math_names, 'sos' in math_names
(True, False)
math_names[0], math_names[1], math_names[-1]
('sin', 'cos', 'div')
math_names[1:3]
['cos', 'rot']
math_names[::-1]
['div', 'rot', 'cos', 'sin']
# Q: What will happen?
print([1, 2] + [3, 4])
print([2, 3] * 4)
print([2, 3] + 4)
[1, 2, 3, 4]
[2, 3, 2, 3, 2, 3, 2, 3]
---------------------------------------------------------------------------
TypeError Traceback (most recent call last)
Cell In[75], line 4
2 print([1, 2] + [3, 4])
3 print([2, 3] * 4)
----> 4 print([2, 3] + 4)
TypeError: can only concatenate list (not "int") to list
letters = ['alpha', 'beta', 'gamma']
letters.append('delta')
letters
['alpha', 'beta', 'gamma', 'delta']
books = ['Философский камень', 'Тайная комната']
books += ['Узник Азкабана']
books
['Философский камень', 'Тайная комната', 'Узник Азкабана']
letters.pop(), letters # pop deletes the last element of the list and returns it
('delta', ['alpha', 'beta', 'gamma'])
letters.pop(1), letters # delete element at position 1
('beta', ['alpha', 'gamma'])
yyyyy = [555, "World", [1,2,3], True, 10.5]
inception = ['dream']
inception.append(inception)
inception
['dream', [...]]
inception[1][1][1][1][1][1][1][1][1][1][1][1]
['dream', [...]]
type(inception)
list
Tuple#
(1, 2, 3)
(1, 2, 3)
(True, 100)
(True, 100)
(complex, 21, False)
(complex, 21, False)
21 in (complex, 21, False)
True
print((1, 2) + (3, 4))
print((True, False) * 10)
(1, 2, 3, 4)
(True, False, True, False, True, False, True, False, True, False, True, False, True, False, True, False, True, False, True, False)
print((20, 30) + 40)
---------------------------------------------------------------------------
TypeError Traceback (most recent call last)
Cell In[90], line 1
----> 1 print((20, 30) + 40)
TypeError: can only concatenate tuple (not "int") to tuple
ls = [1,2, 3]
ls
[1, 2, 3]
ls[1] = 10
ls
[1, 10, 3]
t = (4, 5, 6)
t
(4, 5, 6)
t[1] = 20
---------------------------------------------------------------------------
TypeError Traceback (most recent call last)
Cell In[94], line 1
----> 1 t[1] = 20
TypeError: 'tuple' object does not support item assignment
Set#
{'foo', 'bar', 'foobar'}
{'bar', 'foo', 'foobar'}
{'foo', 'bar', 'foobar', 'foo', 'foo'}
{'bar', 'foo', 'foobar'}
s = {1, 2, 3}
s[1]
---------------------------------------------------------------------------
TypeError Traceback (most recent call last)
Cell In[97], line 2
1 s = {1, 2, 3}
----> 2 s[1]
TypeError: 'set' object is not subscriptable
set("A man, a plan, a canal, - Panama")
{' ', ',', '-', 'A', 'P', 'a', 'c', 'l', 'm', 'n', 'p'}
nile ="""
1120 1160 963 1210 1160 1160 813 1230 1370 1140 995 935 1110
994 1020 960 1180 799 958 1140 1100 1210 1150 1250 1260 1220
1030 1100 774 840 874 694 940 833 701 916 692 1020 1050 969
831 726 456 824 702 1120 1100 832 764 821 768 845 864 862 698
845 744 796 1040 759 781 865 845 944 984 897 822 1010 771 676
649 846 812 742 801 1040 860 874 848 890 744 749 838 1050 918
986 797 923 975 815 1020 906 901 1170 912 746 919 718 714 740
"""
nile_list = nile.split()
print(nile_list[:10])
print("Size of list:", len(nile_list))
['1120', '1160', '963', '1210', '1160', '1160', '813', '1230', '1370', '1140']
Size of list: 100
#Q. How to get all unique values from the list?
s = set(nile_list)
print(type(s))
l = list(s)
print(type(l))
len(list(set(nile_list)))
<class 'set'>
<class 'list'>
85
print(s)
{'860', '846', '1370', '1210', '781', '1170', '801', '963', '742', '821', '897', '1100', '813', '822', '984', '698', '1050', '718', '759', '848', '824', '901', '940', '1260', '1020', '1250', '1220', '995', '1030', '916', '994', '694', '768', '1230', '771', '906', '1040', '1150', '1120', '831', '456', '740', '714', '749', '865', '701', '774', '890', '726', '815', '862', '1140', '692', '874', '944', '912', '840', '918', '1180', '864', '975', '764', '812', '833', '676', '796', '746', '1010', '1160', '1110', '838', '960', '923', '649', '744', '845', '958', '969', '799', '919', '702', '797', '935', '832', '986'}
print(list(s)[:10])
['860', '846', '1370', '1210', '781', '1170', '801', '963', '742', '821']
Packing, unpacking#
int, 21, False
(int, 21, False)
type(_) # the variable _ stores the result of the last expression evaluation
tuple
numbers = (4, 8, 15, 16, 23, 42)
a, b, c, d, e, f = numbers
a
4
e
23
colors = ('red', 'green', 'blue')
red_variable, *other_colors = colors
red_variable
'red'
other_colors
['green', 'blue']
letters = 'abcdefgh'
*other, prev, last = letters
print(other, prev, last)
['a', 'b', 'c', 'd', 'e', 'f'] g h
# pack, transfer, unpack
type_of_object = float
text = '-273.15'
pack = (type_of_object, text)
# ...
call, value = pack
call(value)
# swap a and b
a = 10
b = 20
print(a, b)
a, b = b, a
print(a, b)
Implicit type conversion#
True + 1
2
1 + 1.0
2.0
True + 1.0
2.0
True == 1, \
1.0 == 1, \
"1" == 1
(True, True, False)
Explicit type conversion#
int(4.9), int("345"), float(-7), float("1.4")
(4, 345, -7.0, 1.4)
float("-inf"), float("inf"), float("nan")
(-inf, inf, nan)
list("abc")
['a', 'b', 'c']
str([1,2,3])
'[1, 2, 3]'
tuple(["Obi", "Wan", "Kenobi"])
('Obi', 'Wan', 'Kenobi')
bool(-2), bool(-1), bool(0), bool(1), bool(2)
(True, True, False, True, True)
bool(-100.0), bool(0.0), bool(1000.0)
bool(-1j), bool(0j+0), bool(3j)
bool(""), bool("True"), bool("False"), bool("[]")
bool([]), bool([10, 20, 30]), bool([True]), bool([False])
bool(()), bool((False)), bool((False,))
Control flow#
Condition operator, if, elif, else#
x = 35
if x % 10 == 0: # semicolon emphasizes the beginning of еру new code block
# indentation is important!
print(x, "Divisible by 10")
elif x % 5 == 0:
print(x, "Divisible by 5")
else:
print(x, "Not divisible by 5")
35 Divisible by 5
value = 4
if value > 5:
print('> 5')
elif value == 5:
print('5')
elif value < 5:
print('< 5')
else:
print('HOW?!') # Q: for which value "HOW?!" will be printed?
< 5
if (x % 10 == 0): # unnecessary brackets impede reading of this code
print(x, "Divisible by 10")
elif (x % 5 == 0):
print(x, "Divisible by 5")
else:
print(x, "Not divisible by 5")
35 Divisible by 5
Ternary operator#
if x % 10 == 0:
answer = "Divisible by 10"
else:
answer = "Not divisible by 10"
answer = "Divisible by 10" if x % 10 == 0 else "Not divisible by 10"
x = 13
answer = x % 10 if x > 0 else "Wrong value!"
Execsice -1. Write a function, which returns the middle of three given integers.
def get_middle_value(a: int, b: int, c: int) -> int:
"""
Takes three values and returns middle value.
"""
pass
assert get_middle_value(1, 2, 3) == 2
assert get_middle_value(1, 3, 2) == 2
assert get_middle_value(2, 1, 3) == 2
assert get_middle_value(2, 3, 1) == 2
assert get_middle_value(3, 1, 2) == 2
assert get_middle_value(3, 2, 1) == 2
assert get_middle_value(-100, -10, 100) == -10
assert get_middle_value(100, -100, -10) == -10
assert get_middle_value(-10, -10, -5) == -10
assert get_middle_value(-10, -10, -10) == -10
assert get_middle_value(-100, 10, 100) == 10
assert get_middle_value(0, 0, 0) == 0
assert get_middle_value(10**12, -10**12, 10**10) == 10**10
or, and#
bool(20), bool(0)
(True, False)
bool(20 or 0), bool(20 and 0)
(True, False)
type(20 or 0), type(20 and 0)
(int, int)
x = ""
if x:
pass # pass just does nothing
else:
x = "default"
x
'default'
x = ""
x = x or "default" # often pattern to substitute empty value
x
Lazy evaluation#
#Q: what will happen?
print(0 and NEVER_EXISTED_VARIABLE)
print([10] or NEVER_EXISTED_VARIABLE)
0
[10]
print(10 and NEVER_EXISTED_VARIABLE)
---------------------------------------------------------------------------
NameError Traceback (most recent call last)
Cell In[152], line 1
----> 1 print(10 and NEVER_EXISTED_VARIABLE)
NameError: name 'NEVER_EXISTED_VARIABLE' is not defined
10 or print("lazy")
10
print("start") or print("end")
start
end
print("start")
None#
# None means the value is missing
None
type(_) # sic! None is not stored to _ (because "value is missing")
# so _ contains one of previous values
int
type(None)
NoneType
bool(None), str(None)
(False, 'None')
None usage#
# Turning video on...
is_working = False # something is broken
if is_working:
channel = 'Megasuper Channel'
else:
channel = None
value = print("I am print!") # print just prints objects, and returns None
print("value", "=", value)
I am print!
value = None
# print size of value, if value is not None
value = None
# value = "Football"
# value = ""
# Newbie
if value != None: # works for basic types, but
# nonstandard objects could equal None,
# not beeing None
print(len(value))
# Newbie 2
if value: # empty list coerced to False
print(len(value))
# Expert
if value is not None: # is checks if value is the same object
print(len(value))
Loops: for#
# print numbers from 1 to 10
print(1)
print(2)
print(3)
print(4)
print(5)
print(6)
print(7)
print(8)
print(9)
print(10)
1
2
3
4
5
6
7
8
9
10
for n in [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]:
print(n)
1
2
3
4
5
6
7
8
9
10
for letter in "ABCDE":
print("Letter", letter)
Letter A
Letter B
Letter C
Letter D
Letter E
for obj in (int, 10.1, True, None):
print(obj)
<class 'int'>
10.1
True
None
for k in {"a": 1, "b": 2}:
print(k)
a
b
d = {"a": 1, "b": 2}
for k, v in d.items():
print(k ,v)
a 1
b 2
# print numbers 1 to 1000000
for n in [1, 2, 3, ]: # too long to type...
print(n)
range#
numbers = range(10)
type(numbers)
range
numbers[0], numbers[-1]
(0, 9)
reversed_numbers = numbers[::-1]
reversed_numbers
list(range(10))
list(range(4, 14)) # half-open interval [4, 14)
list(range(4, 14, 3))
list(range(14, 4, -2))
range(10**100) # does not fit in memory, but range can handle it
range('a', 'f', 2) # Q: will it work?
Loops: for#
# Print numbers from 0 to 1000000
for n in range(1000000):
# print(n) # закомментировано, чтоб случайно не напечатать и не подвесить jupyter ;]
pass
# print alphabet in direct and reversed orders
alphabet = 'abcdefghijklmnopqrstuvwxyz'
for letter in alphabet:
print(letter, end=' ')
print()
for letter in alphabet[::-1]:
print(letter, end=' ')
a b c d e f g h i j k l m n o p q r s t u v w x y z
z y x w v u t s r q p o n m l k j i h g f e d c b a
reversed(alphabet)
<reversed at 0x10e1cda20>
for letter in reversed(alphabet):
print(letter, end=' ')
z y x w v u t s r q p o n m l k j i h g f e d c b a
for value in range(9, -1, -1):
print(value, end=' ')
print()
for value in reversed(range(10)):
print(value, end=' ')
9 8 7 6 5 4 3 2 1 0
9 8 7 6 5 4 3 2 1 0
Exersice 0. Write five versions of reversing list program.
def reverse_iterative(lst: list[int]) -> list[int]:
"""
Return reversed list. You can use only iteration
:param lst: input list
:return: reversed list
"""
# lst = [1, 5, 7, 8, -54] -> [-54, 8, 7, 5, 1]
result = list()
for i in range(len(lst)):
result.append(lst[len(lst) - i - 1])
return result
def reverse_inplace_iterative(lst: list[int]) -> None:
"""
Revert list inplace. You can use only iteration
:param lst: input list
:return: None
"""
# YOUR CODE HERE
def reverse_inplace(lst: list[int]) -> None:
"""
Revert list inplace with reverse method
:param lst: input list
:return: None
"""
# YOUR CODE HERE
def reverse_reversed(lst: list[int]) -> list[int]:
"""
Revert list with `reversed`
:param lst: input list
:return: reversed list
"""
# YOUR CODE HERE
def reverse_slice(lst: list[int]) -> list[int]:
"""
Revert list with slicing
:param lst: input list
:return: reversed list
"""
# YOUR CODE HERE
reverse_iterative([1, 5, 7, 8, -54])
reverse_slice([1, 5, 7, 8, -54])
Excercise 1. The legendary FizzBuzz (if you think that it is too easy, look here).
def FizzBuzz(n):
"""
Prints numbers from 1 to n.
But for the multiples of three print 'Fizz' instead of the number
and for the multiples of five print 'Buzz'.
For numbers which are multiples of both three and five print 'FizzBuzz'.
"""
# YOUR CODE HERE
FizzBuzz(5)
FizzBuzz(15)
Loops: while#
data = [.0, .0, .0, 1.2]
while len(data) > data[-1]:
last = data[-1]
data.append(last * last)
print(data)
[0.0, 0.0, 0.0, 1.2, 1.44]
[0.0, 0.0, 0.0, 1.2, 1.44, 2.0736]
[0.0, 0.0, 0.0, 1.2, 1.44, 2.0736, 4.299816959999999]
[0.0, 0.0, 0.0, 1.2, 1.44, 2.0736, 4.299816959999999, 18.488425889503635]
def gcd(a, b):
# Euclid's algorithm
while a > 0:
if a > b:
a, b = b, a
a, b = b % a, a
return b
gcd(10010, 1100)
110
data = [10, 20, 30, 40]
print("Print every element in the list")
for value in data:
print(value, end=', ')
print()
print("For every element in the list print: (index, element)")
for i, value in enumerate(data):
# enumerate maps data objects to the tuple (index, object)
print(i, value, end=', ')
print()
print("Print index every element in the list")
for i in range(len(data)):
print(i, end=', ')
data = [0,1,2,3,4,5,6,7,8,9,10]
for value in data:
print(value, end=', ')
data.pop(0) # delete the first element
data = [0,1,2,3,4,5,6,7,8,9,10]
while data:
value = data.pop(0)
print(value, end=', ')
Excercise 2. Collatz conjecture states that for any \(n \in \mathbb N\) the iterative calculation of the function $\( f(n) = \begin{cases} \frac n2,& n \text{ is even,}\\ 3n+1, & n \text{ is odd.} \end{cases} \)\( finally reaches \)1$. For example:
\(f(2) = 1\) (one iteration);
\(f(3) = 10\), \(f(10) = 5\), \(f(5) = 16\), \(f(16) = 8\), \(f(8) = 4\), \(f(4) = 2\), \(f(2) = 1\) (seven iterations);
\(f(4) = 2\), \(f(2) = 1\) (two iterations).
Calculate the number of steps for n to reach 1.
def collatz_steps(n):
# YOUR CODE HERE
# run this cell to test your solution
ground_truth = {1: 0,
2: 1,
3: 7,
4: 2,
5: 5,
6: 8,
7: 16,
8: 3,
9: 19,
27: 111,
257: 122,
1000: 111,
100500: 66
}
for n, steps in ground_truth.items():
got_steps = collatz_steps(n)
assert got_steps == steps, f"Expected {steps}, got: {got_steps}"
Loops: break, continue, else#
contnue#
# continue stops the execution of the current iteration and goes to the next one
skip = 7
for n in range(10):
if n == skip:
print('*', end=', ')
continue
print(n, end=', ')
data = [1, 2, 3, 4, 5, 6, 7]
while data:
value = data.pop() # remove the last element
if 2 <= len(data) <= 4:
continue
print(value, end=', ')
break#
# break interrupts the loop execution
# break interrupts the loop execution
for natural in [1, 2, 3, 4, -1, 20]:
if natural < 0:
print("ERROR")
break
ERROR
from datetime import datetime
secret_number = 1 + datetime.now().microsecond % 100
while True:
n = int(input("Enter your guess:"))
if n == secret_number:
print("You win!")
break
if n == 0:
print("The secret number was", secret_number)
break
else#
# if the loop was not finished by break then else-block executes
for line in ['Hello', 'World', 'Library']:
if len(line) > 7:
print("Found long line!")
break
else:
print("All lines are short")
Built-ins#
dir(__builtins__)
['ArithmeticError',
'AssertionError',
'AttributeError',
'BaseException',
'BaseExceptionGroup',
'BlockingIOError',
'BrokenPipeError',
'BufferError',
'BytesWarning',
'ChildProcessError',
'ConnectionAbortedError',
'ConnectionError',
'ConnectionRefusedError',
'ConnectionResetError',
'DeprecationWarning',
'EOFError',
'Ellipsis',
'EncodingWarning',
'EnvironmentError',
'Exception',
'ExceptionGroup',
'False',
'FileExistsError',
'FileNotFoundError',
'FloatingPointError',
'FutureWarning',
'GeneratorExit',
'IOError',
'ImportError',
'ImportWarning',
'IndentationError',
'IndexError',
'InterruptedError',
'IsADirectoryError',
'KeyError',
'KeyboardInterrupt',
'LookupError',
'MemoryError',
'ModuleNotFoundError',
'NameError',
'None',
'NotADirectoryError',
'NotImplemented',
'NotImplementedError',
'OSError',
'OverflowError',
'PendingDeprecationWarning',
'PermissionError',
'ProcessLookupError',
'RecursionError',
'ReferenceError',
'ResourceWarning',
'RuntimeError',
'RuntimeWarning',
'StopAsyncIteration',
'StopIteration',
'SyntaxError',
'SyntaxWarning',
'SystemError',
'SystemExit',
'TabError',
'TimeoutError',
'True',
'TypeError',
'UnboundLocalError',
'UnicodeDecodeError',
'UnicodeEncodeError',
'UnicodeError',
'UnicodeTranslateError',
'UnicodeWarning',
'UserWarning',
'ValueError',
'Warning',
'ZeroDivisionError',
'__IPYTHON__',
'__build_class__',
'__debug__',
'__doc__',
'__import__',
'__loader__',
'__name__',
'__package__',
'__spec__',
'abs',
'aiter',
'all',
'anext',
'any',
'ascii',
'bin',
'bool',
'breakpoint',
'bytearray',
'bytes',
'callable',
'chr',
'classmethod',
'compile',
'complex',
'copyright',
'credits',
'delattr',
'dict',
'dir',
'display',
'divmod',
'enumerate',
'eval',
'exec',
'execfile',
'filter',
'float',
'format',
'frozenset',
'get_ipython',
'getattr',
'globals',
'hasattr',
'hash',
'help',
'hex',
'id',
'input',
'int',
'isinstance',
'issubclass',
'iter',
'len',
'license',
'list',
'locals',
'map',
'max',
'memoryview',
'min',
'next',
'object',
'oct',
'open',
'ord',
'pow',
'print',
'property',
'range',
'repr',
'reversed',
'round',
'runfile',
'set',
'setattr',
'slice',
'sorted',
'staticmethod',
'str',
'sum',
'super',
'tuple',
'type',
'vars',
'zip']
# Q: What will happen?
str = 'hello'
str(1)
---------------------------------------------------------------------------
TypeError Traceback (most recent call last)
Cell In[179], line 3
1 # Q: What will happen?
2 str = 'hello'
----> 3 str(1)
TypeError: 'str' object is not callable
str = __builtins__.str # Recovering
print, reversed, enumerate
# built-in functions
min, max, sum, ...
data = [1, 2, 3, 4, 5]
# Newbie
total = 0
for el in data:
total += el
# Expert
total = sum(data)
total
data = [1, 2, 3, 4, 5]
# Newbie
min_value = None
for el in data:
if min_value is None:
min_value = el
else:
if el < min_value:
min_value = el
# Expert
min_value = min(data)
# Q: What's the value of this expression?
max([])
# newbie
for value in [1, 2, 3, 4, 5]:
print(value, end=', ')
print()
# expert
for value in range(1, 6):
print(value, end=', ')
data = [1, 2, 3, 4, 5]
# newbie
for i in range(len(data)):
print(i, data[i], end=', ')
print()
# expert
for i, value in enumerate(data):
print(i, value, end=', ')
data = [1, 2, 3, 4, 5]
letters = ['a', 'b', 'c', 'd', 'e']
# newbie
for i in range(len(data)):
print(i, (data[i], letters[i]), end=', ')
print()
# expert
for i, value in enumerate(zip(data, letters)):
print(i, value, end=', ')
math_vector_first = [1.0, -3.5, 4.1]
math_vector_second = [7.0, -1.1, -1.4]
#math_vector_sum = ? Using numpy is a better choice!
math_vector_sum = []
for element_first, element_second in zip(math_vector_first, math_vector_second):
# теперь на каждой итерации мы видим пару элементов и можем их сложить
math_vector_sum.append(
element_first + element_second
)
math_vector_sum
Sorting#
lst = [-1, 5, 7.34, 0.15, 2.5, -3]
sorted(lst)
[-3, -1, 0.15, 2.5, 5, 7.34]
sorted(lst, reverse=True)
[7.34, 5, 2.5, 0.15, -1, -3]
# make list of ints from nile.txt file, using list comprehension
nile_lst = [int(s) for s in nile.split()]
print(sorted(nile_lst))
Exercise 3. The merging part of Merge Sort.
def merge_iterative(lst_a: list[int], lst_b: list[int]) -> list[int]:
"""
Merge two sorted lists in one sorted list
:param lst_a: first sorted list
:param lst_b: second sorted list
:return: merged sorted list
"""
# YOUR CODE HERE
def merge_sorted(lst_a: list[int], lst_b: list[int]) -> list[int]:
"""
Merge two sorted lists in one sorted list using `sorted`
:param lst_a: first sorted list
:param lst_b: second sorted list
:return: merged sorted list
"""
# YOUR CODE HERE
assert merge_sorted([],[]) == []
assert merge_iterative([],[]) == []
assert merge_sorted([1],[]) == [1]
assert merge_iterative([1],[]) == [1]
assert merge_sorted([],[1]) == [1]
assert merge_iterative([],[1]) == [1]
assert merge_sorted([1],[1]) == [1, 1]
assert merge_iterative([1],[1]) == [1, 1]
assert merge_sorted([2],[1]) == [1, 2]
assert merge_iterative([2],[1]) == [1, 2]
assert merge_sorted([1],[2]) == [1, 2]
assert merge_iterative([1],[2]) == [1, 2]
assert merge_sorted([1, 3],[2, 4]) == [1, 2, 3, 4]
assert merge_iterative([1, 3],[2, 4]) == [1, 2, 3, 4]
assert merge_sorted([1, 3, 3, 4],[2, 3, 4]) == [1, 2, 3, 3, 3, 4, 4]
assert merge_iterative([1, 3, 3, 4],[2, 3, 4]) == [1, 2, 3, 3, 3, 4, 4]
Exercise 4. Using binary search, determine wether a value is present in a sorted list.
def find_value(l: list[int], v: int):
"""
Returns True iff the value v is present in the increasingly sorted list l.
"""
# YOUR CODE HERE
# Run this and the next cell to test your solution
import copy
import dataclasses
@dataclasses.dataclass
class Case:
nums: list[int] | range
value: int
result: bool
name: str | None = None
def __str__(self) -> str:
if self.name is not None:
return self.name
return 'find_{}_in_{}'.format(self.value, self.nums)
BIG_VALUE = 10**15
TEST_CASES = [
Case(nums=[], value=2, result=False),
Case(nums=[1], value=2, result=False),
Case(nums=[1, 3, 5], value=0, result=False),
Case(nums=[1, 3, 5], value=2, result=False),
Case(nums=[1, 3, 5], value=4, result=False),
Case(nums=[1, 3, 5], value=6, result=False),
Case(nums=[1, 3, 5], value=1, result=True),
Case(nums=[1, 3, 5], value=3, result=True),
Case(nums=[1, 3, 5], value=5, result=True),
Case(nums=[3], value=3, result=True),
Case(nums=[1, 3], value=1, result=True),
Case(nums=[1, 3], value=3, result=True),
Case(nums=[1, 3, 5, 7], value=0, result=False),
Case(nums=[1, 3, 5, 7], value=2, result=False),
Case(nums=[1, 3, 5, 7], value=4, result=False),
Case(nums=[1, 3, 5, 7], value=6, result=False),
Case(nums=[1, 3, 5, 7], value=8, result=False),
Case(nums=[1, 3, 5, 7], value=1, result=True),
Case(nums=[1, 3, 5, 7], value=3, result=True),
Case(nums=[1, 3, 5, 7], value=5, result=True),
Case(nums=[1, 3, 5, 7], value=7, result=True),
Case(nums=[1, 3, 5, 7, 9], value=0, result=False),
Case(nums=[1, 3, 5, 7, 9], value=2, result=False),
Case(nums=[1, 3, 5, 7, 9], value=4, result=False),
Case(nums=[1, 3, 5, 7, 9], value=6, result=False),
Case(nums=[1, 3, 5, 7, 9], value=8, result=False),
Case(nums=[1, 3, 5, 7, 9], value=10, result=False),
Case(nums=[1, 3, 5, 7, 9], value=1, result=True),
Case(nums=[1, 3, 5, 7, 9], value=3, result=True),
Case(nums=[1, 3, 5, 7, 9], value=5, result=True),
Case(nums=[1, 3, 5, 7, 9], value=7, result=True),
Case(nums=[1, 3, 5, 7, 9], value=9, result=True),
Case(nums=[1, 5, 5, 5, 9], value=1, result=True),
Case(nums=[1, 5, 5, 5, 9], value=5, result=True),
Case(nums=[1, 5, 5, 5, 9], value=9, result=True),
Case(nums=[1, 5, 5, 5, 9], value=7, result=False),
Case(nums=range(0, BIG_VALUE, 2), value=BIG_VALUE - 2, result=True, name="max_in_big_range"),
Case(nums=range(0, BIG_VALUE, 2), value=0, result=True, name="min_in_big_range"),
Case(nums=range(0, BIG_VALUE, 2), value=BIG_VALUE, result=False, name="greater_than_max_in_big_range"),
Case(nums=range(0, BIG_VALUE, 2), value=-1, result=False, name="less_than_min_in_big_range"),
Case(nums=range(0, BIG_VALUE, 2), value=BIG_VALUE // 2, result=True, name="middle_in_big_range"),
Case(nums=range(0, BIG_VALUE, 2), value=BIG_VALUE // 2 + 1, result=False, name="middle_not_exists_in_big_range"),
]
def test_find_value(t: Case) -> None:
nums_copy = copy.deepcopy(t.nums)
answer = find_value(nums_copy, t.value)
assert answer == t.result, str(t)
assert t.nums == nums_copy, "You shouldn't change inputs"
%%time
for case in TEST_CASES:
test_find_value(case)
Functions#
print("Welcome to Moscow!")
print("Welcome to Samara!")
print("Welcome to Novosibirsk!")
print("Welcome to Sochi!")
print("Welcome to Khabarovsk!")
# can write as a loop
for city in ['Moscow', 'Samara', 'Novosibirsk', 'Sochi', 'Khabarovsk']:
print("Welcome to ", city, '!', sep='')
# and wrap into a function
def print_wellcome(location):
print("Welcome to ", city, '!', sep='')
for city in ['Moscow', 'Samara', 'Novosibirsk', 'Sochi', 'Khabarovsk']:
print_wellcome(city)
Typing#
import typing as tp
def f(
int_value: int,
float_value: float,
str_value: str,
list_value: list[str],
tuple_value: tuple[str, str],
optional_str_value: str | None,
str_or_int_value: str | int,
type_value: type,
any_value: tp.Any
) -> None:
pass
f(
int_value=10,
float_value=10.1,
str_value=str,
list_value=["a", "b"],
tuple_value=("a", "b"),
optional_str_value="hello", # or None
str_or_int_value="world", # or 1
type_value=int,
any_value=12345 # or any other type
)
# Newbie
def ctr(clicks, shows):
return clicks / shows
# Expert
def ctr(clicks: int, shows: int) -> float:
return clicks / shows
# Tried to figure out what are the types of the arguments of this function...
def _update_input_fields(input_fields, output_fields):
for output_field_name, output_field_type in dict(output_fields).iteritems():
if output_field_name not in input_fields:
input_fields[output_field_name] = output_field_type
elif init_to_string(input_fields[output_field_name]) != init_to_string(output_field_type):
input_fields[output_field_name] = _merge_field_type(input_fields[output_field_name], output_field_type)
# Done. Do not do like this
import typing as tp
def _update_input_fields(
input_fields: tp.Mapping[str, tp.Union[tp.Tuple[tp.Union[tp.Callable[tp.Any, tp.Any], str], tp.Union[tp.Callable[tp.Any, tp.Any], str]], tp.Union[tp.Callable[tp.Any, tp.Any], str]]],
output_fields: tp.Union[tp.Mapping[str, tp.Union[tp.Tuple[tp.Union[tp.Callable[tp.Any, tp.Any], str], tp.Union[tp.Callable[tp.Any, tp.Any], str]], tp.Union[tp.Callable[tp.Any, tp.Any], str]]], tp.Sequence[tp.Tuple[str, tp.Union[tp.Tuple[ tp.Union[tp.Callable[tp.Any, tp.Any], str], tp.Union[tp.Callable[tp.Any, tp.Any], str]], tp.Union[tp.Callable[tp.Any, tp.Any], str]]]]]
) -> None:
for output_field_name, output_field_type in dict(output_fields).iteritems():
if output_field_name not in input_fields:
input_fields[output_field_name] = output_field_type
elif init_to_string(input_fields[output_field_name]) != init_to_string(output_field_type):
input_fields[output_field_name] = _merge_field_type(input_fields[output_field_name], output_field_type)
# newbie
def ctr(clicks: int, shows: int) -> float | None:
if shows != 0:
return clicks / shows
# expert
def ctr(clicks: int, shows: int) -> float:
if shows != 0:
return clicks / shows
return 0.0
Documentation#
def print_message(message: tp.Optional[str]):
# Prints the passed message
# Handles the cases of empty and missing messages separately
if message is None:
print("No message!")
elif not message:
print("Empty message!")
else:
print(message)
help(print_message) # help – one more builtin
def print_message(message: tp.Optional[str]):
"""
Prints the passed message.
Handles the cases of empty and missing messages separately.
"""
if message is None:
print("No message!")
elif not message:
print("Empty message!")
else:
print(message)
print_message.__doc__
help(print_message)
print_message # in Jupyter use shift + tab for docs
# newbie
def ctr1(clicks: int, shows: int) -> float:
if shows != 0:
return clicks / shows
return 0.0
# expert
def ctr2(clicks: int, shows: int) -> float:
"""
Calculate ctr. If there are no shows, return 0.
:param clicks: number of clicks on banner
:param shows: number of banners shows
:return: clicks-through rate
"""
if shows != 0:
return clicks / shows
return 0.0
assert#
assert True # assert
assert False # this always fails
assert 2 + 2 == 4 # assert expresses the confidence that the expression is always true
# during the normal program execution. Mainly used for debugging
allways_filled_list = []
assert allways_filled_list, "List must not be empty!"
import typing as tp
def _dot_product(first: tp.Sequence[float], second: tp.Sequence[float]) -> float:
result = 0
for i in range(len(first)):
result += first[i] * second[i]
return result
def length(vector: tp.List[float]) -> float:
return _dot_product(vector, vector) ** 0.5
length([1, 2, 3, 4, 5])
import typing as tp
def _dot_product(first: tp.List[float], second: tp.List[float]) -> float:
assert len(first) == len(second), "First and second should be equal length"
result = 0
for el_first, el_second in zip(first, second):
result += el_first * el_second
return result
def length(vector: tp.List[float]) -> float:
return _dot_product(vector, vector) ** 0.5
length([1, 2, 3, 4, 5])
assert in tests#
def dummy_function(a) -> str:
return "hello"
def test_dummy_function(value) -> None:
assert dummy_function(value) == value, "Function doesn't return the same value as passed"
test_dummy_function("hello")
test_dummy_function("hi")
Comments#
1. is abscent because the clear code made it unnecessary
2. answers the question "why?"