Learn-Python-Programming-Masterclass

section 1

buildin function

https://docs.python.org/3/library/functions.html#built-in-functions

print()

print(‘Hello, World!’)

def print(*values: object,
sep: str | None = “ “,
end: str | None = “\n”,
file: SupportsWrite[str] | None = None,
flush: Literal[False] = False) -> None
Prints the values to a stream, or to sys.stdout by default.

sep
string inserted between values, default a space.
end
string appended after the last value, default a newline.
file
a file-like object (stream); defaults to the current sys.stdout.
flush
whether to forcibly flush the stream.
print(*values, sep=" ", end="\n", file=None, flush=False)

1
2
3
4
5
6
7
print('Hello, World!')
print("Hello, World!")
print(1 + 2)
print(7 * 6)
print()
# The end or is it? keep watching to learn more about python 3
print("The end", "or is it?", "keep watching to learn more about python", 3)

literal: a value of some type

input()

def input(__prompt: object = “”) -> str
Read a string from standard input. The trailing newline is stripped.
The prompt string, if given, is printed to standard output without a trailing newline before reading input.
If the user hits EOF (nix: Ctrl-D, Windows: Ctrl-Z+Return), raise EOFError. Onnix systems, readline is used if available.
input(__prompt="")

type()

type(object) -> the object’s type

sum()

Return the sum of a ‘start’ value (default: 0) plus an iterable of numbers

id()

def id(__obj: object) -> int
Return the identity of an object.
This is guaranteed to be unique among simultaneously existing objects. (CPython uses the object’s memory address.)

immutable type
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
# result = True
# another_result = result
#
# print(id(result)) # 140714778504272
# print(id(another_result)) # 140714778504272
#
# result = False
# print(id(result)) # 140714778504304

result = "Correct"
another_result = result

print(id(result)) # 2290629759584
print(id(another_result)) # 2290629759584

result += "ish"
print(id(result)) # 2290629932336

string

1
2
3
4
5
6
7
8
9
10
11
12
print("Today is a good day to learn Python")
print("Python's string are easy to use")
print('We can even include "quotes" in strings')
print("hello" + " world") # 'hello world'

greeting = "Hello"
# name = "Bruce"
name = input("Please enter your name ")

print(greeting + name) # HelloBruce
# if we want space, we can add that too
print(greeting + ' ' + name) # Hello Bruce
mutable type
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
shopping_list = [
'milk',
'pasta',
'eggs',
'spam',
'bread',
'rice'
]

another_list = shopping_list
print(id(shopping_list)) # 1978024513920
print(id(another_list)) # 1978024513920

shopping_list += ['cookies']
print(shopping_list)
print(id(shopping_list)) # 1978024513920
print(id(another_list)) # 1978024513920

escape charater

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
# 'This string has been '
# 'split over '
# 'several '
# 'lines'
splitString = "This string has been \nsplit over \nseveral \nlines"
print(splitString)

# 1 2 3 4 5
tabbedString = "1\t2\t3\t4\t5"
print(tabbedString)

# The pet shop owner said "No, no, 'e's uh, ...he's resting".
print('The pet shop owner said "No, no, \'e\'s uh, ...he\'s resting".')
print("The pet shop owner said \"No, no, 'e's uh, ...he's resting\".")

print("""The pet shop owner said "No, no, 'e's uh, ...he's resting".""")

# This string has been
# split over
# several
# lines
anotherSplitString = """This string has been
split over
several
lines"""
print(anotherSplitString)

anotherSplitString_1 ="""
This string has been
split over
several
lines
"""
# ''
# This string has been
# split over
# several
# lines
# ''
print(anotherSplitString_1)

# This string has not been split over several lines
notSplitString = """This string has not been \
split over \
several \
lines"""
print(notSplitString)

# perfered!!!
print("C:\\Users\\timbuchalka\\notes.txt")
# raw string
print(r"C:\Users\timbuchalka\notes.txt")

variable names

  • must begin with a letter(either upper or lower case) or an underscore_ character
  • can contain letters, numbers or underscore characters(but cannot begin with a number)
  • are case sensitive, so greeting and Greeting would refer to 2 different variables

initializing variable

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
shopping_list = ['milk', 'pasta', 'eggs', 'spam', 'bread', 'rice']

item_to_find = 'spam'
found_at = None

# find the first item in a list
for index in range(len(shopping_list)):
if shopping_list[index] == item_to_find:
found_at = index
break

if found_at is not None:
print("Item found at index {}".format(found_at))
else:
print("{} not found".format(item_to_find))

dynamic type and strongly typed

1
2
3
4
5
6
7
8
9
greeting = "Hello"
age = 24
print(age)

print(type(greeting)) # <class 'str'>
print(type(age)) # <class 'int'>

age = "2 years"
print(type(age)) # <class 'str'>
1
2
3
4
greeting = "Hello"
age = 24
# TypeError: can only concatenate str (not "int") to str
# print(name + " is " + age + " years old")

constants

Constants are usually defined on a module level and written in all capital letters with underscores separating words. Examples include MAX_OVERFLOW and TOTAL.

numeric data type

int: python has no maximun size of the number
float: 64bit, max1.79e+308, min 2.22e-308, 52 digits of percison
Deicmal: more precise decimal numbers
complex: contain a real and imaginary part, base on the square root of minus one

1
2
3
4
5
6
7
8
9
a = 12
b = 3

print(a + b) # 15
print(a - b) # 9
print(a * b) # 36
print(a / b) # 4.0 float
print(a // b) # 4 integer division, rounded down towards minus infinity
print(a % b) # 0 modulo: the remainder after integer division

operator precedence

https://docs.python.org/3/reference/expressions.html#operator-precedence

Operator

Description

(expressions…),

[expressions…], {key: value…}, {expressions…}

Binding or parenthesized expression, list display, dictionary display, set display

x[index], x[index:index], x(arguments…), x.attribute

Subscription, slicing, call, attribute reference

await x

Await expression

**

Exponentiation [5]

+x, -x, ~x

Positive, negative, bitwise NOT

*, @, /, //, %

Multiplication, matrix multiplication, division, floor division, remainder [6]

+, -

Addition and subtraction

<<, >>

Shifts

&

Bitwise AND

^

Bitwise XOR

|

Bitwise OR

in, not in, is, is not, <, <=, >, >=, !=, ==

Comparisons, including membership tests and identity tests

not x

Boolean NOT

and

Boolean AND

or

Boolean OR

if – else

Conditional expression

lambda

Lambda expression

:=

Assignment expression

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
a = 12
b = 3
# -35.0
print(a + b / 3 - 4 * 12) # -35.0
print(a + (b / 3) - (4 * 12)) # -35.0

c = a + b
d = c / 3
e = d - 4
print(e * 12) # 12.0
print(((a + b) / 3 - 4) * 12) # 12.0

print()

print(a / (b * a) / b) # 0.11111111
print((a / (b * a)) / b) # 0.11111111

sequence type: string

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
#         01234567890123
parrot = "Norwegian Blue"
# -43210987654321

print(parrot)

print(parrot[0]) # 'N'
print(parrot[3]) # 'w'

print(parrot[-1]) # 'e'
print(parrot[-2]) # 'u'


print()
print(parrot[3])
print(parrot[4])
print()
print(parrot[14 - 11])
print(parrot[14 - 8])
print(parrot[14 - 6])

print(len(parrot)) # 14
print(parrot[3 - 14])
print(parrot[4 - 14])
print()
print(parrot[-11])
print(parrot[-8])
print(parrot[-6])

slice string

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
#         01234567890123
parrot = "Norwegian Blue"
# -43210987654321

print(parrot)


print(parrot[0:6]) # 'Norweg'
print(parrot[3:5]) # 'we'

print(parrot[0:9]) # 'Norwegian'
print(parrot[:9]) # Norwegian

print(parrot[10:14]) # 'Blue'
print(parrot[10:]) # 'Blue'

print(parrot[:]) # 'Norwegian Blue'

### slice with negative numbers

print(parrot[-4:-1]) # 'Blu'
# print(parrot[-4:0]) ### error
print(parrot[-4:]) # 'Blue'
print(parrot[:-5]) # 'Norwegian'

print(parrot[3:5]) # 'we'
print(parrot[-11:-9]) # 'we'
print(parrot[3:-9]) # 'we'
print(parrot[-11:5]) # 'we'

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
### step in string slice

print(parrot[0:6]) # 'Norweg'
print(parrot[0:6:2]) # 'Nre'
print(parrot[0:6:3]) # 'Nw'

number = "2,123;456:123 478,124;807"
print(number[1::4]) # ",;: ,;"
seperators = number[1::4]


value = "".join(char if char not in seperators else " " for char in number).split()
print([int(val) for val in value]) ## [2, 123, 456, 123, 478, 124, 807]

values = []
for char in number:
if char not in seperators:
values.append(char)
else:
values.append(' ')
value_1 = "".join(values).split()
print(value_1) # ['2', '123', '456', '123', '478', '124', '807']
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
### slice backwards

letters = "abcdefghijklmnopqrstuvwxyz"

## starter > stopper(not included)
backwards = letters[25:0:-1]
print(backwards) # 'zyxwvutsrqponmlkjihgfedcb'

backwards = letters[25::-1]
print(backwards) # 'zyxwvutsrqponmlkjihgfedcba'
# backwards = letters[25:-1:-1] ## error ''

## recommend
backwards = letters[::-1]
print(backwards) # 'zyxwvutsrqponmlkjihgfedcba'

##example
print(letters[16:13:-1]) # 'qpo'
print(letters[4::-1]) # 'edcba' # first 5 letters
print(letters[:-9:-1]) # 'zyxwvuts' # last 8 letters
1
2
3
4
5
letters = []

print(letters[0]) ## first letter, when null have error: IndexError: list index out of range
print(letters[:1]) ## first letter, when null no error
print(letters[-1:]) ## last letter, when null no error

string operators

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
string1 = "he's "
string2 = "probably "
string3 = "pining "
string4 = "for the "
string5 = "fjords"

# he's probably pining for the fjords
print(string1 + string2 + string3 + string4 + string5)
### not recommend
print("Hello " "World!") # Hello World!
# print(string1 string2 string3) ## error

print("Hello " * 5) # "Hello Hello Hello Hello Hello "
# print("Hello" + 5) # TypeError: can only concatenate str (not "int") to str


today = "friday"
print("day" in today) # True
print('thur' in today) # False

fstring

1
2
3
4
5
# Pi is approximately    3.14285714285714279370154144999105483293533325195312
print(f"Pi is approximately {22 / 7:55.50f}")
pi = 22 / 7
# Pi is approximately 3.14285714285714279370154144999105483293533325195312
print(f"Pi is approximately {pi:12.50f}")

string methods

https://docs.python.org/3/library/stdtypes.html#string-methods

str()

class str(Sequence[str])
str(object=’’) -> str str(bytes_or_buffer[, encoding[, errors]]) -> str
Create a new string object from the given object. If encoding or errors is specified, then the object must expose a data buffer that will be decoded using the given encoding and error handler. Otherwise, returns the result of object.__str__() (if defined) or repr(object). encoding defaults to sys.getdefaultencoding(). errors defaults to ‘strict’.

format()

def format(self: LiteralString,
*args: LiteralString,
**kwargs: LiteralString) -> LiteralString
S.format(*args,**kwargs) -> str
Return a formatted version of S, using substitutions from args and kwargs. The substitutions are identified by braces (‘{‘ and ‘}’)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
age = 24
print("My age is " + str(age) + " years") # "My age is 24 years"
# replacement fields
print("My age is {0} years".format(age))

## There are 31 days in Jan, Mar, May, Jul, Aug, Oct and Dec
print("There are {0} days in {1}, {2}, {3}, {4}, {5}, {6} and {7}"
.format(31, 'Jan', 'Mar','May', 'Jul', 'Aug', 'Oct', 'Dec'))

## Jan: 31, Feb: 28, Mar: 31, Apr: 30, May: 31, Jun: 30, Jul: 31, Sep: 30, Oct: 31, Nov: 30, Dec: 31
print("Jan: {2}, Feb: {0}, Mar: {2}, Apr: {1}, May: {2}, Jun: {1}, Jul: {2}, Sep: {1}, Oct: {2}, Nov: {1}, Dec: {2}"
.format(28, 30, 31))

print("""Jan: {2}
Feb: {0}
Mar: {2}
Apr: {1}
May: {2}
Jun: {1}
Jul: {2}
Sep: {1}
Oct: {2}
Nov: {1}
Dec: {2}""".format(28, 30, 31))
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
# No. 1 squared is 1 and cubed is 1
# No. 2 squared is 4 and cubed is 8
# No. 3 squared is 9 and cubed is 27
# No. 4 squared is 16 and cubed is 64

for i in range(1, 13):
print("No. {} squared is {} and cubed is {}".format(i, i ** 2, i ** 3))

# No. 1 squared is 1 and cubed is 1
# No. 2 squared is 4 and cubed is 8
# No. 3 squared is 9 and cubed is 27

for i in range(1, 13):
print("No. {0:2} squared is {1:4} and cubed is {2:4}".format(i, i ** 2, i ** 3))

### < left align, ^ center
# No. 1 squared is 1 and cubed is 1
# No. 2 squared is 4 and cubed is 8
# No. 3 squared is 9 and cubed is 27
# No. 4 squared is 16 and cubed is 64
# No. 5 squared is 25 and cubed is 125
print()
for i in range(1, 13):
print("No. {0:2} squared is {1:<3} and cubed is {2:^4}".format(i, i ** 2, i ** 3))


print()
# Pi is approximately 3.142857142857143
print("Pi is approximately {0:12}".format(22 / 7))
# Pi is approximately 3.142857 ## 6 digits
print("Pi is approximately {0:12f}".format(22 / 7))


# Pi is approximately 3.14285714285714279370154144999105483293533325195312
# Pi is approximately 3.14285714285714279370154144999105483293533325195312
# Pi is approximately 3.14285714285714279370154144999105483293533325195312
print("Pi is approximately {0:12.50f}".format(22 / 7))
print("Pi is approximately {0:52.50f}".format(22 / 7))
print("Pi is approximately {0:62.50f}".format(22 / 7))

# Pi is approximately 3.142857142857142793701541449991054832935333252
# Pi is approximately 3.14285714285714279370154144999105483293533325195312
# Pi is approximately 3.142857142857142793701541449991054832935333251953125000
print("Pi is approximately {0:<72.45f}".format(22 / 7))
print("Pi is approximately {0:<72.50f}".format(22 / 7))
print("Pi is approximately {0:<72.54f}".format(22 / 7))

join()

def join(self: LiteralString, __iterable: Iterable[LiteralString]) -> LiteralString
Concatenate any number of strings.
The string whose method is called is inserted in between each given string. The result is returned as a new string.
Example: ‘.’.join([‘ab’, ‘pq’, ‘rs’]) -> ‘ab.pq.rs’

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
flowers = [
'Daffodil',
'Evening Primrose',
'Hydrangea',
'Iris',
'Lavender',
'Sunflower',
'Tiger Lily',
]

for flower in flowers:
print(flower)

separator = ' | '
output = separator.join(flowers)
## Daffodil | Evening Primrose | Hydrangea | Iris | Lavender | Sunflower | Tiger Lily
print(output)

''.join(['1', '2', '3'])

split()

def split(self: LiteralString,
sep: LiteralString | None = None,
maxsplit: SupportsIndex = -1) -> list[LiteralString]
Return a list of the substrings in the string, using sep as the separator string.

sep
The separator used to split the string.
When set to None (the default value), will split on any whitespace character (including newline, tab and spaces) and will discard
empty strings from the result.
maxsplit
Maximum number of splits (starting from the left). -1 (the default value) means no limit

1
2
3
4
5
6
7
8
9
10
panagram = "The quick brown fox jumps over the lazy dog"
panagram = """The quick brown
fox jumps\tover
the lazy dog"""

words = panagram.split()
print(words) # ['The', 'quick', 'brown', 'fox', 'jumps', 'over', 'the', 'lazy', 'dog']

numbers = "12,165,425,365,254"
print(numbers.split(',')) # ['12', '165', '425', '365', '254']
center()

def center(self: LiteralString,
__width: SupportsIndex,
__fillchar: LiteralString = “ “) -> LiteralString
Return a centered string of length width.
Padding is done using the specified fill character (default is a space).

1
2
3
4
5
6
7
8
9
10
11
12
def banner_text(text):
screen_width = 80
if len(text) > screen_width -4:
print("The text is too long to fit in the specified width")
if text == '*':
print('*' * screen_width)
else:
output_string = '**{0}**'.format(text.center(screen_width -4))
print(output_string)

# "** Hello world! **"
banner_text('Hello world!')
casefold()

def casefold(self: LiteralString) -> LiteralString
Return a version of the string suitable for caseless comparisons

isnumeric()

def isnumeric(self) -> bool
Return True if the string is a numeric string, False otherwise.
A string is numeric if all characters in the string are numeric and there is at least one character in the string.

isalpha()

def isalpha(self) -> bool
Return True if the string is an alphabetic string, False otherwise.
A string is alphabetic if all characters in the string are alphabetic and there is at least one character in the string.

Common Sequence Operations

s[i]

ith item of s, origin 0

s[i:j]

slice of s from i to j

s[i:j:k]

slice of s from i to j with step k

in, not in

in
not in

comp_operator ::= “<” | “>” | “==” | “>=” | “<=” | “<>” | “!=”
| “is” [“not”] | [“not”] “in”

The operators in and not in test for collection membership. x in s evaluates to true if x is a member of the collection s, and false otherwise. x not in s returns the negation of x in s.
in it make sense for many other object types to support membership tests without being a sequence. In particular, dictionaries (for keys) and sets support membership testing.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
parrot = "Norwegian Blue"

# letter = input("Enter a character: ")

letter = 'A'
# letter = 'Blue'

if letter in parrot:
print("{} is in {}".format(letter, parrot))
else:
print("I don't need that letter")

### not in

# activity = input("What would you like to do today? ")
# activity = "I want to learn Python"
activity = "I want to go to the Cinema" # But I want to go to the cinema
if "cinema" not in activity:
print("But I want to go to the cinema")


activity = "I want to go to the Cinema" # But I want to go to the cinema
if "cinema" not in activity.casefold():
print("But I want to go to the cinema")
count()

def count(self,
x: str,
__start: SupportsIndex | None = …,
__end: SupportsIndex | None = …) -> int
S.count(sub[, start[, end]]) -> int
Return the number of non-overlapping occurrences of substring sub in string S[start:end]. Optional arguments start and end are interpreted as in slice notation.

print("mississippi".count("s")) # 4
print("mississippi".count("ssi")) # 2

min() max()

min(s)

smallest item of s

max(s)

largest item of s

index()

def index(self,
__value: _T,
__start: SupportsIndex = 0,
__stop: SupportsIndex = sys.maxsize) -> int
Return first index of value.
Raises ValueError if the value is not present.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
shopping_list = ['milk', 'pasta', 'eggs', 'spam', 'bread', 'rice']

item_to_find = 'spam'
found_at = None

# # find the first item in a list
# for index in range(len(shopping_list)):
# if shopping_list[index] == item_to_find:
# found_at = index
# break

if item_to_find in shopping_list:
found_at = shopping_list.index(item_to_find)

if found_at is not None:
print("Item found at index {}".format(found_at))
else:
print("{} not found".format(item_to_find))

some random meaning

What are iterator, iterable, and iteration?

Iteration is a general term for taking each item of something, one after another. Any time you use a loop, explicit or implicit, to go over a group of items, that is iteration.

In Python, iterable and iterator have specific meanings.

An iterable is an object that has an __iter__ method which returns an iterator, or which defines a __getitem__ method that can take sequential indexes starting from zero (and raises an IndexError when the indexes are no longer valid). So an iterable is an object that you can get an iterator from.

An iterator is an object with a next (Python 2) or __next__ (Python 3) method.

Whenever you use a for loop, or map, or a list comprehension, etc. in Python, the next method is called automatically to get each item from the iterator, thus going through the process of iteration.

pseudocode

epression

evaluated to a value

refactoring

means changing its structure, without changing its behaviour

edge case and corner cases testing

outlying values at both the low and high ends
outlying values at the low end only
outlying values at the high end only
no outlying values
only outlying values(no valid ones)
an empty data set

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58

data = [4,5,104,105,110,120,130,130,150,
160,170,183,185,187,188,191,350,360]
# data = [4,5,104,105,110,120,130,130,150,
# 160,170,183,185,187,188,191]
# data = [104,105,110,120,130,130,150,
# 160,170,183,185,187,188,191,350,360]
# data = [104,105,110,120,130,130,150,
# 160,170,183,185,187,188,191]
# data = [350,3601,1464,1541,1132,3112,4682]
# data = []

# del data[0:2]
# print(data) # [104, 105, 110, 120, 130, 130, 150, 160, 170, 183, 185, 187, 188, 191, 350, 360]
#
# del data[14:]
# print(data) # [104, 105, 110, 120, 130, 130, 150, 160, 170, 183, 185, 187, 188, 191]


min_valid = 100
max_valid = 200

## wrong approche
# for index, value in enumerate(data):
# if value < min_valid or value > max_valid:
# del data[index]
# print(data)

#### for ordered list
# process the low values in the list
stop = 0
for index, value in enumerate(data):
if value >= min_valid:
stop = index
break
print(stop) # 2
del data[:stop]
print(data) # [104, 105, 110, 120, 130, 130, 150, 160, 170, 183, 185, 187, 188, 191, 350, 360]
# process the high values in the list
# ### error the high outlying test fell
# start = -1
# for index, value in enumerate(data):
# if value >= max_valid:
# start = index
# break
# print(start)
# del data[start:]
# print(data)
### more efficient
start = 0
for index in range(len(data) - 1, -1, -1):
print(index)
if data[index] <= max_valid:
start = index + 1
break
print(start)
del data[start:]
print(data)

algorithms Performance

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
import timeit

max_value = 10000000
min_valid = 10
max_valid = 9999997

data_list1 = list(range(max_value))
data_list2 = list(range(max_value))
data_list3 = list(range(max_value))


def sanitise_1(data):

# process the low values in the list
stop = 0
for index, value in enumerate(data):
if value >= min_valid:
stop = index
break
del data[:stop]
# process the high values in the list

start = 0
for index in range(len(data) - 1, -1, -1):
if data[index] <= max_valid:
start = index + 1
break
del data[start:]

def sanitise_2(data):
top_index = len(data) - 1
for index, value in enumerate(reversed(data)):
if value < min_valid or value > max_valid:
del data[top_index - index]


def sanitise_3(data):
for index in range(len(data) - 1, -1, -1):
current = data[index]
if current < min_valid or current > max_valid:
del data[index]


if __name__ == '__main__':
print("Timing")
x = timeit.timeit("sanitise_1(data_list1)",
setup="from __main__ import sanitise_1, data_list1",
number=1)
print("{:15.15f}".format(x))
y = timeit.timeit("sanitise_2(data_list2)",
setup="from __main__ import sanitise_2, data_list2",
number=1)
print("{:15.15f}".format(y))
z = timeit.timeit("sanitise_3(data_list3)",
setup="from __main__ import sanitise_3, data_list3",
number=1)
print("{:15.15f}".format(z))

function signature

the definition of a function

def print(*values: object,
sep: str | None = “ “,
end: str | None = “\n”,
file: SupportsWrite[str] | None = None,
flush: Literal[False] = False) -> None

1
2
3
4
5
name = 'Tim'
age = 10

print(name, age, 'python', 2020) # 'Tim 10 python 2020'
print(name, age, 'python', 2020, sep=', ') # 'Tim, 10, python, 2020'

debug

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
LOW = 1
HIGH = 1000


def guess_binary(answer, low, high):
guesses = 1
while True:
guess = low + (high - low) // 2

if guess == answer:
return guesses
elif guess > answer:
high = guess - 1
elif guess < answer:
low = guess + 1

guesses = guesses + 1


max_guesses = 0
guesses_count = 0
for number in range(LOW, HIGH + 1):
number_of_guesses = guess_binary(number, LOW, HIGH)
if max_guesses < number_of_guesses:
max_guesses, guesses_count = number_of_guesses, 1
elif max_guesses == number_of_guesses:
guesses_count += 1
print(f"{number} guessed in {number_of_guesses}")
print(f"Max guess {max_guesses} repeat {guesses_count} times")

section 2

code blocks

indentation to indicate code blocks

if statements

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
# name = input("Please enter your name: ")
name = 'lucfe'
# age = int(input("How old are you, {0}? ".format(name)))
age = 15
print(age)

if age >= 18:
print("You are old enough to vote")
print("Please put an x in the box")
else:
print("Please come back in {0} years".format(18 - age))


if age < 18:
print("Please come back in {0} years".format(18 - age))
elif age == 900:
print("Sorry, Yoda, you die in Return of the Jedi")
else:
print("You are old enough to vote")
print("Please put an x in the box")
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
answer = 5

print("Please guess number between 1 and 10: ")
guess = int(input())

if guess < answer:
print("Pleaser guess higher")
guess = int(input())
if guess == answer:
print("Well done, you guessed it")
else:
print("Sorry, you have not guessed correctly")
elif guess > answer:
print("Pleaser guess lower")
guess = int(input())
if guess == answer:
print("Well done, you guessed it")
else:
print("Sorry, you have not guessed correctly")
else:
print("You got it first time")
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
answer = 5

print("Please guess number between 1 and 10: ")
guess = int(input())

if guess != answer:
if guess < answer:
print("Please guess higher")
else: # guess must be greater than answer
print("Please guess lower")
guess = int(input())
if guess == answer:
print("Well done, you guessed it")
else:
print("Sorry, you have not guessed correctly")
else:
print("You guess it first time!")

Boolean expression

or
and
not

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
age = int(input("How old are you? "))

if age >= 16 and age <= 65:
print("Have a good day at work")

# Simplify chained comparison
if 16 <= age <= 65:
print("Have a good day at work")
else:
print("Enjoy your free time")

#### or
if age < 16 or age > 65:
print("Enjoy your free time")
else:
print("Have a good day at work")
  • when comparing conditions using and, Python will stop checking as soon as it finds a condition that is False.
  • when comparing conditions using or, Python will stop checking as soon as it finds a condition that is True.
1
2
3
4
5
6
7
8
9
10
# day = "Monday"
day = "Saturday"
temperature = 30
# raining = True
raining = False

if (day == "Saturday" and temperature > 27) or not raining:
print("Go swimming")
else:
print("Learn Python")

truthy value

Here are most of the built-in objects considered false:

  • constants defined to be false: None and False
  • zero of any numeric type: 0, 0.0, 0j, Decimal(0), Fraction(0, 1)
  • empty sequences and collections: ‘’, (), [], {}, set(), range(0)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
if 0 :
print("True")
else:
print("False")

if '' :
print("True")
else:
print("False")

name = input("Please enter your name: ")
# if name != '':
if name:
print("Hello, {}". format(name))
else:
print("Are you the man with no name")

repeat block

for loop

an iterable object: that can be iterated over

1
2
3
4
parrot = "Norwegian Blue"

for character in parrot:
print(character)
1
2
3
4
5
6
7
8
9
number = "2,123;456:123 478,124;807"
# print(number[1::4]) # ",;: ,;"
# seperators = number[1::4]

seperators = ''
for char in number:
if not char.isnumeric():
seperators = seperators + char
print(seperators) # ",;: ,;"
nest for loop
1
2
3
4
for i in range(1, 9):
for j in range(1, 9):
print("{0} times {1} is {2}".format(j, i, i * j))
print("----------------------")
range()

class range(Sequence[int])
range(stop) -> range object range(start, stop[, step]) -> range object
Return an object that produces a sequence of integers from start (inclusive) to stop (exclusive) by step. range(i, j) produces i, i+1, i+2, …, j-1. start defaults to 0, and stop is omitted! range(4) produces 0, 1, 2, 3. These are exactly the valid indices for a list of 4 elements. When step is given, it specifies the increment (or decrement).

1
2
3
4
5
# i is now 1
# i is now 2
# i is now 3
for i in range(1, 4):
print("i is now {}".format(i))
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
# 0
# 1
# 2
# 3
for i in range(4):
print(i)


# 1
# 3
for i in range(1, 4, 2):
print(i)


# 4
# 2
for i in range(4, 1, -2):
print(i)
1
2
3
4
5
6
7
age = int(input("How old are you? "))
## more efficient
if 16 <= age <= 65:
# if age in range(16,66):
print("Have a good day at work")
else:
print("Enjoy your free time")

continue and break

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
shopping_list = ['milk', 'pasta', 'eggs', 'spam', 'bread', 'rice']

# for item in shopping_list:
# if item != 'spam':
# print("Buy " + item)

# Buy milk
# Buy pasta
# Buy eggs
# Buy bread
# Buy rice
for item in shopping_list:
if item == 'spam':
continue
print("Buy " + item)


#### break
# Buy milk
# Buy pasta
# Buy eggs
for item in shopping_list:
if item == 'spam':
break
print("Buy " + item)

break is efficient

1
2
3
4
5
6
7
8
9
10
11
12
shopping_list = ['milk', 'pasta', 'eggs', 'spam', 'bread', 'rice']

item_to_find = 'spam'
found_at = None

# find the first item in a list
for index in range(len(shopping_list)):
if shopping_list[index] == item_to_find:
found_at = index
break

print("Item found at index {}".format(found_at))
in while loop
1
2
3
4
5
6
7
8
9
10
available_exits = ['north', 'south', 'east', 'west']

chosen_exit = ''
while chosen_exit not in available_exits:
chosen_exit = input("Please choose a direction: ").casefold()
if chosen_exit == 'quit':
print("Game over")
break

print("aren't you glad you got out of there")

while loop

dont need to know how many times the loop will execute.

1
2
3
4
5
6
7
8
9
10
11
12
# for i in range(4):
# print("i is now {}".format(i))

i = 0
# i is now 0
# i is now 1
# i is now 2
# i is now 3
while i < 4:
print("i is now {}".format(i))
i += 1
# i = i + 1
1
2
3
4
5
6
7
available_exits = ['north', 'south', 'east', 'west']

chosen_exit = ''
while chosen_exit not in available_exits:
chosen_exit = input("Please choose a direction: ")

print("aren't you glad you got out of there")
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
import random

highest = 10
answer = random.randint(1, highest)
print(answer) # TODO: remove after testing

guess = 0
while guess != answer:
guess = int(input("Please guess number between 1 and {}: ".format(highest)))

if guess == 0:
break

if guess == answer:
print("You guess it")
break
elif guess < answer:
print("Please guess higher")
else:
print("Please guess lower")

when you have an ordered set of data to search through, you can split the data in half each time.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
low = 1
high = 1000

print("Please think of a number between {} and {}".format(low, high))
input("Press ENTER to start")

my_number = 837
guesses = 1
while True:
print(f"\tGuessing in the range of {low} to {high}")
guess = low + (high - low) // 2
low_or_high = input("is {0} the number? enter '0' for correct. "
"{0} is higher or lower than the answer? enter '+' for higher, '-' for lower ".format(guess))
if low_or_high == '0':
print(f"the number is {guess}")
print(f"I guess {guesses} times")
break
elif low_or_high == '+': # guess is higher than the answer
high = guess - 1
elif low_or_high == '-': # guess is lower than the answer
low = guess + 1
else:
print("Please enter '+', '-', '0'")

guesses = guesses + 1

else in a loop

if the loop terminate normal

  • for loop end without a break
  • while loop condition false without break
1
2
3
4
5
6
7
8
9
10
# numbers = [1, 45, 32, 12, 60]  # The numbers are unacceptable
numbers = [1, 45, 31, 12, 60] # All those numbers are fine


for number in numbers:
if number % 8 == 0:
print("The numbers are unacceptable")
break
else:
print("All those numbers are fine")
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
low = 1
high = 1000

print("Please think of a number between {} and {}".format(low, high))
input("Press ENTER to start")

my_number = 837
guesses = 1
while low != high:
print(f"\tGuessing in the range of {low} to {high}")
guess = low + (high - low) // 2
low_or_high = input("is {0} the number? enter '0' for correct. "
"{0} is higher or lower than the answer? "
"enter '+' for higher, '-' for lower ".format(guess))
if low_or_high == '0':
print(f"the number is {guess}")
print(f"I guess {guesses} times")
break
elif low_or_high == '+': # guess is higher than the answer
high = guess - 1
elif low_or_high == '-': # guess is lower than the answer
low = guess + 1
else:
print("Please enter '+', '-', '0'")

guesses = guesses + 1
else:
print(f"You thought of the number {low}")
1
2
3
4
5
6
7
8
9
10
available_exits = ['north', 'south', 'east', 'west']

chosen_exit = ''
while chosen_exit not in available_exits:
chosen_exit = input("Please choose a direction: ").casefold()
if chosen_exit == 'quit':
print("Game over")
break
else:
print("aren't you glad you got out of there")

list comprehensions and generator expressions

modules

each .py file created by you becomes a new python module

virtualenv venv

active venv

cd C:\Users\liucf\Documents\learning\Learn-Python-Programming-Masterclass
Set-ExecutionPolicy Unrestricted -Scope Process
.\.venv\Scripts\activate

deactivate
deactivate

random module

1
2
import random
answer = random.randint(1, 10)

def randint(self,
a: int,
b: int) -> int
Return random integer in range [a, b], including both end points.

augmented assignment

guesses = 0
guesses = guesses + 1
guesses += 1

when binary operation(it take 2 operands to work on) and there is an assignment
change to augmented assignment

in python, the augmented assignment form only evaluates the assignee(guesses in the example) once, so more efficient
using an augmented assignment, it can perform the operation in-place where possible, modifying the original variable(not create a new one)

evaluating a variable basically consists of looking its value

PEP: python enhancement proposal

PEP 8
style guide for python

https://peps.python.org/pep-0008/

naming conventions

Function names should be lowercase, with words separated by underscores as necessary to improve readability.

Variable names follow the same convention as function names.

code layout

Maximum Line Length

pycham -> file -> setting -> editor -> code style -> visual guides -> 72,80

conditional debugging(breakpoint)

right click breakpoint( red dot )

hashlib

hashlib.algorithms_guaranteed
A set containing the names of the hash algorithms guaranteed to be supported by this module on all platforms.

sha256

def encode(self,
encoding: str = “utf-8”,
errors: str = “strict”) -> bytes
Encode the string using the codec registered for encoding.

encoding
The encoding in which to encode the string.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
import hashlib

print(sorted(hashlib.algorithms_guaranteed))
# ['blake2b', 'blake2s', 'md5', 'sha1', 'sha224', 'sha256', 'sha384', 'sha3_224', 'sha3_256', 'sha3_384', 'sha3_512', 'sha512', 'shake_128', 'shake_256']
print(sorted(hashlib.algorithms_available))

python_program = """for i in range(10):
print(i)
"""
print(python_program)

# def chr(__i: int) -> str
# Return a Unicode string of one character with ordinal i; 0 <= i <= 0x10ffff.
# 102 f
# 111 o
# 114 r
# ...
# for b in python_program.encode('utf-8'):
# print(b, chr(b))
# print(python_program.encode('utf-8')) # b'for i in range(10):\nprint(i)\n'


original_hash = hashlib.sha256(python_program.encode('utf-8'))
print(original_hash) # <sha256 _hashlib.HASH object @ 0x000002862FFB9930>
print(f"SHA256 {original_hash.hexdigest()}") # SHA256 5033b46b90e4250ce294d67078ad62b516b4b65964488e4605c7d216263c1565

python_program += "print('code change')"
new_hash = hashlib.sha256(python_program.encode('utf-8'))
print(f"SHA256 {new_hash.hexdigest()}") # SHA256 0b4bd931add7a7164314f8feb7927ebca575417f8650b1f6069ba783a3a0f9e2

# the code and been modified
if new_hash.hexdigest() == original_hash.hexdigest():
print('the code has not changed')
else:
print('the code and been modified')

section 3: buildin types

5 sequence types

https://docs.python.org/3/library/stdtypes.html#sequence-types-list-tuple-range

sequence: an ordered set of items
index can be used to refer the item
sequence are iterable types

string
list
tuple
range
bytes and bytearray

muteble sequence

slice and assign and delete

s[i] = x

item i of s is replaced by x

s[i:j] = t

slice of s from i to j is replaced by the contents of the iterable t

1
2
3
4
5
6
7
8
9
10
11
12
13
14
computer_parts = [
'computer',
'monitor',
'keyboard',
'mouse',
'mouse mat'
]
print(computer_parts)
computer_parts[3] = 'trackball'
print(computer_parts) # ['computer', 'monitor', 'keyboard', 'trackball', 'mouse mat']

print(computer_parts[3:]) # ['trackball', 'mouse mat']
computer_parts[3:] = ['trackball']
print(computer_parts) # ['computer', 'monitor', 'keyboard', 'trackball']

s[i:j:k] = t

the elements of s[i:j:k] are replaced by those of t

del s[i:j]

same as s[i:j] = []

del s[i:j:k]

removes the elements of s[i:j:k] from the list

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42

data = [4,5,104,105,110,120,130,130,150,
160,170,183,185,187,188,191,350,360]

# del data[0:2]
# print(data) # [104, 105, 110, 120, 130, 130, 150, 160, 170, 183, 185, 187, 188, 191, 350, 360]
#
# del data[14:]
# print(data) # [104, 105, 110, 120, 130, 130, 150, 160, 170, 183, 185, 187, 188, 191]


min_valid = 100
max_valid = 200

## wrong approche
# for index, value in enumerate(data):
# if value < min_valid or value > max_valid:
# del data[index]
# print(data)

#### for ordered list
# process the low values in the list
stop = 0
for index, value in enumerate(data):
if value >= min_valid:
stop = index
break
print(stop) # 2
del data[:stop]
print(data) # [104, 105, 110, 120, 130, 130, 150, 160, 170, 183, 185, 187, 188, 191, 350, 360]
# process the high values in the list

#### more efficient
start = 0
for index in range(len(data) - 1, -1, -1):
print(index)
if data[index] <= max_valid:
start = index + 1
break
print(start)
del data[start:]
print(data)
itarate backwards
1
2
3
4
5
6
7
8
9
10
11
12
13
#### for not sorted list
data = [104,101,4,105,308,103,5,
107,100,306,106,102,108]

min_valid = 100
max_valid = 200

for index in range(len(data) - 1, -1, -1):
if data[index] < min_valid or data[index] > max_valid:
# print(index, data)
del data[index]
### [104, 101, 105, 103, 107, 100, 106, 102, 108]
print(data)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
data = [104,101,4,105,308,103,5,
107,100,306,106,102,108]

min_valid = 100
max_valid = 200

# for index in range(len(data) - 1, -1, -1):
# if data[index] < min_valid or data[index] > max_valid:
# # print(index, data)
# del data[index]
# ### [104, 101, 105, 103, 107, 100, 106, 102, 108]
# print(data)

## more efficient
top_index = len(data) - 1
for index, value in enumerate(reversed(data)):
i = top_index - index
# print(i)
if value < min_valid or value > max_valid:
print(i, data)
del data[i]
print(data)

s.append(x)

appends x to the end of the sequence (same as s[len(s):len(s)] = [x])

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
available_parts = [
'computer',
'monitor',
'keyboad',
'mouse',
# 'mouse mat',
'hdmi cable'
]
current_choice = '-'
computer_parts = []

# valid_choices = [str(i) for i in range(1, len(available_parts) + 1)]
# print(valid_choices) # ['1', '2', '3', '4', '5', '6']

valid_choices = []
for i in range(1, len(available_parts) + 1):
valid_choices.append(str(i))
print(valid_choices) # ['1', '2', '3', '4', '5', '6']

while current_choice != '0':
# if current_choice in '123456':
if current_choice in valid_choices:
print(f"Adding {current_choice}")

# if current_choice == '1':
# computer_parts.append("computer")
# elif current_choice == '2':
# computer_parts.append("monitor")
# elif current_choice == '3':
# computer_parts.append("keyboad")
# elif current_choice == '4':
# computer_parts.append("mouse")
# elif current_choice == '5':
# computer_parts.append("mouse mat")
# elif current_choice == '6':
# computer_parts.append('hdmi cable')

computer_parts.append(available_parts[int(current_choice) - 1])

else:
print("Please add options from the list below:")

# print("1: computer")
# print("2: monitor")
# print("3: keyboad")
# print("4: mouse")
# print("5: mouse mat")
# print("6: hdmi cable")
# print("0: to finish")

# not efficient
# for part in available_parts:
# print(f"{available_parts.index(part) + 1}: {part}")

for number, part in enumerate(available_parts):
print(f"{number + 1}: {part}")

current_choice = input()


print(computer_parts)

s.clear()

removes all items from s (same as del s[:])

s.copy()

creates a shallow copy of s (same as s[:])

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
# more_numbers = list(numbers)
# print(more_numbers) # [2, 4, 6, 8, 1, 3, 5, 7, 9]
# print(numbers is more_numbers) # False
# print(numbers == more_numbers) # True
#
# another_numbers = numbers[:]
# print(another_numbers) # [2, 4, 6, 8, 1, 3, 5, 7, 9]
# print(numbers is another_numbers) # False
# print(numbers == another_numbers) # True

## most efficient
more_numbers = numbers.copy()
print(more_numbers) # [2, 4, 6, 8, 1, 3, 5, 7, 9]
print(numbers is more_numbers) # False
print(numbers == more_numbers) # True

s.extend(t) or s += t

extends s with the contents of t (for the most part the same as s[len(s):len(s)] = t)

def extend(self, __iterable: Iterable[_T]) -> None
Extend list by appending elements from the iterable

1
2
3
4
even = [2, 4, 6, 8]
odd = [1, 3, 5, 7, 9]
even.extend(odd)
print(even) ## [2, 4, 6, 8, 1, 3, 5, 7, 9]

s *= n

updates s with its contents repeated n times

s.insert(i, x)

inserts x into s at the index given by i (same as s[i:i] = [x])

s.pop() or s.pop(i)

retrieves the item at i and also removes it from s

s.remove(x)

remove the first item from s where s[i] is equal to x

def remove(self, __value: _T) -> None
Remove first occurrence of value.
Raises ValueError if the value is not present.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
available_parts = [
'computer',
'monitor',
'keyboad',
'mouse',
# 'mouse mat',
'hdmi cable'
]
current_choice = '-'
computer_parts = []

valid_choices = []
for i in range(1, len(available_parts) + 1):
valid_choices.append(str(i))
print(valid_choices) # ['1', '2', '3', '4', '5', '6']

while current_choice != '0':
if current_choice in valid_choices:

index = int(current_choice) - 1
chosen_part = available_parts[index]

# remove method
if chosen_part in computer_parts:
print(f"Removing {current_choice}")
computer_parts.remove(chosen_part)
else:
print(f"Adding {current_choice}")
computer_parts.append(chosen_part)
print(computer_parts)
else:
print("Please add options from the list below:")

for number, part in enumerate(available_parts):
print(f"{number + 1}: {part}")

current_choice = input()


print(computer_parts)

s.reverse()

reverses the items of s in place

buildin method reversed()

Return a reverse iterator over the values of the given sequence.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
data = [104,101,4,105,308,103,5,
107,100,306,106,102,108]

min_valid = 100
max_valid = 200

# for index in range(len(data) - 1, -1, -1):
# if data[index] < min_valid or data[index] > max_valid:
# # print(index, data)
# del data[index]
# ### [104, 101, 105, 103, 107, 100, 106, 102, 108]
# print(data)

## more efficient
top_index = len(data) - 1
for index, value in enumerate(reversed(data)):
i = top_index - index
# print(i)
if value < min_valid or value > max_valid:
print(i, data)
del data[i]
print(data)

list

list are mutalbe

list() function

Built-in mutable sequence.
If no argument is given, the constructor creates a new empty list. The argument must be an iterable if specified.

bindin mutible name to a list

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
shopping_list = [
'milk',
'pasta',
'eggs',
'spam',
'bread',
'rice'
]

another_list = shopping_list
print(id(shopping_list)) # 1978024513920
print(id(another_list)) # 1978024513920

shopping_list += ['cookies']
print(shopping_list)
print(id(shopping_list)) # 1978024513920
print(another_list) # ['milk', 'pasta', 'eggs', 'spam', 'bread', 'rice', 'cookies']
print(id(another_list)) # 1978024513920

a = b = c = d = e = f = another_list
print(a)

print("Adding cream")
b.append("cream")

print(c) # ['milk', 'pasta', 'eggs', 'spam', 'bread', 'rice', 'cookies', 'cream']
print(d) # ['milk', 'pasta', 'eggs', 'spam', 'bread', 'rice', 'cookies', 'cream']

create list

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
empty_list = []

even = [2, 4, 6, 8]
odd = [1, 3, 5, 7, 9]

numbers = even + odd
print(numbers) # [2, 4, 6, 8, 1, 3, 5, 7, 9]

sorted_numbers = sorted(numbers)
print(sorted_numbers) # [1, 2, 3, 4, 5, 6, 7, 8, 9]

digits = sorted("4425725136")
print(digits) # ['1', '2', '2', '3', '4', '4', '5', '5', '6', '7']

digits_1 = list("17854612")
print(digits_1) # ['1', '7', '8', '5', '4', '6', '1', '2']

nest list

1
2
3
4
5
6
7
8
9
10
even = [2, 4, 6, 8]
odd = [1, 3, 5, 7, 9]

numbers = [even, odd]
print(numbers) # [[2, 4, 6, 8], [1, 3, 5, 7, 9]]

for number_list in numbers:
print(number_list)
for number in number_list:
print(number)
1
2
3
4
5
6
7
8
my_list = [
1, 2, 3,
4, 5, 6,
]
result = some_function_that_takes_arguments(
'a', 'b', 'c',
'd', 'e', 'f',
)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
menu = [
['egg', 'bacon'],
['egg', 'sausage', 'bacon'],
['egg', 'spam'],
['egg', 'bacon', 'spam'],
['egg', 'bacon', 'sausage', 'spam'],
['spam', 'bacon', 'sausage', 'spam'],
['spam', 'sausage', 'spam', 'bacon', 'spam', 'tomato', 'spam'],
['spam', 'egg', 'spam', 'spam', 'bacon', 'spam'],
]

for meal in menu:
for index in range(len(meal) - 1, -1, -1):
if meal[index] == 'spam':
del meal[index]
print(meal)

for meal in menu:
for item in meal:
if item != 'spam':
print(item, end=' ')
print()

buildin function: enumerate()

class enumerate(Iterator[tuple[int, _T]], Generic[_T])
Return an enumerate object.

iterable
an object supporting iteration
The enumerate object yields pairs containing a count (from start, which defaults to zero) and a value yielded by the iterable argument.
enumerate is useful for obtaining an indexed list:
(0, seq[0]), (1, seq[1]), (2, seq[2]), …

1
2
3
4
5
# 0 a
# 1 b
# 2 c
for index, character in enumerate("abc"):
print(index, character)
1
2
3
4
5
6
7
8
# (0, 'a')
# (1, 'b')
# (2, 'c')
for t in enumerate("abc"):
print(t)
index, character = t
print(index)
print(character)

list methods

sort()

def sort(self: list[SupportsRichComparisonT],
*,
key: None = None,
reverse: bool = False) -> None
Sort the list in ascending order and return None.
The sort is in-place (i.e. the list itself is modified) and stable (i.e. the order of two equal elements is maintained).
If a key function is given, apply it once to each list item and sort them, ascending or descending, according to their function values.
The reverse flag can be set to sort in descending order.
sort(self, *, key=None, reverse=False)

1
2
3
4
5
6
7
8
9
10
11
even = [2, 4, 6, 8]
odd = [1, 3, 5, 7, 9]
even.extend(odd)
print(even) ## [2, 4, 6, 8, 1, 3, 5, 7, 9]

even.sort()
print(even) # [1, 2, 3, 4, 5, 6, 7, 8, 9]
another_even = even
even.sort(reverse=True)
print(even) # [9, 8, 7, 6, 5, 4, 3, 2, 1]
print(another_even) # [9, 8, 7, 6, 5, 4, 3, 2, 1]
buildin function: sorted()

def sorted(__iterable: Iterable[SupportsRichComparisonT],
*,
key: None = None,
reverse: bool = False) -> list[SupportsRichComparisonT]
Return a new list containing all items from the iterable in ascending order.
A custom key function can be supplied to customize the sort order, and the reverse flag can be set to request the result in descending order.
sorted(__iterable, *, key=None, reverse=False)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
pangram = "The quick brown fox jumps over the lazy dog"

# [' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', 'T', 'a', 'b', 'c', 'd', 'e', 'e', 'e', 'f', 'g', 'h', 'h', 'i', 'j', 'k',
# 'l', 'm', 'n', 'o', 'o', 'o', 'o', 'p', 'q', 'r', 'r', 's', 't', 'u', 'u', 'v', 'w', 'x', 'y', 'z']
letters = sorted(pangram)
print(letters)

numbers = [2.3, 4.5, 8.7, 3.1, 9.2, 1.6]
sorted_numbers = sorted(numbers)
print(sorted_numbers) # [1.6, 2.3, 3.1, 4.5, 8.7, 9.2]
print(numbers) # [2.3, 4.5, 8.7, 3.1, 9.2, 1.6]

missing_letter = sorted("The quick brown fox jumps over the lazy dog")
print(missing_letter)
1
2
3
4
# [' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', 'a', 'b', 'c', 'd', 'e', 'e', 'e', 'f', 'g', 'h', 'h', 'i', 'j',
# 'k', 'l', 'm', 'n', 'o', 'o', 'o', 'o', 'p', 'q', 'r', 'r', 's', 'T', 't', 'u', 'u', 'v', 'w', 'x', 'y', 'z']
missing_letter = sorted("The quick brown fox jumps over the lazy dog", key = str.casefold)
print(missing_letter)

tuples

an ordered set of data
immutable

1
2
3
t = "a", 'b', 'c'
t = ("a", 'b', 'c')
print(t) # ('a', 'b', 'c')
1
2
3
4
5
6
7
name = 'Tim'
age = 10

print(name, age, 'python', 2020) # 'Tim 10 python 2020'

# ('Tim', 10, 'python', 2020)
print((name, age, 'python', 2020)) # 'Tim 10 python 2020'
1
2
3
4
5
6
7
8
9
10
11
12
13
14
metallica = "Ride the Lightning", "Meallica", 1984

print(metallica)
print(metallica[0]) # Ride the Lightning
print(metallica[1])
print(metallica[2])

## Tuples don't support item assignment
# metallica[0] = "Master of Puppets"

metallica2 = list(metallica)
print(metallica2) # ['Ride the Lightning', 'Meallica', 1984]
metallica2[0] = "Master of Puppets"
print(metallica2) # ['Master of Puppets', 'Meallica', 1984]

unpacking tuples

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
a = b = c = d = e = f = 12
print(c)

### unpacking
x, y, z = 1, 2, 76
print(x)
print(y)
print(z)

data = 1, 2, 76 # tuple
x, y, z = data ## left side is not a tuple
# "data" not change
print(x)
print(y)
print(z)

data = (12, 13, 14)
(x, y, z) = data
print(x, y, z)

print("unpacking a list")
data_list = [12, 13, 14]
# data_list.append(15) # ValueError: too many values to unpack (expected 3)

p, q, r = data_list
print(p) # 12
print(q)
print(r)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
# 0 a
# 1 b
# 2 c
for index, character in enumerate("abc"):
print(index, character)

# (0, 'a')
# (1, 'b')
# (2, 'c')
for t in enumerate("abc"):
print(t)
index, character = t
print(index)
print(character)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
metallica = "Ride the Lightning", "Meallica", 1984

title, artist, year = metallica
print(title) # Ride the Lightning
print(artist)
print(year)

table = ("Coffe table", 200, 100, 75, 34.50)
print(table[1] * table[2])

name, length, width, height, price = table
print(length * width)

# print(metallica)
# print(metallica[0]) # Ride the Lightning
# print(metallica[1])
# print(metallica[2])
nested tuple in list
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
albums = [("Welcome to my Nightmare", "Alice Cooper", 1975),
("Bad Company", "Bad Company", 1974),
("Nightflight", "Budgie", 1981),
("More Mayhem", "Emilda May", 2011),
("Ride the Lightning", "Meallica", 1984), ]

print(len(albums))

for album in albums:
title, artist, year = album
print(f"Album: {title}, Artist: {artist}, Year: {year}")
print("Album: {0}, Artist: {1}, Year: {2}".format(title, artist, year))

### more efficient
for title, artist, year in albums:
print("Album: {0}, Artist: {1}, Year: {2}".format(title, artist, year))
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
albums = [
("Welcome to my Nightmare", "Alice Cooper", 1975,
[
(1, "Welcome to my Nightmare"),
(2, "Devil's Food"),
(3, "The Black Widow"),
(4, "Some Folks"),
(5, "Only Women Bleed"),
]
),
("Bad Company", "Bad Company", 1974,
[
(1, "Can't Get Enough"),
(2, "Rock Steady"),
(3, "Ready for Love"),
(4, "Don't Let Me Down"),
(5, "Bad Company"),
(6, "The Way I Choose"),
(7, "Movin' On"),
(8, "Seagull"),
]
),
("Nightflight", "Budgie", 1981,
[
(1, "I Turned to Stone"),
(2, "Keeping a Rendezvous"),
(3, "Reaper of the Glory"),
(4, "She Used Me Up"),
]
),
("More Mayhem", "Imelda May", 2011,
[
(1, "Pulling the Rug"),
(2, "Psycho"),
(3, "Mayhem"),
(4, "Kentish Town Waltz"),
]
),
]

for name, artist, year, songs in albums:
print(f"Album: {name}, Artist: {artist}, Year: {year}")
print("Songs: ")
for index, song_name in songs:
print(f"Song {index}: {song_name}")

print(albums[3]) # ('More Mayhem', 'Imelda May', 2011, [(1, 'Pulling the Rug'), (2, 'Psycho'), (3, 'Mayhem'), (4, 'Kentish Town Waltz')])
print(albums[3][3]) # [(1, 'Pulling the Rug'), (2, 'Psycho'), (3, 'Mayhem'), (4, 'Kentish Town Waltz')]
print(albums[3][3][2]) # (3, 'Mayhem')
print(albums[3][3][2][1]) # Mayhem
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
from nested_data import albums

SONGS_LIST_INDEX = 3
SONG_TITLE_INDEX = 1

# print(albums)
while True:
print("Please choose your album (invalid choice exits): ")
for index, (title, artist, year, songs) in enumerate(albums):
print("{}: {}".format(index + 1, title))
# for index, value in enumerate(albums):
# # print(index, value)
# title, artist, year, songs = value
# print("{}, {}, {}, {}, {}"
# .format(index + 1, title, artist, year, songs))
choice = int(input())
if 1 <= choice <= len(albums):
songs_list = albums[choice - 1][SONGS_LIST_INDEX]
else:
break

print("Please choose your song (invalid choice exits): ")
for index, (song_num, song_name) in enumerate(songs_list):
print("{}: {}".format(index + 1, song_name))
song_choice = int(input())
if 1 <= song_choice <= len(songs_list):
song_title = songs_list[song_choice - 1][SONG_TITLE_INDEX]
print(f"Playing {song_title}")
else:
break
print('=' * 40)

tuple() function

section 4: function

1
2
3
4
5
6
7
def multiply():
result = 10.5 * 4
return result


answer = multiply()
print(answer) # 42.0

parameter and argument

formal parameter

arguments are the values that will be used by formal parameters, when call a function.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
def multiply(x, y):
result = x * y
return result


# TypeError: multiply() missing 2 required positional arguments: 'x' and 'y'
# answer = multiply()
answer = multiply(10.5, 4)
print(answer) # 42.0

forty_two = multiply(6, 7)
print(forty_two) # 42

for val in range(1, 5):
two_times = multiply(val, 2)
print(two_times)

parameter types

  • any positional-or-keyword arguments that we define, MUST come first in the parameter list
  • a var_positional parameter(start with *) must come after any positional-or-keyword arguments
  • any parameters defined after a var-positional parameter must be keyword-only arguments(which includes var-keyword arguments)
  • any var-keyword arguments appear last
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
def func(p1, p2, *args, k, **kwargs):
print(f'positional-or-keyword:....{p1}, {p2}')
print(f"var-positional (*args):...{args}")
print(f"keyword:..................{k}")
print(f"var-keyword:..............{kwargs}")

# positional-or-keyword:....1, 2
# var-positional (*args):...(3, 4, 5)
# keyword:..................6
# var-keyword:..............{'key1': 7, 'key2': 8}
func(1, 2, 3, 4, 5, k=6, key1=7, key2=8)
# positional-or-keyword:....1, 2
# var-positional (*args):...(3, 4, 5, 9)
# keyword:..................6
# var-keyword:..............{'key1': 7, 'key2': 8}
func(1, 2, 3, 4, 5, 9, k=6, key1=7, key2=8)

# TypeError: func() missing 1 required keyword-only argument: 'k'
# func(1, 2, 3, 4, 5, 9, key1=7, key2=8)

arguments are passed by assignment

the behaviour is similar to pass by reference, when passing a mutable object.
for immutable objects, the behaviour is closer to pass by value.

in a function call a function

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
def is_palindrome(string):
string = string.casefold()
backwards = string[::-1]
return backwards == string


def is_palindrome_sentence(sentence):
sentence_without = ''
for character in sentence:
if str(character).isalpha():
sentence_without += character
return is_palindrome(sentence_without)


print(is_palindrome_sentence("Was it a car, or a cat, I saw?"))

positional argument

default parameter values

1
2
3
4
5
6
7
8
9
10
11
12
13
14
def banner_text(text, screen_width=80):
if len(text) > screen_width - 4:
# print("The text is too long to fit in the specified width")
raise ValueError("String {0} is larger than specified width {1}"
.format(text, screen_width))
if text == '*':
print('*' * screen_width)
else:
output_string = '**{0}**'.format(text.center(screen_width -4))
print(output_string)


# "** Hello world! **"
banner_text('Hello world!')

Don’t use spaces around the = sign when used to indicate a keyword argument, or when used to indicate a default value for an unannotated function parameter:

  • Correct:

def complex(real, imag=0.0):
return magic(r=real, i=imag)

  • Wrong:

def complex(real, imag = 0.0):
return magic(r = real, i = imag)

*args

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
numbers = (0, 1, 2, 3, 4, 5)

print(numbers) # (0, 1, 2, 3, 4, 5)

## unpack sequence
print(*numbers) # '0 1 2 3 4 5'
print(0, 1, 2, 3, 4, 5) # '0 1 2 3 4 5'

## pack to sequence

def test_star(*args):
print(args)
for x in args:
print(x)


test_star(0, 1, 2, 3, 4, 5) # (0, 1, 2, 3, 4, 5)
test_star() # ()
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
# Some ANSI escape sequences for colours and effects
BLACK = '\u001b[30m'
RED = '\u001b[31m'
GREEN = '\u001b[32m'
YELLOW = '\u001b[33m'
BLUE = '\u001b[34m'
MAGENTA = '\u001b[35m'
CYAN = '\u001b[36m'
WHITE = '\u001b[37m'
RESET = '\u001b[0m'

BOLD = '\u001b[1m'
UNDERLINE = '\u001b[4m'
REVERSE = '\u001b[7m'

# print(RED, "this will be in red")
# print("and so is this")
# print(RESET)
# print("this is normal")


def colour_print(text: str, *effects: str) -> None:
"""
Print `text` using the Ansi sequences to change colour, etc.

:param text: the text to print
:param effects: the effects we want, one of the constants
defined at the start of this module
"""
effects_joined = ''.join(effects)
output_string = "{0}{1}{2}".format(effects_joined, text, RESET)
print(output_string)


colour_print("Hello, Blue", BLUE)
colour_print("Hello, Underline and Red", UNDERLINE, RED)

positional/keyword argument

keyword argument: an argument preceded by an identifier (e.g. name=) in a function call or passed as a value in a dictionary preceded by **. For example, 3 and 5 are both keyword arguments in the following calls to complex():

complex(real=3, imag=5)
complex(**{'real': 3, 'imag': 5})

positional argument: an argument that is not a keyword argument. Positional arguments can appear at the beginning of an argument list and/or be passed as elements of an iterable preceded by *. For example, 3 and 5 are both positional arguments in the following calls:

complex(3, 5)
complex(*(3, 5))

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
def banner_text(text=' ', screen_width=80):
if len(text) > screen_width - 4:
# print("The text is too long to fit in the specified width")
raise ValueError("String {0} is larger than specified width {1}"
.format(text, screen_width))
if text == '*':
print('*' * screen_width)
else:
output_string = '**{0}**'.format(text.center(screen_width -4))
print(output_string)


# "** Hello world! **"
banner_text('Hello world!')

banner_text()

### keyword argument ###
# banner_text(, 60)
banner_text(screen_width=60)

handling invalid argument

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
def banner_text(text):
screen_width = 80
if len(text) > screen_width -4:
# print("The text is too long to fit in the specified width")
raise ValueError("String {0} is larger than specified width {1}"
.format(text, screen_width))
if text == '*':
print('*' * screen_width)
else:
output_string = '**{0}**'.format(text.center(screen_width -4))
print(output_string)


# "** Hello world! **"
banner_text('Hello world!')
banner_text("01234567890123456789012345678901234567890123456789012345678901234567890123456789")
exceptions

https://docs.python.org/3/library/exceptions.html#concrete-exceptions

exception ValueError
Raised when an operation or function receives an argument that has the right type but an inappropriate value, and the situation is not described by a more precise exception such as IndexError.

return statement

1
2
3
4
5
6
7
8
9
10
def get_integer(prompt):
while True:
temp = input(prompt)
if temp.isnumeric():
return int(temp)
# else:
# print("Please enter a number")
print("Please enter a number")

guess = get_integer("Please guess the number: ")

return None

1
2
3
4
5
6
def multiply(x, y):
result = x * y


answer = multiply(10.5, 4)
print(answer) # None

documentation for functions

python pep docstring

pycharm -> ctrl + q

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
def get_integer(prompt):
"""
Get an integer form Standard Input (stdin).

The function will continue looping, and prompting the user,
until a valid `int` is entered

:param prompt: The String that the user will see, when they
are prompted to enter to value
:return: The integer thar the user enters
"""
while True:
temp = input(prompt)
if temp.isnumeric():
return int(temp)
else:
print("Please enter a number")

print(get_integer.__doc__)
help(get_integer)

function annotations and type hints

def fibonacci(n: int) -> int
Return the n th Fibonacci number, for positive n.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
def fibonacci(n: int) -> int:
"""
Return the `n` th Fibonacci number, for positive `n`.
"""
if 0 <= n <= 1:
return n

n_minus1, n_minus2 = 1, 0

result = None
for f in range(n - 1):
result = n_minus2 + n_minus1
n_minus2 = n_minus1
n_minus1 = result

return result
1
2
3
def multiply(x: float, y: float) -> float:
result = x * y
return result

When combining an argument annotation with a default value, however, do use spaces around the = sign:

  • Correct:
    def munge(sep: AnyStr = None): …
    def munge(input: AnyStr, sep: AnyStr = None, limit=1000): …
  • Wrong:
    def munge(input: AnyStr=None): …
    def munge(input: AnyStr, limit = 1000): …
1
2
3
4
5
6
7
8
9
10
def banner_text(text: str = ' ', screen_width: int = 80) -> None:
if len(text) > screen_width - 4:
# print("The text is too long to fit in the specified width")
raise ValueError("String {0} is larger than specified width {1}"
.format(text, screen_width))
if text == '*':
print('*' * screen_width)
else:
output_string = '**{0}**'.format(text.center(screen_width -4))
print(output_string)

section 5: dictionaries and sets

dictionary

a collection of values, that are stored using a key

Mapping Types — dict
A mapping object maps hashable values to arbitrary objects. Mappings are mutable objects. There is currently only one standard mapping type, the dictionary

dictionaries are indexed by keys, which can be any immutable type; strings and numbers can always be keys. Tuples can be used as keys if they contain only strings, numbers, or tuples; if a tuple contains any mutable object either directly or indirectly, it cannot be used as a key. You can’t use lists as keys, since lists can be modified in place using index assignments, slice assignments, or methods like append() and extend().

hashable: an object is hashable if it has a hash value that doesn’t change during its lifetime and can be compared to other objects. In practice, it means that to be hashable objects need methods __hash__() and __eq__() .

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
vehicles = {
'dream': 'Honda 250T',
'roadster': 'BMW R1100',
'er5': 'Kawasaki ER5',
'can-am': 'Bombardier Can-Am 250',
'virago': 'Yamaha XV250',
'tenere': 'Yamaha XT650',
'jmny': 'Suzuki Jimny 1.5',
'fiesta': 'Ford Fiesta Ghia 1.4',
}

my_car = vehicles['fiesta']
print(my_car) # 'Ford Fiesta Ghia 1.4'

computer = vehicles['virago']
# computer = vehicles['Virago'] # KeyError: 'Virago'
print(computer) # 'Yamaha XV250'

# def get(self, __key: _KT) -> _VT | None
# Return the value for key if key is in the dictionary, else default.
learner = vehicles.get('er5') # 'Kawasaki ER5'
learner = vehicles.get('ER5') # None
print(learner)

hash table

hash is one way

alt text

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
data = [
('orange', 'a sweet, orange, citrus fruit'),
('apple', 'good for making cider'),
('lemon', 'a sour, yellow citrus fruit'),
('grape', 'a small, sweet fruit growing in bunches'),
('melon', 'sweet and juicy'),
]
# def ord(__c: str | bytes | bytearray) -> int
# Return the Unicode code point for a one-character string
# print(ord('a')) # 97

def simple_hash(s: str) -> int:
basic_hash = ord(s[0])
return basic_hash % 10

def get(k: str) -> str:
hash_code = simple_hash(k)
if values[hash_code]:
return values[hash_code]
else:
return None

# for key, value in data:
# # print(key, simple_hash((key)))
# h = hash(key)
# print(key, h)

keys = [''] * 10 # ['', '', '', '', '', '', '', '', '', '']
values = keys.copy()

for key, value in data:
h = simple_hash(key)
print(key, h)
keys[h] = key
values[h] = value

print(keys) # ['', 'orange', '', 'grape', '', '', '', 'apple', 'lemon', 'melon']
print(values) # ['', 'a sweet, orange, citrus fruit', '', 'a small, sweet fruit growing in bunches', '', '', '', 'good for making cider', 'a sour, yellow citrus fruit', 'sweet and juicy']

print()

value = get('lemon')
print(value) # a sour, yellow citrus fruit

value = get('tomato')
print(value) # None

### hash value collosion
value = get('banana')
print(value) # a sour, yellow citrus fruit

iterate a dict

when iterate over a dictionary with python 3.7 and above, the keys will appear in the order they were added to the dictionary

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
vehicles = {
'dream': 'Honda 250T',
'roadster': 'BMW R1100',
'er5': 'Kawasaki ER5',
'can-am': 'Bombardier Can-Am 250',
'virago': 'Yamaha XV250',
'tenere': 'Yamaha XT650',
'jmny': 'Suzuki Jimny 1.5',
'fiesta': 'Ford Fiesta Ghia 1.4',
}

# for key in vehicles:
# print(key, vehicles[key], sep=', ')

## more efficient
# def items(self) -> dict_items[_KT, _VT]
# D.items() -> a set-like object providing a view on D's items
for key, value in vehicles.items():
print(key, value, sep=', ')

add item

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
vehicles = {
'dream': 'Honda 250T',
'roadster': 'BMW R1100',


vehicles['starfighter'] = 'Lockheed F-104'
vehicles['learjet'] = 'Bombardier Learjet 75'


# dream, Honda 250T
# roadster, BMW R1100
# starfighter, Lockheed F-104
# learjet, Bombardier Learjet 75
for key, value in vehicles.items():
print(key, value, sep=', ')

changing value in a dict

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
vehicles = {
'dream': 'Honda 250T',
'roadster': 'BMW R1100', ### duplicated
'er5': 'Kawasaki ER5',
'can-am': 'Bombardier Can-Am 250',
'virago': 'Yamaha XV250',

'roadster': 'Triumph Street Triple',
}



vehicles['virago'] = 'Yamaha XV535'

# dream, Honda 250T
# roadster, Triumph Street Triple !!!! duplicated
# er5, Kawasaki ER5
# can-am, Bombardier Can-Am 250
# virago, Yamaha XV535 !!!! changed
for key, value in vehicles.items():
print(key, value, sep=', ')

delete item from a dict

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
vehicles = {
'dream': 'Honda 250T',
'er5': 'Kawasaki ER5',
'can-am': 'Bombardier Can-Am 250',
'virago': 'Yamaha XV250',

}



del vehicles['virago']

# dream, Honda 250T
# er5, Kawasaki ER5
# can-am, Bombardier Can-Am 250
for key, value in vehicles.items():
print(key, value, sep=', ')

# KeyError: 'notexist'
# del vehicles['notexist']

# vehicles.pop('f1') # KeyError: 'f1'
# D.pop(k[,d]) -> v, remove specified key and return the corresponding value.
# If the key is not found, return the default if given; otherwise, raise a KeyError.
result = vehicles.pop('f1', None)
print(result)

use in to a dict

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
available_parts = {
'1': 'computer',
'2': 'monitor',
'3': 'keyboad',
'4': 'mouse',
'5': 'hdmi cable',
'6': 'dvd drive',
}

current_choice = None
while current_choice != '0':
if current_choice in available_parts:
# if current_choice in available_parts.keys():
chosen_part = available_parts[current_choice]
print(f"Adding {chosen_part}")
else:
print("Please add options from the list")
for index, part in available_parts.items():
print(index, part, sep=': ')
print("0: to finish")

current_choice = input("> ")

setdefault()

1
2
3
4
5
6
def add_shopping_item(data: dict, item: str, amount: int) -> None:
# if item in dict:
# dict[item] += amount
# else:
# dict[item] = amount
data[item] = data.setdefault(item, 0) + amount
1
2
3
4
5
6
7
8
9
10
11
12
13
14
pantry = {
'chicken': 500,
}

chicken_quantity = pantry.setdefault('chicken', 0)
print(f"chicken: {chicken_quantity}") # chicken: 500
print(pantry) # {'chicken': 500}

beans_quantity = pantry.setdefault('beans', 0)
print(f"chicken: {beans_quantity}") # chicken: 0
print(pantry) # {'chicken': 500, 'beans': 0}

z_quantity = pantry.setdefault('zucchini', 'eight')
print(f"zucchini: {z_quantity}") # zucchini: eight

enumerate(dict)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
recipes = {
'Egg sandwich': [
'egg',
'bread',
'butter',
],
'Beans on toast': [
'beans',
'bread',
],
'Span a la tin': [
'spam',
'tin opener',
'spoon',
],
}

# 0 Egg sandwich
# 1 Beans on toast
# 2 Span a la tin
for index, key in enumerate(recipes):
print(index, key)

dict example

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
pantry = {
"chicken": 500,
"lemon": 2,
"cumin": 24,
"paprika": 18,
"chilli powder": 7,
"yogurt": 300,
"oil": 450,
"onion": 5,
"garlic": 9,
"ginger": 2,
"tomato puree": 125,
"almonds": 75,
"rice": 500,
"coriander": 20,
"lime": 3,
"pepper": 8,
"egg": 6,
"pizza": 2,
"spam": 1,
}

recipes = {
"Butter chicken": {
'chicken': 1,
'lemon': 1,
'cumin': 1,
'paprika': 1,
'chilli powder': 1,
'yogurt': 1,
'oil': 1,
'garlic': 1,
'ginger': 1,
'tomato puree': 1,
'almonds': 1,
'rice': 1,
'coriander': 1,
'lime': 1,
},
'Chicken and chips': {
'chicken': 100,
'potatoes': 3,
'salt': 1,
'malt vinegar': 5,
},
'Pizza': {
"pizza": 1,
},
'Egg sandwich': {
'egg': 2,
'bread': 80,
'butter': 10,
},
'Beans on toast': {
'beans': 1,
'bread': 40,
},
'Span a la tin': {
'spam': 1,
'tin opener': 1,
'spoon': 1,
},
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53

from contents import pantry, recipes

print(pantry, recipes)


def add_shopping_item(data: dict, item: str, amount: int) -> None:
# if item in dict:
# dict[item] += amount
# else:
# dict[item] = amount
data[item] = data.setdefault(item, 0) + amount

display_dict = {}
for index, key in enumerate(recipes):
print(index, key)
display_dict[str(index + 1)] = key
print(display_dict)

items_to_buy = {}

while True:
print(f"Please choose your recipe")
for key, value in display_dict.items():
print(f"{key} - {value}")

choice = input(": ")
if choice == '0':
break
elif choice in display_dict:
select_item = display_dict[choice]
print(f"You have selected {select_item}")

print("checking ingredients...")
ingredients = recipes[select_item]
# print(ingredients)

for ingredient, required_quantity in ingredients.items():
quantity_in_pantry = pantry.get(ingredient, 0)
if quantity_in_pantry >= required_quantity:
print(f"\t{ingredient} OK")
pantry[ingredient] -= required_quantity
else:
quantity_to_buy = required_quantity - quantity_in_pantry
print(f"\tYou need to buy {quantity_to_buy} of {ingredient}")
add_shopping_item(items_to_buy, ingredient, quantity_to_buy)
if ingredient in pantry:
pantry[ingredient] -= 0

for key, value in pantry.items():
print(f"there is {key} of {value}")
for key, value in items_to_buy.items():
print(f"buy {key} of {value}")

dict methods

def fromkeys(cls,
__iterable: Iterable[_T],
__value: None = None) -> dict[_T, Any | None]
Create a new dictionary with keys from iterable and values set to value.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
d = {
0: 'zero',
1: 'one',
2: 'two',
3: 'three',
4: 'four',
5: 'five',
6: 'six',
7: 'seven',
8: 'eight',
9: 'nine',
}

pantry_items = ['chicken', 'spam', 'egg', 'bread', 'lemon']

new_dict = dict.fromkeys(pantry_items)
# {'chicken': None, 'spam': None, 'egg': None, 'bread': None, 'lemon': None}
print(new_dict)
new_dict = dict.fromkeys(pantry_items, 0)
# {'chicken': 0, 'spam': 0, 'egg': 0, 'bread': 0, 'lemon': 0}
print(new_dict)

keys = d.keys()
print(keys, type(keys)) # dict_keys([0, 1, 2, 3, 4, 5, 6, 7, 8, 9]) <class 'dict_keys'>
# for item in d:
for item in d.keys():
print(item)
update()

update([other])
Update the dictionary with the key/value pairs from other, overwriting existing keys. Return None.

update() accepts either another dictionary object or an iterable of key/value pairs (as tuples or other iterables of length two). If keyword arguments are specified, the dictionary is then updated with those key/value pairs: d.update(red=1, blue=2).

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
d = {
0: 'zero',
1: 'one',
2: 'two',
3: 'three',
4: 'four',
5: 'five',
6: 'six',
7: 'seven',
8: 'eight',
9: 'nine',
}

pantry_items = ['chicken', 'spam', 'egg', 'bread', 'lemon']

d2 = {
7: 'lucky seven',
10: 'tem',
3: 'this is the new three',
}

d.update(d2)

# 0 zero
# 1 one
# 2 two
# 3 this is the new three ## changed
# 4 four
# 5 five
# 6 six
# 7 lucky seven ## changed
# 8 eight
# 9 nine
# 10 tem ## new
for key, value in d.items():
print(key, value)

print()


# 0 chicken ## changed
# 1 spam ## changed
# 2 egg ## changed
# 3 bread ## changed
# 4 lemon ## changed
# 5 five
# 6 six
# 7 lucky seven
# 8 eight
# 9 nine
# 10 tem
d.update(enumerate(pantry_items))
for key, value in d.items():
print(key, value)
values() keys() items()

Dictionary view objects
The objects returned by dict.keys(), dict.values() and dict.items() are view objects. They provide a dynamic view on the dictionary’s entries, which means that when the dictionary changes, the view reflects these changes.

Dictionary views can be iterated over to yield their respective data, and support membership tests:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
d = {
0: 'zero',
1: 'one',
2: 'two',
3: 'three',
4: 'four',
5: 'five',
6: 'six',
7: 'seven',
8: 'eight',
9: 'nine',
}

pantry_items = ['chicken', 'spam', 'egg', 'bread', 'lemon']

v = d.values()
print(v, type(v)) # dict_values(['zero', 'one', 'two', 'three', 'four', 'five', 'six', 'seven', 'eight', 'nine']) <class 'dict_values'>
d[10] = 'ten' # dict_values(['zero', 'one', 'two', 'three', 'four', 'five', 'six', 'seven', 'eight', 'nine', 'ten'])
print(v)

print("four" in v) # True
print("elven" in v) # False


## not efficient
keys = list(d.keys())
values = list(d.values())
if "four" in values:
index = values.index("four")
key = keys[index]
print(f"{d[key]} was found with the key {key}")

print()


for key, value in d.items():
if value == "four":
print(f"{d[key]} was found with the key {key}")
shallow copy copy()
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
# animals = {
# 'lion': 'scary',
# 'elephant': 'big',
# 'teddy': 'cuddly',
# }
#
# # things = animals
# # animals['teddy'] = 'toy'
# # print(things['teddy']) # toy
#
# things = animals.copy()
# animals['teddy'] = 'toy'
# print(things['teddy']) # cuddly
# print(animals['teddy']) # toy

animals = {
'lion': ['scary', 'big', 'cat'],
'elephant': ['big', 'grey', 'wrinkled'],
'teddy': ['cuddly', 'stuffed'],
}

things = animals.copy()
print(things['teddy']) # ['cuddly', 'stuffed']
print(animals['teddy']) # ['cuddly', 'stuffed']

things['teddy'].append('toy')
print(things['teddy']) # ['cuddly', 'stuffed', 'toy']
print(animals['teddy']) # ['cuddly', 'stuffed', 'toy']
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
lion_list = ['scary', 'big', 'cat']
teddy_list = ['cuddly', 'stuffed']
elephant_list = ['big', 'grey', 'wrinkled']
animals = {
'lion': lion_list,
'elephant': elephant_list,
'teddy': teddy_list,
}

things = animals.copy()
# things = {
# 'lion': lion_list,
# 'elephant': elephant_list,
# 'teddy': teddy_list,
# }

print(things['teddy']) # ['cuddly', 'stuffed']
print(animals['teddy']) # ['cuddly', 'stuffed']

things['teddy'].append('toy')
# teddy_list.append('toy')
print(things['teddy']) # ['cuddly', 'stuffed', 'toy']
print(animals['teddy']) # ['cuddly', 'stuffed', 'toy']

animals['teddy'].append('added via `animals`')
things['teddy'].append('added via `things`')
print(things['teddy']) # ['cuddly', 'stuffed', 'toy', 'added via `animals`', 'added via `things`']
print(animals['teddy']) # ['cuddly', 'stuffed', 'toy', 'added via `animals`', 'added via `things`']

alt text

deepcopy

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
import copy

animals = {
'lion': ['scary', 'big', 'cat'],
'elephant': ['big', 'grey', 'wrinkled'],
'teddy': ['cuddly', 'stuffed'],
}

# ## shllow copy
# things = animals.copy()
#
# print(things['teddy'], id(things['teddy'])) # ['cuddly', 'stuffed'] 3016496911360
# print(animals['teddy'], id(things['teddy'])) # ['cuddly', 'stuffed'] 3016496911360
#
# things['teddy'].append('toy')
# print(things['teddy']) # ['cuddly', 'stuffed', 'toy']
# print(animals['teddy']) # ['cuddly', 'stuffed', 'toy']

## deep copy
things = copy.deepcopy(animals)


print(things['teddy'], id(things['teddy'])) # ['cuddly', 'stuffed'] 2759661916928
print(animals['teddy'], id(animals['teddy'])) # ['cuddly', 'stuffed'] 2759661917184

things['teddy'].append('toy')
print(things['teddy']) # ['cuddly', 'stuffed', 'toy']
print(animals['teddy']) # ['cuddly', 'stuffed']
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
import contents

### two level
def my_deepcopy(d: dict) -> dict:
copy_d = {}
for key, value in d.items():
# if type(value) == dict:
# copy_d[key] = {}
# for k, v in value.items():
# copy_d[key][k] = v
# elif type(value) == list:
# copy_d[key] = []
# for item in value:
# copy_d[key].append(item)
# else:
# copy_d[key] = value
new_value = value.copy()
copy_d[key] = new_value
return copy_d

recipes_copy = my_deepcopy(contents.recipes)
recipes_copy['Butter chicken']['ginger'] = 300
print(recipes_copy['Butter chicken']['ginger'])
print(contents.recipes['Butter chicken']['ginger'])

set

https://docs.python.org/3/library/stdtypes.html#set

an unordered collection of things. there is no wary to retrieve a specific titme from a set

item must be hashble

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
farm_animals = {'cow', 'sheep', 'hen', 'goat', 'horse'}
print(farm_animals) # {'horse', 'sheep', 'goat', 'hen', 'cow'}
print(type(farm_animals)) # <class 'set'>

#

# sheep
# hen
# cow
# goat
# horse
for animal in farm_animals:
print(animal)

# goat
# cow
# sheep
# hen
# horse
for animal in farm_animals:
print(animal)

# print('Indexing a set is not possible')
## Class 'set' does not define '__getitem__', so the '[]' operator cannot be used on its instances
# goat = farm_animals[3] # TypeError: 'set' object is not subscriptable
# print(goat)


more_animals = {'sheep', 'goat', 'cow', 'horse', 'hen'}

# The sets are equal
if more_animals == farm_animals:
print('The sets are equal')

set(), test for membership ‘in’

set(iterable) -> new set object
Build an unordered collection of unique elements.

set(“12345”)
set(range(1, 5, 2))

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
options = ['1. Learn Python', '2. Learn Java', '3. Go swimming', '4. Have dinner', '5. Go to bed', '0. Exit']

choose = '-'
while choose != '0':

# choose = int(input())
#
# if 1 <= choose <= 5:
# print(options[choose - 1])

# if choose in '12345': ## have bug if '123'
# if choose in list('12345'):
## set more effecient
# print(set("12345")) # {'4', '2', '5', '1', '3'}
# if choose in set('12345'):
if choose in {'4', '2', '5', '1', '3'}:
print(options[int(choose) - 1])
else:
print("Please choose your option from the list below:")
for option in options:
print(option)
choose = input()
else:
print("Game over")

modify set

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
numbers = {}


print(type(numbers)) # <class 'dict'>
# numbers = {*''}
# print(numbers, type(numbers)) # set() <class 'set'>

numbers = set()
numbers.add(1)
print(numbers) # {1}

while len(numbers) < 4:
next_value = int(input("Please enter your next value: "))
numbers.add(next_value)
print(numbers
remove items
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
small_ints = set(range(21))

print(small_ints) # {0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20}

# small_ints.clear()
# print(small_ints) # set()


# def remove(self, __element: _T) -> None
# Remove an element from a set; it must be a member.
# If the element is not a member, raise a KeyError
small_ints.remove(10)
small_ints.discard(11)
print(small_ints) # {0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 12, 13, 14, 15, 16, 17, 18, 19, 20}


# def discard(self, __element: _T) -> None
# Remove an element from a set if it is a member.
# Unlike set.remove(), the discard() method does not raise an exception when an element is missing from the set.
small_ints.discard(99)
print(small_ints)

# small_ints.remove(98) # KeyError: 98
# print(small_ints)
discard()
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
travel_mode = {'1': 'car', '2': 'plane'}

items = {
'can openner',
'scissors',
'razor',
'knife',
'razor blades',
'mug',
'tent',
'stove',
'socks (3 pairs)',
'stove',
}
restricted_items = {
'catapult',
'fuel',
'gun',
'knife',
'razor blades',
'scissors',
'shampoo',
}

print("Please choose your mode of travel: ")
for key, value in travel_mode.items():
print(f"{key}: {value}")

mode = "-"
while mode not in travel_mode:
mode = input("> ")

if mode == '2':
for restricted_item in restricted_items:
items.discard(restricted_item)

print("You need to pack:")
for item in sorted(items):
print(item)
remove()
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
from prescription_data import *

# trial_patients = ['Denise', 'Eddie', 'Frank', 'Georgia']
trial_patients = ['Denise', 'Eddie', 'Frank', 'Georgia', 'Kenny']

for patient in trial_patients:
prescription = patients[patient]
if warfarin in prescription:
prescription.remove(warfarin)
prescription.add(edoxaban)
else:
print('dont have warfarin')
prescription.add(edoxaban)


## more efficient
for patient in trial_patients:
prescription = patients[patient]
try:
prescription.remove(warfarin)
prescription.add(edoxaban)
except KeyError:
print('dont have warfarin')
prescription.add(edoxaban)

union, intersection, difference(substract), symmetric differect

subset

using set to remove duplicate values: converting a list to a set

1
2
3
4
5
6
7
8
9
10
11
12
13
data = ['blue', 'red', 'blue', 'green', 'red', 'blue', 'white']

unique_data = sorted(set(data))
print(unique_data) # ['blue', 'green', 'red', 'white']

## create a list of unique colours, keeping the order they appear
# def fromkeys(cls,
# __iterable: Iterable[_T],
# __value: None = None) -> dict[_T, Any | None]
# Create a new dictionary with keys from iterable and values set to value
# print(dict.fromkeys(data)) # {'blue': None, 'red': None, 'green': None, 'white': None}
unique_data = list(dict.fromkeys(data)) # ['blue', 'red', 'green', 'white']
print(unique_data)

list comprehension

1
2
3
4
5
6
7
8
9
10
11
12
13
14
numbers = "12,165,425,365,254"
numbers_list = numbers.split(',')
print(numbers_list) # ['12', '165', '425', '365', '254']

## create a new list
numbers_int = []
for number in numbers_list:
numbers_int.append(int(number))
print(numbers_int)

# replace the values in place
for index in range(len(numbers_list)):
numbers_list[index] = int(numbers_list[index])
print(numbers_list)