0% found this document useful (0 votes)
4 views

2. Pythonics I

Uploaded by

cheukyinchanvic
Copyright
© © All Rights Reserved
Available Formats
Download as PDF, TXT or read online on Scribd
0% found this document useful (0 votes)
4 views

2. Pythonics I

Uploaded by

cheukyinchanvic
Copyright
© © All Rights Reserved
Available Formats
Download as PDF, TXT or read online on Scribd
You are on page 1/ 144

Lecture 2 - Pythonics I

ADVIST

by
Dmitrii Nechaev & Dr. Lothar Richter

28.10.22
Recap

2/136
Recap

Python is a strongly typed dynamic language.

3/136
Recap

Python is a strongly typed dynamic language.


1 my_variable = 1
2 my_variable = 'one'
3 print(my_variable)

one
1 my_variable = 1 + 'one'

TypeError: unsupported operand type(s) for +: 'int' and 'str'

3/136
Recap: Naming scheme

4/136
Recap: Naming scheme

valid characters include letters, digits, and underscore sign


a name can’t start with a digit
use lowercase for the variable and function names, with words separated by
underscores (do_it_like_this_123)
class names should follow upper camel case convention
(WriteYourClassNamesLikeThis)

4/136
Recap: Data Types

5/136
Recap: Data Types

5/136
Recap: Control Flow

6/136
Recap: Control Flow

6/136
Recap: Functions

7/136
Recap: Functions

a function is a named block of code


a function can have parameters (accept arguments)
a function can have a return value

7/136
Recap: Classes

8/136
Recap: Classes

use the class mechanism to create new data types


combine state and behaviour
create subclasses (inheritance)
override methods

8/136
Recap: Modules

9/136
Recap: Modules

a file containing python code and having .py extension


code persistency (duh)
library organisation
naming conflicts (namespaces)
import mechanism

9/136
Recap: Packages

10/136
Recap: Packages

namespace packages (directories containing modules)


packages (__init__.py file contains initialization logic)

10/136
Going Deeper

11/136
Today
everything is an object
exceptions
more on assignments
more on functions
more on numbers
more on lists
more on tuples
more on dicts
more on sets
more on strings
more containers via collections

12/136
Everything is an Object
We start by saying that everything is an object in python.
1 def empty_func():
2 '''This is a doc string.'''
3 pass
4 empty_func.__doc__

'This is a doc string.'


1 empty_func.__name__

'empty_func'
1 (1).__add__(2)

13/136
Exceptions

14/136
Exceptions

An exception occurs when syntactically correct code results in an error:


1 1 / 0

ZeroDivisionError: division by zero

15/136
Throwing Exceptions

Many things can go wrong. We can try to divide by zero, or try to open a file that doesn’t
exist, or try to pop from an empty stack. In such cases Python program throws an
exception and immediately terminates. We can manually throw an exception via
raise:
1 raise ValueError('We raised this exception ourselves!')

ValueError: We raised this exception ourselves!

16/136
Catching Exceptions

To catch an exception, use try...except:


1 try:
2 1 / 0
3 except:
4 print('Oopsie')

Oopsie
That said, NEVER use an except without a specific exception you want to catch!

17/136
Catching Exceptions
Python comes with many built‑in exception classes. Right now, let’s catch only
ZeroDivisionError:
1 try:
2 1 / 0
3 except ZeroDivisionError as error:
4 print(error)
5 print(type(error))

division by zero
<class 'ZeroDivisionError'>

18/136
Catching Exceptions
We can have several except clauses:
1 def divide(a, b):
2 try:
3 return a / b
4 except ZeroDivisionError:
5 print(”Can't divide by zero.”)
6 except TypeError:
7 print(”That's not even a number.”)
8

9 divide(10, 'Hello, World!')

That's not even a number.

19/136
Catching Exceptions
We can catch different exceptions with one except clause:
1 def divide(a, b):
2 try:
3 return a / b
4 except (
5 ZeroDivisionError, TypeError
6 ) as error:
7 print('Seriously?', error, type(error))
8

9 divide(10, 0)

Seriously? division by zero <class 'ZeroDivisionError'>

20/136
Catching Exceptions
We can also specify a block of code that should only be run when no exceptions have
occurred:
1 def divide(a, b):
2 result = 0
3 try:
4 result = a / b
5 except ZeroDivisionError:
6 print('Seriously?')
7 else:
8 print('Yaaaaay!')
9 return result
10
11 divide(10, 2)

Yaaaaay!

5.0

21/136
Catching Exceptions

Why use else instead of putting additional code into the try clause? Because that
improves readability (we need to investigate fewer lines of code wrapped in the try
clause) and prevents accidentally catching an exception that is caused by the additional
code.

22/136
Catching Exceptions

Finally, we can add a block of code that will always be executed as the last task before
the try statement completes via the finally clause.

23/136
Catching Exceptions
1 def sqd(a, b): 1 def sqd_with_finally(a, b):
2 result = 0 2 result = 0
3 try: 3 try:
4 result = a / b 4 result = a / b
5 except ZeroDivisionError as error: 5 except ZeroDivisionError as error:
6 print(error) 6 print(error)
7 else: 7 else:
8 result **= 2 8 result **= 2
9 return result 9 return result
10 print('I always get executed.') 10 finally:
11 print('I always get executed.')
1 sqd(10, 2)
1 sqd_with_finally(10, 2)
25.0
1 sqd(10, '2') I always get executed.

TypeError: unsupported operand type(s) for /: 'int' and 'str' 25.0


1 sqd_with_finally(10, '2')
I always get executed.
TypeError: unsupported operand type(s) for /: 'int' and 'str'

24/136
Exceptions: Overview

throw an exception via raise


catch an exception via try...except
there can be several except clauses
several exceptions can be caught in one except clause
specify code that should only be run when no exceptions have occured via
with else
specify code that will always be executed with finally

25/136
Assignments

26/136
Multiple Assignments
We can assign several values at once:
1 x, y = 10, 20
2 x, y

(10, 20)
We can swap values without using a temporary variable:
1 x, y = y, x
2 x, y

(20, 10)

27/136
Multiple Assignments
We can use multiple assignments with sequences:
1 my_string = 'Hello, world!'
2 a, b, c, d, e, f, g, h, i, j, k, l, m = my_string
3 a

'H'
1 my_range = range(1, 6)
2 a, b, c, d, e = my_range
3 b

28/136
Multiple Assignments
We can use multiple assignments with sequences:
1 my_list = [10, 20, 30, 40, 50]
2 a, b, c, d, e = my_list
3 c

30
1 my_tuple = (100, 200, 300, 400, 500)
2 a, b, c, d, e = my_tuple
3 d

400

29/136
Multiple Assignments
We can use multiple assignments with sets and dicts too:
1 my_dict = {'v': 11, 'w': 12, 'x': 13, 'y': 14, 'z': 15}
2 a, b, c, d, e = my_dict
3 e

'z'
1 my_set = {5, 4, 3, 2, 1}
2 a, b, c, d, e = my_set
3 a, b, c, d, e

(1, 2, 3, 4, 5)

30/136
Multiple Assignments

We have to use a proper number of variables:


1 my_list = [1, 2, 3, 4, 5]
2 a, b, c, d = my_list

ValueError: too many values to unpack (expected 4)

31/136
Multiple Assignments

It is customary to assign the values we don’t need to an underscore variable:


1 my_list

[1, 2, 3, 4, 5]
1 a, b, c, _, _ = my_list
2 c

32/136
Multiple Assignments

We can assign several values to one variable using the *‑operator:


1 my_list = [1, 2, 3, 4, 5]
2 a, *b = my_list
3 a

1
1 b

[2, 3, 4, 5]

33/136
Augmented Assignments

Augmented assignments give us a compact way to change a value bound to a variable:

1 x = 1 1 x **= 4
2 x += 1 2 x
3 x 1296
2 1 x //= 6
1 x *= 3 2 x
2 x 216
6

34/136
Augmented assignments
Of course, we can use augmented assignments with more than just numbers:
1 my_set = {1, 2, 3, 4, 5, 6, 7}
2 my_set &= {4, 5, 6, 7, 8, 9, 10}
3 my_set

{4, 5, 6, 7}
1 my_dict = {'a': 1, 'b': 2}
2 my_dict |= {'a': 3, 'c': 4}
3 my_dict

{'a': 3, 'b': 2, 'c': 4}

35/136
Ternary Operator
Very often we want to assign a value from a specific set of options to a variable A based
on the value of a variable B. One way to do it is simply by using branching:
1 raining = True
2 if raining:
3 mushroom_growth = 'Good'
4 else:
5 mushroom_growth = 'Bad'
6 mushroom_growth

'Good'

36/136
Ternary Operator

Conditional expression allows us to achieve the same result with a one‑liner:


1 raining = True
2 mushroom_growth = 'Good' if raining else 'Bad'
3 mushroom_growth

'Good'

37/136
Assignments: Overview

multiple (tuple, unpacking, iterable unpacking, …) assignments;


augmented assignments;
assignments with ternary operator (also known as conditional expression).

38/136
Functions

39/136
More on Functions
We’ve already seen that functions can accept multiple arguments and can have a return
value:
1 def my_pow(x, power):
2 return x ** power
3

4 cube_of_four = my_pow(4, 3)
5 cube_of_four

64
Now, we will cover default parameter values, keyword arguments, and arbitrary number
of arguments.

40/136
Default Parameter Values

We can specify default values for parameters:


1 def my_pow(x, power=2):
2 return x ** power
3 my_pow(10)

100
If we omit the argument for a parameter with a default value as we invoke the function,
the default value is automatically used.

41/136
Default Parameter Values

Keep in mind that parameters with default values have to appear in the parameter list
after the parameters that don’t have defaults:
1 def my_pow(x=10, power):
2 return x ** power
3 my_pow(2)

SyntaxError: non-default argument follows default argument (3227177810.py, line 1)

42/136
Keyword Arguments

Keyword arguments allow us to pass arguments in any order when calling a function:
1 def my_pow(x, power=2):
2 return x ** power
3 my_pow(power=3, x=10)

1000

43/136
Keyword Arguments

Keyword arguments must be placed after positional arguments at function call.


1 def my_date(year, month, day):
2 return str(year) + '.' + str(month) + '.' + str(day)

1 my_date(day=31, month=12, 2021)

SyntaxError: positional argument follows keyword argument (2194942446.py, line 1)

44/136
Keyword Arguments

What if we keep the arguments in the same order as function parameters?


1 my_date(year=2021, month=12, 31)

SyntaxError: positional argument follows keyword argument (1135159070.py, line 1)

Still doesn’t work!

45/136
Keyword Arguments

Keeping positional arguments in front of the keyword arguments works.


1 my_date(2021, day=31, month=12)

'2021.12.31'

46/136
Arbitrary # of Arguments
The *‑operator allows us to create functions that accept arbitrary number of arguments:
1 def average(*args):
2 return sum(args) / len(args)
3

4 average(4, 5, 6, 7, 8)

6.0
This function has only one parameter ‑ args (this name is used by convention). Its value
is a tuple with positional arguments.

47/136
Mixing It Up
We can combine the *‑operator with keyword arguments:
1 def sum_of_powers(*args, power=1):
2 return sum([x ** power for x in args])
3

4 sum_of_powers(1, 2, 3, 4, power=2)

30
1 sum_of_powers(5, 10)

15
Again, the keyword arguments have to follow the positional arguments.

48/136
Unpacking Arguments

We can unpack collections and pass their values as arguments to a function:


1 my_values = [2021, 12, 31]
2

3 def my_date(year, month, day):


4 return str(year) + '.' + str(month) + '.' + str(day)
5

6 my_date(*my_values)

'2021.12.31'

49/136
Unpacking Keyword Arguments

We can do the same with dictionaries and keyword arguments:


1 my_values = {
2 'day': 31,
3 'month': 12,
4 'year': 2021
5 }
6 my_date(**my_values)

'2021.12.31'

50/136
Functions: Overview

default parameter values


keyword arguments
arbitrary number of arguments
unpacking arguments
unpacking keyword arguments

51/136
Functions are Objects

Functions are objects, and we can pass them into other functions as arguments. When
would we want to use that? Consider the following examples.

52/136
Functions as Objects

We have a list of strings and want to sort them alphabetically. The sorted function
allows us to do exactly that (keep in mind that it doesn’t sort in place, it returns a new
sorted list):
1 strings = ['Bob', 'Charles', 'Alice']
2 sorted(strings)

['Alice', 'Bob', 'Charles']


That was easy.

53/136
Functions as Objects

We have a list of strings and want to sort them by length. The sorted function accepts
a keyword argument key=func, where func is a function of one argument that
creates a comparison key for sorting:
1 strings = ['Bob', 'Charles', 'Alice']
2 sorted(strings, key=len)

['Bob', 'Alice', 'Charles']


That wasn’t difficult either.

54/136
Functions as Objects
We have a list of strings and want to sort them by their second character. We need a
function that returns a second character of a given string to use as a key:
1 strings = ['Bob', 'Charles', 'Alice']
2

3 def second_char(string):
4 return string[1]
5

6 sorted(strings, key=second_char)

['Charles', 'Alice', 'Bob']


Things are becoming a bit more complicated. We have to define a function just to
perform the sorting we want. Thankfully, there is a better way.

55/136
Lambdas

Lambdas allow us to create a function inline (in place) where it’s needed:
1 sorted(strings, key=lambda x: x[1])

['Charles', 'Alice', 'Bob']


A lambda expression creates an anonymous function. The keyword lambda is followed
by a comma‑separated list of parameters, a colon :, and an expression (an expression
can’t have branches or loops, return or yield statements). A lambda implicitly returns
the value of the expression.

56/136
Numbers

57/136
More on Numbers

We have mentioned integers and floating point values during the first lecture. We have
also looked into comparisons and basic arithmetic operations. Let’s cover additional
numeric data types and additional mathematical operations.

58/136
Complex Numbers

We can create complex numbers using either a literal notation or a complex


constructor:

1 x = 1+2j 1 y = complex(3, -5)


2 x.real 2 y.real
1.0 3.0
1 x.imag 1 y.imag
2.0 -5.0

59/136
Decimals
We have mentioned that floats have limited precision:
1 1.10 + 2.20

3.3000000000000003
Decimals, on the other hand, provide fast exact arithmetic:
1 from decimal import getcontext, Decimal
2 getcontext().prec = 7
3 dec_1 = Decimal(1.10)
4 dec_2 = Decimal(2.20)
5 print(dec_1 + dec_2)

3.300000

60/136
Decimals

Be careful with precision!


1 getcontext().prec = 3
2 dec_1 = Decimal(9.99)
3 dec_2 = Decimal(8.88)
4 print(dec_1 + dec_2)

18.9
Refer to the official documentation for more!

61/136
Fractions
The fractions module provides support for rational number arithmetic. We can create
fractions from integers, floats, decimals, and strings:
1 from fractions import Fraction
2 Fraction(1, 2)

Fraction(1, 2)
1 Fraction(1.5)

Fraction(3, 2)
1 Fraction('3/7')

Fraction(3, 7)

62/136
math module
The math module provides additional mathematical functions:
1 import math
2 math.ceil(5.6)

6
1 math.floor(5.6)

5
1 math.sqrt(81)

9.0

63/136
math module
Would you like to do some combinatorics?
1 math.factorial(6)

720
1 math.comb(10, 3)

120
1 math.perm(10, 3)

720

64/136
math module

How about some trigonometrics and angular conversions?


1 math.cos(math.pi)

-1.0
1 math.degrees(math.pi)

180.0

65/136
statistics module
The statistics module provides functions for calculating mathematical statistics of
numeric data:
1 import statistics
2 numbers = [5, 7, 5, 11, 17]
3 statistics.mean(numbers)

9
1 statistics.mode(numbers)

5
1 statistics.median(numbers)

66/136
random module
The random module implements pseudo‑random number generators:
1 import random
2 random.seed(727)
3 random.randint(1, 5)

5
1 random.randrange(1, 9, 2)

3
1 random.choice('Hello, world!')

'o'

67/136
Numbers: Overview

complex numbers
decimals
fractions
math module
statistics module
random module

68/136
Lists

69/136
More on Lists
We have already mentioned creating lists, concatenating them, and using the append
and extend methods:
1 list_1 = [1, 2]
2 list_2 = [3, 4]
3 list_3 = [5, 6]
4 big_list = list_1 + list_2
5 big_list.extend(list_3)
6 big_list.append(7)
7 big_list

[1, 2, 3, 4, 5, 6, 7]
Let us take a closer look at the list methods now.

70/136
list.clear

We can remove all items from a list via the clear method:
1 big_list

[1, 2, 3, 4, 5, 6, 7]
1 big_list.clear()
2 big_list

[]

71/136
list.copy
The copy method allows us to create a copy of a list:
1 list_1 = [1, 2, 3]
2 list_2 = list_1.copy()
3 list_1[2] += 1
4 list_1

[1, 2, 4]
1 list_2

[1, 2, 3]

72/136
list.copy
However, it creates a shallow copy, so be carefult:
1 list_of_mutables_1 = [
2 [1, 2],
3 [3, 4],
4 [5, 6]
5 ]
6 list_of_mutables_2 = list_of_mutables_1.copy()
7 list_of_mutables_1[1][0] *= 10
8 list_of_mutables_2

[[1, 2], [30, 4], [5, 6]]

73/136
Shallow Copy - Immutables
Let’s take a closer look at the two previous examples:

74/136
Shallow Copy - Immutables

75/136
Shallow Copy - Immutables

76/136
Shallow Copy - Mutables

77/136
Shallow Copy - Mutables

78/136
Shallow Copy - Mutables

79/136
list.count

To count the number of occurences of a specific item in a list, use the count method:
1 my_list = ['a', 'b', 'c', 'b', 'a', 'a']

1 my_list.count('a') 1 my_list.count('c')
3 1
1 my_list.count('b') 1 my_list.count('d')
2 0

80/136
list.index
We can find the first position of a specific item via the index method:
1 my_list = ['a', 'b', 'c', 'b', 'a', 'a']
2 my_list.index('c')

2
1 my_list.index('a')

0
1 my_list.index('d')

ValueError: 'd' is not in list

81/136
list.insert
We have already added items to a list’s end with the append method. The insert
method allows us to add an item before an arbitrary position:
1 my_list = ['a', 'b', 'c', 'b', 'a', 'a']
2 my_list.insert(0, 'z')
3 my_list

['z', 'a', 'b', 'c', 'b', 'a', 'a']


1 my_list.insert(100500, 'what happened?')
2 my_list

['z', 'a', 'b', 'c', 'b', 'a', 'a', 'what happened?']

82/136
list.pop
We can remove an item at an arbitrary position using the pop method:
1 my_list = ['a', 'b', 'c', 'b', 'a', 'a']
2 my_list.pop(2)
3 my_list

['a', 'b', 'b', 'a', 'a']


Invoking pop without an argument removes the last item:
1 my_list.pop()
2 my_list

['a', 'b', 'b', 'a']

83/136
list.pop

Be careful with list.pop!


1 my_list = ['a', 'b', 'c', 'b', 'a', 'a']
2 my_list.pop(100500)

IndexError: pop index out of range

84/136
list.pop

Be careful with list.pop!


1 my_list.clear()
2 my_list.pop()

IndexError: pop from empty list

85/136
list.remove
If we want to remove specific values (instead of values at specific positions), we need to
use remove:
1 my_list = ['a', 'b', 'c', 'b', 'a', 'a']
2 my_list.remove('a')
3 my_list

['b', 'c', 'b', 'a', 'a']


1 my_list.remove('d')

ValueError: list.remove(x): x not in list

86/136
list.reverse

The reverse method reverses a list in‑place:


1 my_list = ['a', 'b', 'c', 'b', 'a', 'a']
2 my_list.reverse()
3 my_list

['a', 'a', 'b', 'c', 'b', 'a']

87/136
list.sort

The sort method sorts a list in‑place:


1 my_list = ['a', 'b', 'c', 'b', 'a', 'a']
2 my_list.sort()
3 my_list

['a', 'a', 'a', 'b', 'b', 'c']

88/136
Reversing and Sorting not in Place

What if we want to keep the original data intact? We need to use the following functions:
1 my_list = ['a', 'b', 'c', 'b', 'a', 'a']

1 list(reversed(my_list)) 1 sorted(my_list)
['a', 'a', 'b', 'c', 'b', 'a'] ['a', 'a', 'a', 'b', 'b', 'c']
1 my_list 1 my_list
['a', 'b', 'c', 'b', 'a', 'a'] ['a', 'b', 'c', 'b', 'a', 'a']

89/136
Slicing Lists
We can access several items at once via slicing:
1 my_list = ['a', 'b', 'c', 'b', 'a', 'a']
2 my_list[0:3]

['a', 'b', 'c']


The start value is set to 0, the stop value is set to 3, and we get all the items from index 0
to index (3 - 1).
1 my_list[3:-1]

['b', 'a']
The start value is set to 3, the stop value is set to -1, and we get all the items from index
3 to the penultimate one.

90/136
Slicing Lists
We can omit the start or the stop value when we slice from the very beginning or to the
very end, respectively:
1 my_list = ['a', 'b', 'c', 'b', 'a', 'a']
2 my_list[:3]

['a', 'b', 'c']


If we specify only the stop value, we will get items from index 0 to index (stop - 1).
1 my_list[3:]

['b', 'a', 'a']


If we specify only the start value, we will get items from index start to the end of the
list.

91/136
Slicing Lists
We can also specify the step value:
1 my_list = ['a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i']
2 my_list[1:8:2]

['b', 'd', 'f', 'h']


step value can be negative:
1 my_list[::-1]

['i', 'h', 'g', 'f', 'e', 'd', 'c', 'b', 'a']

92/136
List Comprehensions
Python provides compact syntax to map one list onto another list. Imagine that we have
a list of numbers and want to create a list with squares of those numbers. We could do
this:
1 numbers = [2, 3, 4]
2 squares = []
3

4 for number in numbers:


5 squares.append(number ** 2)
6

7 squares

[4, 9, 16]

93/136
List Comprehensions

Thanks to list comprehension, we can also do this:


1 numbers = [2, 3, 4]
2 squares = [number ** 2 for number in numbers]
3 squares

[4, 9, 16]

94/136
List Comprehensions

We can filter input elements, keeping only those that satisfy a condition:
1 numbers = [2, 3, 4]
2 even_squares = [
3 n ** 2 for n in numbers if n % 2 == 0
4 ]
5 even_squares

[4, 16]

95/136
List Comprehensions
We can loop over nested lists as well:
1 inner_list_1 = [1, 2, 3]
2 inner_list_2 = [4, 5, 6]
3 inner_list_3 = [7, 8, 9]
4 outer_list = [
5 inner_list_1, inner_list_2, inner_list_3
6 ]
7 squares = [
8 n ** 2 for i_l in outer_list for n in i_l
9 ]
10 squares

[1, 4, 9, 16, 25, 36, 49, 64, 81]

96/136
Lists: Overview

Add items: Process items:


append ‘index
extend count
insert Manipulate lists:
Remove items: clear
pop copy
remove reverse
List Comprehensions sort

97/136
Tuples

98/136
More on Tuples

Since tuples are immutable, we can’t do much with them!


1 my_tuple = ('a', 'b', 'c', 'b', 'a', 'a')

1 my_tuple.count('a') 1 my_tuple[1:-1:2]
3 ('b', 'b')
1 my_tuple.index('c') 1 [ord(n) for n in my_tuple]
2 [97, 98, 99, 98, 97, 97]

99/136
Dictionaries

100/136
More on Dictionaries
We have already seen creating dictionaries and accessing their values by key:
1 my_dict = {'c': 1, 'b': 2, 'a': 3}
2 my_dict['d'] = 4
3 my_dict['c'] = 'Hello, world!'
4 my_dict

{'c': 'Hello, world!', 'b': 2, 'a': 3, 'd': 4}


1 my_dict['z']

KeyError: 'z'

101/136
in and dict.get
If we try to access a key that isn’t in the dictionary, we get a KeyError. We can mitigate
this by either using a check with in (and not in) or by using the dict.get method:
1 my_dict = {'c': 1, 'b': 2, 'a': 3}
2

3 my_value = my_dict['d'] if 'd' in my_dict else 'default'


4 my_value

'default'
1 my_value = my_dict.get('d', 'default')
2 my_value

'default'

102/136
keys, values, and items
To get dictionary’s keys, values, or key‑value pairs, we need to use the corresponding
methods:
1 my_dict = {'c': 1, 'b': 2, 'a': 3}
2 my_dict.keys()

dict_keys(['c', 'b', 'a'])


1 my_dict.values()

dict_values([1, 2, 3])
1 my_dict.items()

dict_items([('c', 1), ('b', 2), ('a', 3)])

103/136
dict.clear and dict.copy
dict.clear and dict.copy methods act like their list counterparts:
1 my_dict_1 = {'c': 1, 'b': 2, 'a': 3}
2 my_dict_2 = my_dict_1.copy()
3 my_dict_1.clear()
4 my_dict_1

{}
1 my_dict_2

{'c': 1, 'b': 2, 'a': 3}

104/136
dict.pop and dict.popitem

To remove items from a dictionary, use pop and popitem methods:


1 my_dict = {'c': 1, 'b': 2, 'a': 3}

1 my_dict.pop('b') 1 my_dict.pop('z', 'default')


2 'default'
1 my_dict.pop('z') 1 my_dict
KeyError: 'z' {'c': 1, 'a': 3}

105/136
dict.pop and dict.popitem
To remove items from a dictionary, use pop and popitem methods:
1 my_dict

{'c': 1, 'a': 3}
1 my_dict.popitem()

('a', 3)
1 my_dict.popitem()

('c', 1)
1 my_dict.popitem()

KeyError: 'popitem(): dictionary is empty'

106/136
Dictionary Comprehensions

Dictionary comprehensions give us a convenient way to generate dictionaries:


1 {char: ord(char) for char in 'Hello!'}

{'H': 72, 'e': 101, 'l': 108, 'o': 111, '!': 33}

107/136
Sets

108/136
More on Sets
Adding items
add
update
Removing items
remove
discard
pop
clear
Manipulating sets
clear
copy

109/136
Set Comprehensions

Set comprehensions allow us to map one set to another in the same fashion:
1 my_set = {5, 4, 3, 2, 1}
2 {x ** 2 for x in my_set if x % 2 != 0}

{1, 9, 25}

110/136
Strings

111/136
More on Strings

We can slice strings just like lists:


1 my_string = 'Hello, world!'
2 my_string[2:12:2]

'lo ol'
1 my_string[::-1]

'!dlrow ,olleH'

112/136
String Formatting

We can format strings in three different way: the printf‑like formatting via the
%‑operator, the str.format method, and the f‑strings. The printf‑like formatting is pretty
much obsolete, I do not advise using it:
1 '%s is approximately %.2f' % ('Pi', 3.14)

'Pi is approximately 3.14'

113/136
String Formatting
The str.format method allows us to substitute values by index:
1 '{}, {}, and {}'.format(
2 'first', 'second', 'third'
3 )

'first, second, and third'


We can change the order of the indices:
1 '{1}, {2}, and {0}'.format(
2 'first', 'second', 'third'
3 )

'second, third, and first'

114/136
String Formatting

Alternatively, we can use named fields:


1 '{name} is approximately {value}'.format(
2 value=3.14, name='Pi'
3 )

'Pi is approximately 3.14'

115/136
String Formatting

We can use the same name several times:


1 my_str = '{name} is an important constant.\n{name} is approximately\n{value}'.format(
2 value=3.14, name='Pi'
3 )
4 print(my_str)

Pi is an important constant.
Pi is approximately
3.14

116/136
String Formatting

We can also specify width and alignment of our substitutions:


1 print('|{:<10}|'.format('left'))
2 print('|{:^10}|'.format('center'))
3 print('|{:>10}|'.format('right'))

|left |
| center |
| right|

117/136
String Formatting

1 my_str = '|{name:<6}| is a constant.\n|{name:^6}| is approximately\n|{value:>6}|'.format(


2 value=3.14, name='Pi'
3 )
4 print(my_str)

|Pi | is a constant.
| Pi | is approximately
| 3.14|

118/136
f-strings
f‑strings (strings that start with an f) allow us to embed Python expressions inside
string constants:
1 n = 3
2 p = 4
3 f'{n} to the power of {p} equals {n ** p}'

'3 to the power of 4 equals 81'


We can’t create an f‑string with empty curly braces:
1 f'{} to the power of {} equals {}'

SyntaxError: f-string: empty expression not allowed (2794623023.py, line 1)

119/136
When to use what?
f‑strings are evaluated at runtime, so we can use any valid expression inside:
1 f'{n} to the power of {p} equals {n ** p}'

'3 to the power of 4 equals 81'


1 my_str = 'Hello, world!'
2 f'{my_str[1:11:2]}'

'el,wr'
On the other hand, if we want to create a template (e.g., an HTTP query) and use it with
various values during the program’s life cycle, we probably want to use str.format.

120/136
str methods
Remember to use dir and help! Strings have many useful methods in Python (too
many to cover here). join and split help us to create new strings. lower, upper,
capitalize, and swapcase allow us to manipulate character case. lstrip,
rstrip, and strip remove trailing whitespaces from a string. startswith,
endswith, isalnum and others save us from the hassle of using regular expressions.

1 '25\t165\tMax'.split('\t') 1 'It GeTs BeTtEr'.swapcase()


['25', '165', 'Max'] 'iT gEtS bEtTeR'
1 '\t'.join([str(x) for x in [11, 13, 12, 15, 13, 14, 11]])

'11\t13\t12\t15\t13\t14\t11'

121/136
Containers

122/136
More Containers from collections

The collections module provides several specialized container datatypes. We will cover
namedtuple, Counter, OrderedDict, and deque.

123/136
namedtuple
namedtuple is a complex data type that allows to group variables together under one
name. If we care about grouping attributes but don’t really care about modeling
behavior, we can use a namedtuple instead of defining a new class.
1 from collections import namedtuple
2 ChocoCow = namedtuple('ChocoCow', 'name color cocoa_content')
3 choco_cow = ChocoCow(
4 name='Goldy', color='golden', cocoa_content=65
5 )
6 choco_cow

ChocoCow(name='Goldy', color='golden', cocoa_content=65)

124/136
namedtuple

We can use the dir method to inspect our new object:


1 [x for x in dir(choco_cow) if not x.startswith('_')]

['cocoa_content', 'color', 'count', 'index', 'name']

125/136
namedtuple

We see an index method in there! Does that mean that we can also access the
properties of our namedtuple by index?

1 choco_cow.name 1 choco_cow[-1]
'Goldy' 65
1 choco_cow[0] 1 choco_cow.cocoa_content
'Goldy' 65

126/136
namedtuple

We can unpack a namedtuple like a regular tuple:


1 name, color, cocoa_content = choco_cow
2 color

'golden'

127/136
Counter

Counter is pretty self‑explanatory:


1 from collections import Counter
2 my_counter = Counter('Hello, World!')
3 print(my_counter)

Counter({'l': 3, 'o': 2, 'H': 1, 'e': 1, ',': 1, ' ': 1, 'W': 1, 'r': 1, 'd': 1, '!': 1})

128/136
Counter

We can update our existing counter with items from another sequence:
1 my_counter.update('Hallo, Welt!')
2 print(my_counter)

Counter({'l': 6, 'o': 3, 'H': 2, 'e': 2, ',': 2, ' ': 2, 'W': 2, '!': 2, 'r': 1, 'd': 1, 'a': 1, 't': 1})
1 my_counter.most_common(3)

[('l', 6), ('o', 3), ('H', 2)]

129/136
Counter

We can even subtract one counter from another!


1 eng_counter = Counter('Hello, World!')
2 de_counter = Counter('Hallo, Welt!')
3 eng_counter - de_counter

Counter({'o': 1, 'r': 1, 'd': 1})

130/136
OrderedDict

Starting with Python 3.7, dictionaries preserve the insertion order, as you might have
already noticed:
1 my_dict = {'c': 1, 'b': 2, 'a': 3}
2 my_dict

{'c': 1, 'b': 2, 'a': 3}


What do we even need an OrderedDict for?

131/136
OrderedDict
Consider the following example:
1 from collections import OrderedDict
2 ord_dict_1 = OrderedDict({'c': 1, 'b': 2, 'a': 3})
3 ord_dict_2 = OrderedDict({'a': 3, 'b': 2, 'c': 1})
4 dict_1 = {'c': 1, 'b': 2, 'a': 3}
5 dict_2 = {'a': 3, 'b': 2, 'c': 1}

1 ord_dict_1 == ord_dict_2 1 dict_1 == dict_2


False True
When we compare two objects of the OrderedDict type, not only the items are tested
for equality, but also the insertion order.

132/136
deque
deque (implemented as a doubly linked list) is a datatype that serves as a
generalization of both a stack and a queue:
1 from collections import deque
2 my_deque = deque([1, 2, 3, 4, 5])
3 my_deque.append(6)
4 my_deque.appendleft(0)

1 my_deque.pop() 1 my_deque.popleft()
6 0
1 my_deque

deque([1, 2, 3, 4, 5])

133/136
deque

deque allows us to efficiently add elements to both ends of the data structure as
opposed to regular Python lists. However, keep in mind that there is no golden pill (or
silver bullet, if you like), and gaining efficiency in some operations probably means
losing efficiency with others. Which operations does a deque perform less efficiently
than a regular list?

134/136
More Containers: Overview

namedtuple
to group variables (properties, attributes) together
Counter
to count collections’ items
OrderedDict
to make sure the insertion order is preserved and checked
deque
to get efficient stack‑like and queue‑like functionality

135/136
Thank you!

QUESTIONS?
136/136

You might also like