PCAP(python)
PCAP(python)
PE1 Module 2 Data Types, Variables, Basic Input-Output Operations, Basic Operators
PE1 Module 3 Boolean Values, Conditional Execution, Loops, Lists and List Processing,
Logical and Bitwise Operations
2. Built-in functions, contrary to user-defined functions, are always available and don't
have to be imported. Python 3.8 comes with 69 built-in functions. You can find their full
list provided in alphabetical order in the Python Standard Library.
3. To call a function (this process is known as function invocation or function call), you
need to use the function name followed by parentheses. You can pass arguments into a
function by placing them inside the parentheses. You must separate arguments with a
comma, e.g., print("Hello,", "world!") . An "empty" print() function outputs an
empty line to the screen.
4. Python strings are delimited with quotes, e.g., "I am a string" (double quotes), or 'I
am a string, too' (single quotes).
6. In Python strings the backslash ( \ ) is a special character which announces that the
next character has a different meaning, e.g., \n (the newline character) starts a new
output line.
7. Positional arguments are the ones whose meaning is dictated by their position, e.g.,
the second argument is outputted after the first, the third is outputted after the second,
etc.
8. Keyword arguments are the ones whose meaning is not dictated by their location, but
by a special word (keyword) used to identify them.
9. The end and sep parameters can be used for formatting the output of the print()
function. The sep parameter specifies the separator between the outputted arguments
(e.g., print("H", "E", "L", "L", "O", sep="-") , whereas the end parameter specifies
what to print at the end of the print statement.
Literals
1. Literals are notations for representing some fixed values in code. Python has various
types of literals - for example, a literal can be a number (numeric literals, e.g., 123 ), or a
string (string literals, e.g., "I am a literal.").
2. The binary system is a system of numbers that employs 2 as the base. Therefore, a
binary number is made up of 0s and 1s only, e.g., 1010 is 10 in decimal.
Octal and hexadecimal numeration systems, similarly, employ 8 and 16 as their bases
respectively. The hexadecimal system uses the decimal numbers and six extra letters.
3. Integers (or simply ints) are one of the numerical types supported by Python. They are
numbers written without a fractional component, e.g., 256 , or -1 (negative integers).
4. Floating-point numbers (or simply floats) are another one of the numerical types
supported by Python. They are numbers that contain (or are able to contain) a fractional
component, e.g., 1.27 .
5. To encode an apostrophe or a quote inside a string you can either use the escape
character, e.g., 'I\'m happy.' , or open and close the string using an opposite set of
symbols to the ones you wish to encode, e.g., "I'm happy." to encode an apostrophe,
and 'He said "Python", not "typhoon"' to encode a (double) quote.
6. Boolean values are the two constant objects True and False used to represent truth
values (in numeric contexts 1 is True , while 0 is False .
EXTRA
There is one more, special literal that is used in Python: the None literal. This literal is a
so-called NoneType object, and it is used to represent the absence of a value. We'll tell
you more about it soon.
2. Operators are special symbols or keywords which are able to operate on the values
and perform (mathematical) operations, e.g., the * operator multiplies two values: x *
y.
Variables
1. A variable is a named location reserved to store values in the memory. A variable is
created or initialized automatically when you assign a value to it for the first time. 2.1.4.1
2. Each variable must have a unique name - an identifier. A legal identifier name must be
a non-empty sequence of characters, must begin with the underscore( _ ), or a letter, and
it cannot be a Python keyword. The first character may be followed by underscores,
letters, and digits. Identifiers in Python are case-sensitive. 2.1.4.1
3. Python is a dynamically-typed language, which means you don't need to declare
variables in it. 2.1.4.3 To assign values to variables, you can use a simple assignment
operator in the form of the equal ( = ) sign, i.e., var = 1 .
4. You can also use compound assignment operators (shortcut operators) to modify
values assigned to variables, e.g., var += 1 , or var /= 5 * 2 . 2.1.4.8
5. You can assign new values to already existing variables using the assignment operator
or one of the compound operators, e.g.: 2.1.4.5
var = 2
print(var)
var = 3
print(var)
var += 1
print(var)
6. You can combine text and variables using the + operator, and use the print()
function to output strings and variables, e.g.: 2.1.4.4
var = "007"
print("Agent " + var)
Key takeaways (Loops, conditional
statements)
1. There are two types of loops in Python: while and for :
# Example 1
while True:
print("Stuck in an infinite loop.")
# Example 2
counter = 5
while counter > 2:
print(counter)
counter -= 1
the for loop executes a set of statements many times; it's used to iterate over a
sequence (e.g., a list, a dictionary, a tuple, or a set - you will learn about them soon)
or other objects that are iterable (e.g., strings). You can use the for loop to iterate
over a sequence of numbers using the built-in range function. Look at the examples
below:
# Example 1
word = "Python"
for letter in word:
print(letter, end="*")
# Example 2
for i in range(1, 10):
if i % 2 == 0:
print(i)
2. You can use the break and continue statements to change the flow of a loop:
text = "pyxpyxpyx"
for letter in text:
if letter == "x":
continue
print(letter, end="")
3. The while and for loops can also have an else clause in Python. The else clause
executes after the loop finishes its execution as long as it has not been terminated by
break , e.g.:
n = 0
while n != 3:
print(n)
n += 1
else:
print(n, "else")
print()
else:
print(i, "else")
Example code:
for i in range(3):
print(i, end=" ") # Outputs: 0 1 2
Create a for loop that counts from 0 to 10, and prints odd numbers to the screen. Use
the skeleton below:
#Sample solution:
Exercise 2
Create a while loop that counts from 0 to 10, and prints odd numbers to the screen. Use
the skeleton below:
#Sample solution:
x = 1
while x < 11:
if x % 2 != 0:
print(x)
x += 1
Exercise 3
Create a program with a for loop and a break statement. The program should iterate
over characters in an email address, exit the loop when it reaches the @ symbol, and
print the part before @ on one line. Use the skeleton below:
for ch in "john.smith@pythoninstitute.org":
if ch == "@":
# Line of code.
# Line of code.`
**Sample solution**:
for ch in "john.smith@pythoninstitute.org":
if ch == "@":
break
print(ch, end="")
Exercise 4
Create a program with a for loop and a continue statement. The program should iterate
over a string of digits, replace each 0 with x , and print the modified string to the screen.
Use the skeleton below:
Sample solution:
Exercise 5
What is the output of the following code?
n = 3
while n > 0:
print(n + 1)
n -= 1
else:
print(n)
4 3 2 0
Exercise 6
What is the output of the following code?
n = range(4)
for num in n:
print(num - 1)
else:
print(num)
-1 0 1 2 3
Exercise 7
and → if both operands are true, the condition is true, e.g., (True and True) is
True ,
or → if any of the operands are true, the condition is true, e.g., (True or False) is
True ,
not → returns false if the result is true, and returns true if the result is false, e.g.,
not True is False .
2. You can use bitwise operators to manipulate single bits of data. The following sample
data:
will be used to illustrate the meaning of bitwise operators in Python. Analyze the
examples below:
& does a bitwise and, e.g., x & y = 0 , which is 0000 0000 in binary,
| does a bitwise or, e.g., x | y = 31 , which is 0001 1111 in binary,
˜ does a bitwise not, e.g., ˜ x = 240 *, which is 1111 0000 in binary,
^ does a bitwise xor, e.g., x ^ y = 31 , which is 0001 1111 in binary,
>> does a bitwise right shift, e.g., y >> 1 = 8 , which is 0000 1000 in binary,
<< does a bitwise left shift, e.g., y << 3 = , which is 1000 0000 in binary,
* -16 (decimal from signed 2's complement) -- read more about the Two's
complement operation.
Exercise 1
What is the output of the following snippet?
x = 1
y = 0
z = ((x == y) and (x == y)) or not(x == y)
print(not(z))
False
Exercise 2
What is the output of the following snippet?
x = 4
y = 1
a = x & y
b = x | y
c = ~x # tricky!
d = x ^ 5
e = x >> 2
f = x << 2
print(a, b, c, d, e, f)
0 5 -5 1 1 16
You will learn more about nesting in module 3.1.7 for the time being, we just want you to
be aware that something like this is possible, too.
my_list = [1, 2, 3, 4]
del my_list[2]
print(my_list) # outputs: [1, 2, 4]
del my_list # deletes the whole list
Again, you will learn more about this in module 3.1.6 don't worry. For the time being just
try to experiment with the above code and check how changing it affects the output.
Exercise 1
lst = [1, 2, 3, 4, 5]
lst.insert(1, 6)
del lst[0]
lst.append(1)
print(lst)
[6, 2, 3, 4, 5, 1]
Exercise 2
lst = [1, 2, 3, 4, 5]
lst_2 = []
add = 0
for number in lst:
add += number
lst_2.append(add)
print(lst_2)
Exercise 3
lst = []
del lst
print(lst)
Exercise 4
What is the output of the following snippet?
[2, 3]
3
lst = [5, 3, 1, 2, 4]
print(lst)
lst.sort()
print(lst) # outputs: [1, 2, 3, 4, 5]
2. There is also a list method called reverse() , which you can use to reverse the list, e.g.:
lst = [5, 3, 1, 2, 4]
print(lst)
lst.reverse()
print(lst) # outputs: [4, 2, 1, 3, 5]
Exercise 1
Exercise 2
a = 3
b = 1
c = 2
lst = [a, c, b]
lst.sort()
print(lst)
Exercise 3
a = "A"
b = "B"
c = "C"
d = " "
lst = [a, b, c, d]
lst.reverse()
print(lst)
2. If you want to copy a list or part of the list, you can do it by performing slicing:
3. You can use negative indices to perform slices, too. For example:
4. The start and end parameters are optional when performing a slice:
list[start:end] ,
e.g.:
my_list = [1, 2, 3, 4, 5]
slice_one = my_list[2: ]
slice_two = my_list[ :2]
slice_three = my_list[-2: ]
print(slice_one) # outputs: [3, 4, 5]
print(slice_two) # outputs: [1, 2]
print(slice_three) # outputs: [4, 5]
my_list = [1, 2, 3, 4, 5]
del my_list[0:2]
print(my_list) # outputs: [3, 4, 5]
del my_list[:]
print(my_list) # deletes the list content, outputs: []
6. You can test if some items exist in a list or not using the keywords in and not in ,
e.g.:
Exercise 1
Exercise 2
['B', 'C']
Exercise 3
[]
Exercise 4
Exercise 5
Insert in or not in instead of ??? so that the code outputs the expected result.
Here's an example of a list comprehension - the code creates a five-element list filled
with with the first five natural numbers raised to the power of 3
2. You can use nested lists in Python to create matrices (i.e., two-dimensional lists). For
example:
# A four-column/four-row table - a two dimensional array (4x4)
3. You can nest as many lists in lists as you want, and therefore create n-dimensional lists,
e.g., three-, four- or even sixty-four-dimensional arrays. For example:
def hi(name):
print("Hi,", name)
hi("Greg")
positional argument passing in which the order of arguments passed matters (Ex.
1,
keyword (named) argument passing in which the order of arguments passed
doesn't matter (Ex. 2,
a mix of positional and keyword argument passing (Ex. 3.
# Ex. 1
def subtra(a, b):
print(a - b)
subtra(5, 2) # outputs: 3
subtra(2, 5) # outputs: -3
# Ex. 2
def subtra(a, b):
print(a - b)
subtra(a=5, b=2) # outputs: 3
subtra(b=2, a=5) # outputs: 3
# Ex. 3
def subtra(a, b):
print(a - b)
subtra(5, b=2) # outputs: 3
subtra(5, 2) # outputs: 3
It's important to remember that positional arguments mustn't follow keyword arguments.
That's why if you try to run the following snippet:
3. You can use the keyword argument passing technique to pre-define a value for a given
argument:
Exercise 1
intro()
Exercise 2
intro(b="Sean Connery")
My name is Sean Connery. James Bond.
Exercise 3
Exercise 4
Key takeaways
1. You can use the return keyword to tell a function to return some value. The return
statement exits the function, e.g.:
def wishes():
return "Happy Birthday!"
w = wishes()
print(w) # outputs: Happy Birthday!
# Example 1
def wishes():
print("My Wishes")
# Example 2
def wishes():
print("My Wishes")
return "Happy Birthday"
print(wishes())
# outputs: My Wishes
# Happy Birthday
def hi_everybody(my_list):
for name in my_list:
print("Hi,", name)
def create_list(n):
my_list = []
for i in range(n):
my_list.append(i)
return my_list
print(create_list(5))
Exercise 1
What is the output of the following snippet?
def hi():
return
print("Hi!")
hi()
Exercise 2
def is_int(data):
if type(data) == int:
return True
elif type(data) == float:
return False
print(is_int(5))
print(is_int(5.0))
print(is_int("5"))
True
False
None
Exercise 3
def even_num_lst(ran):
lst = []
for num in range(ran):
if num % 2 == 0:
lst.append(num)
return lst
print(even_num_lst(11))
[0, 2, 4, 6, 8, 10]
Exercise 4
foo = [1, 2, 3, 4, 5]
print(list_updater(foo))
Example 1
var = 2
def mult_by_var(x):
return x * var
print(mult_by_var(7))
# outputs: 14
Example 2
def mult(x):
var = 5
return x * var
print(mult(7))
# outputs: 35
Example 3
def mult(x):
var = 7
return x * var
var = 3
print(mult(7))
# outputs: 49
2. A variable that exists inside a function has a scope inside the function body (Example
4, e.g.:
Example 4
def adding(x):
var = 7
return x + var
print(adding(4))
# outputs: 11
print(var)
# NameError
3. You can use the global keyword followed by a variable name to make the variable's
scope global, e.g.:
var = 2
print(var) # outputs: 2
def return_var():
global var
var = 5
return var
print(return_var()) # outputs: 5
print(var) # outputs: 5
Exercise 1
What will happen when you try to run the following code?
def message():
alt = 1
print("Hello, World!")
print(alt)
The NameError exception will be thrown ( NameError: name 'alt' is not defined )
Exercise 2
a = 1
def fun():
a = 2
print(a)
fun()
print(a)
2 1
Exercise 3
a = 1
def fun():
global a
a = 2
print(a)
fun()
a = 3
print(a)
2 3
Exercise 4
a = 1
def fun():
global a
a = 2
print(a)
a = 3
fun()
print(a)
2 2
2. You can use recursive functions in Python to write clean, elegant code, and divide it
into smaller, organized chunks. On the other hand, you need to be very careful as it
might be easy to make a mistake and create a function which never terminates. You also
need to remember that recursive calls consume a lot of memory, and therefore may
sometimes be inefficient.
When using recursion, you need to take all its advantages and disadvantages into
consideration.
The factorial function is a classic example of how the concept of recursion can be put in
practice:
print(factorial(4))
# 4 * 3 * 2 * 1 = 24
Exercise 1
What will happen when you attempt to run the following snippet and why?
def factorial(n):
return n * factorial(n - 1)
print(factorial(4))
The factorial function has no termination condition (no base case) so Python will raise an
exception ( RecursionError: maximum recursion depth exceeded )
Exercise 2
def fun(a):
if a > 30:
return 3
else:
return a + fun(a + 3)
print(fun(25))
56
my_tuple = (1, 2, True, "a string", (3, 4), [5, 6], None)
print(my_tuple)
my_list = [1, 2, True, "a string", (3, 4), [5, 6], None]
print(my_list)
Each tuple element may be of a different type (i.e., integers, strings, booleans, etc.). What
is more, tuples can contain other tuples or lists (and the other way round).
empty_tuple = ()
print(type(empty_tuple)) # outputs: <class 'tuple'>`
If you remove the comma, you will tell Python to create a variable, not a tuple:
my_tuple_1 = 1
my_tuple = 1, 2, 3,
del my_tuple():
print(my_tuple) # NameError: name 'my_tuple' is not defined`
6. You can loop through a tuple elements (Example 1, check if a specific element is
(not)present in a tuple (Example 2, use the len() function to check how many elements
there are in a tuple (Example 3, or even join/multiply tuples (Example 4
# Example 1
tuple_1 = (1, 2, 3)
for elem in tuple_1:
print(elem)
# Example 2
tuple_2 = (1, 2, 3, 4)
print(5 in tuple_2)
print(5 not in tuple_2)
# Example 3
tuple_3 = (1, 2, 3, 5)
print(len(tuple_3))
# Example 4
EXTRA
You can also create a tuple using a Python built-in function called tuple() . This is
particularly useful when you want to convert a certain iterable (e.g., a list, range, string,
etc.) to a tuple:
my_tuple = tuple((1, 2, "string"))
print(my_tuple)
my_list = [2, 4, 6]
print(my_list) # outputs: [2, 4, 6]
print(type(my_list)) # outputs: <class 'list'>
tup = tuple(my_list)
print(tup) # outputs: (2, 4, 6)
print(type(tup)) # outputs: <class 'tuple'>
By the same fashion, when you want to convert an iterable to a list, you can use a Python
built-in function called list() :
tup = 1, 2, 3,
my_list = list(tup)
print(type(my_list)) # outputs: <class 'list'>
Each dictionary is a set of key: value pairs. You can create it by using the following
syntax:
my_dictionary = {
key1: value1,
key2: value2,
key3: value3,
2. If you want to access a dictionary item, you can do so by making a reference to its key
inside a pair of square brackets (ex. 1 or by using the get() method (ex. 2
pol_eng_dictionary = {
"kwiat": "flower",
"woda": "water",
"gleba": "soil"
3. If you want to change the value associated with a specific key, you can do so by
referring to the item's key name in the following way:
pol_eng_dictionary = {
"zamek": "castle",
"woda": "water",
"gleba": "soil"
pol_eng_dictionary["zamek"] = "lock"
item = pol_eng_dictionary["zamek"]
print(item) # outputs: lock
4. To add or remove a key (and the associated value), use the following syntax:
You can also insert an item to a dictionary by using the update() method, and remove
the last element by using the popitem() method, e.g.:
pol_eng_dictionary.update({"gleba": "soil"})
pol_eng_dictionary.popitem()
print(pol_eng_dictionary) # outputs: {'kwiat': 'flower'}
5. You can use the for loop to loop through a dictionary, e.g.:
pol_eng_dictionary = {
"zamek": "castle",
"woda": "water",
"gleba": "soil"
# outputs: zamek
# woda
# gleba
6. If you want to loop through a dictionary's keys and values, you can use the items()
method, e.g.:
pol_eng_dictionary = {
"zamek": "castle",
"woda": "water",
"gleba": "soil"
7. To check if a given key exists in a dictionary, you can use the in keyword:
pol_eng_dictionary = {
"zamek": "castle",
"woda": "water",
"gleba": "soil"
if "zamek" in pol_eng_dictionary:
print("Yes")
else:
print("No")
8. You can use the del keyword to remove a specific item, or delete a dictionary. To
remove all the dictionary's items, you need to use the clear() method:
pol_eng_dictionary = {
"zamek": "castle",
"woda": "water",
"gleba": "soil"
print(len(pol_eng_dictionary)) # outputs: 3
del pol_eng_dictionary["zamek"] # remove an item
print(len(pol_eng_dictionary)) # outputs: 2
pol_eng_dictionary.clear() # removes all the items
print(len(pol_eng_dictionary)) # outputs: 0
del pol_eng_dictionary # removes the dictionary
pol_eng_dictionary = {
"zamek": "castle",
"woda": "water",
"gleba": "soil"
copy_dictionary = pol_eng_dictionary.copy()
Key takeaways: tuples and dictionaries
Exercise 1
my_tup = (1, 2, 3)
print(my_tup[2])
Exercise 2
tup = 1, 2, 3
a, b, c = tup
print(a * b * c)
The program will print 6 to the screen. The tup tuple elements have been "unpacked" in
the a , b , and c variables.
Exercise 3
Complete the code to correctly use the count() method to find the number of duplicates
of 2 in the following tuple.
tup = 1, 2, 3, 2, 4, 5, 6, 2, 7, 2, 8, 9
duplicates = # Write your code here.
print(duplicates) #
outputs: 4
tup = 1, 2, 3, 2, 4, 5, 6, 2, 7, 2, 8, 9
duplicates = tup.count(2)
print(duplicates) # outputs: 4
Exercise 4
Write a program that will "glue" the two dictionaries ( d1 and d2 ) together and create a
new one ( d3 ).
Sample solution:
print(d3)
Exercise 5
Sample solution:
Exercise 6
colors = (("green", "#008000"), ("blue", "#0000FF")) # Write your code here. print
Sample solution:
Exercise 7
Exercise 8
colors = {
"white": (255, 255, 255),
"grey": (128, 128, 128),
"red": (255, 0, 0),
"green": (0, 128, 0)
}
python white : (255, 255, 255) grey : (128, 128, 128) red : (255, 0, 0) green :
(0, 128, 0)
Key takeaways (Python modules)
A lazy programmer is a programmer who looks for existing solutions and analyzes the
available code before they start to develop their own software from scratch.
1. If you want to import a module as a whole, you can do it using the import module_name
statement. You are allowed to import more than one module at once using a comma-
separated list. For example:
import mod1
import mod2, mod3, mod4
although the latter form is not recommended due to stylistic reasons, and it's better and
prettier to express the same intention in more a verbose and explicit form, such as:
import mod2
import mod3
import mod4
2. If a module is imported in the above manner and you want to access any of its entities,
you need to prefix the entity's name using dot notation. For example:
import my_module
result = my_module.my_function(my_module.my_data)
The snippet makes use of two entities coming from the my_module module: a function
named my_function() and a variable named my_data . Both names must be prefixed by
my_module. None of the imported entity names conflicts with the identical names existing
in your code's namespace.
3. You are allowed not only to import a module as a whole, but to import only individual
entities from it. In this case, the imported entities must not be prefixed when used. For
example:
The above way - despite its attractiveness - is not recommended because of the danger
of causing conflicts with names derived from importing the code's namespace.
4. The most general form of the above statement allows you to import all entities offered
by a module:
from my_module import *
result = my_function(my_data)
Note: this import's variant is not recommended due to the same reasons as previously
(the threat of a naming conflict is even more dangerous here).
5. You can change the name of the imported entity "on the fly" by using the as phrase of
the import . For example:
Exercise 1
You want to invoke the function make_money() contained in the module named mint . Your
code begins with the following line:
import mint
mint.make_money()
Exercise 2
You want to invoke the function make_money() contained in the module named mint . Your
code begins with the following line:
make_money()
Exercise 3
You've written a function named make_money on your own. You need to import a function
of the same name from the mint module and don't want to rename any of your previously
defined names. Which variant of the import statement may help you with the issue?
Exercise 4
What form of the make_money function invocation is valid if your code starts with the
following line?
2. The math module couples more than 50 symbols (functions and constants) that
perform mathematical operations (like sine() , pow() , factorial() ) or providing
important values (like π and the Euler symbol e).
3. The random module groups more than 60 entities designed to help you use pseudo-
random numbers. Don't forget the prefix "random", as there is no such thing as a real
random number when it comes to generating them using the computer's algorithms.
4. The platform module contains about 70 functions which let you dive into the
underlaying layers of the OS and hardware. Using them allows you to get to know more
about the environment in which your code is executed.
Exercise 1
What is the expected value of the result variable after the following code is executed?
import math
result = math.e == math.exp(1)
True
Exercise 2
Complete the sentence) Setting the generator's seed with the same value each time your
program is run guarantees that...
... the pseudo-random values emitted from the random module will be exactly the same.
Exercise 3
Which of the platform module's functions will you use to determine the name of the CPU
running inside your computer?
Exercise 4
import platform
print(len(platform.python_version_tuple()))
3
Key takeaways (How to create a module)
Notes
2. During the very first import of the actual module, Python translates its source code into
the semi-compiled format stored inside the pyc files, and deploys these files into the
__pycache__ directory located in the module's home directory.
3. If you want to instruct your module's user that a particular entity should be treated as
private (i.e. not to be explicitly used outside the module) you can mark its name with
either the _ or __ prefix. Don't forget that this is only a recommendation, not an order.
5. If you want convince Python that it should take into account a non-standard package's
directory, its name needs to be inserted/appended into/to the import directory list stored
in the path variable contained in the sys module.
Exercise 1
You want to prevent your module's user from running your code as an ordinary script.
How will you achieve such an effect?
import sys
if __name__ == "__main__":
print "Don't do that!" sys.exit()`
Exercise 2
Exercise 3
The directory mentioned in the previous exercise contains a sub-tree of the following
structure:
import abc.def.mymodule
2. To make use of The Cheese Shop the specialized tool has been created and its name is
pip (pip installs packages while pip stands for... ok, don't mind). As pip may not be
deployed as a part of standard Python installation, it is possible that you will need to
install it manually. Pip is a console tool.
pip --version
or
pip3 --version
Check yourself which of these works for you in your OS' environment.
Exercise 1
Exercise 2
Why should I ensure which one of pip and pip3 works for me?
When Python 2 and Python 3 coexist in your OS, it's likely that pip identifies the instance
of pip working with Python 2 packages only.
Exercise 3
Exercise 4
You have to ask your sysadmin - don't try to hack your OS!
Key takeaways (ASCII
1. Computers store characters as numbers. There is more than one possible way of
encoding characters, but only some of them gained worldwide popularity and are
commonly used in IT these are ASCII (used mainly to encode the Latin alphabet and
some of its derivates) and UNICODE (able to encode virtually all alphabets being used by
humans).
3. UNICODE uses different ways of encoding when it comes to storing the characters
using files or computer memory: two of them are UCS4 and UTF8 (the latter is the most
common as it wastes less memory space).
Exercise 1
What is BOM?
BOM Byte Order Mark) is a special combination of bits announcing encoding used by a
file's content (eg. UCS4 or UTFB.
Exercise 2
Is Python 3 _I18N_ed?
Yes, it's completely internationalized - we can use UNICODE characters inside our code,
read them from input and send to output.
one-line strings, which cannot cross line boundaries – we denote them using either
apostrophes ( 'string' ) or quotes ( "string" )
multi-line strings, which occupy more than one line of source code, delimited by
trigraphs:
or
2. The length of a string is determined by the len() function. The escape character ( \ )
is not counted. For example:
print(len("\n\n"))
outputs 2 .
3. Strings can be concatenated using the + operator, and replicated using the *
operator. For example:
asterisk = '*'
plus = "+"
decoration = (asterisk + plus) * 4 + asterisk
print(decoration)
outputs *+*+*+*+* .
4. The pair of functions chr() and ord() can be used to create a character using its
codepoint, and to determine a codepoint corresponding to a character. Both of the
following expressions are always true:
chr(ord(character)) == character
ord(chr(codepoint)) == codepoint
6. The method named index() finds the index of a given substring inside the string.
Exercise 1
What is the length of the following string assuming there is no whitespaces between the
quotes?
""" """
Exercise 2
s = 'yesteryears'
the_list = list(s)
print(the_list[3:6])
Exercise 3
bcd
Prev
2. String content can be determined using the following methods (all of them return
Boolean values):
Exercise 1
for ch in "abc123XYX":
if ch.isupper():
print(ch.lower(), end='')
elif ch.islower():
print(ch.upper(), end='')
else:
print(ch, end='')
ABC123xyz
Exercise 2
of
Exercise 3
Where*are*the*snows?
Exercise 4
Key takeaways
1. Strings can be compared to strings using general comparison operators, but comparing
them to numbers gives no reasonable result, because no string can be equal to any
number. For example:
4. A string can be converted to a number (although not every string) using either the
int() or float() function. The conversion fails if a string doesn't contain a valid number
image (an exception is raised then).
Exercise 1
'smith' > 'Smith' 'Smiths' < 'Smith' 'Smith' > '1000' '11' < '8'
1, 3 and 4
Exercise 2
are
Exercise 3
s1 = '12.8'
i = int(s1)
s2 = str(i)
f = float(s2)
print(s1 == s2)
False
When the exception happens, the execution of the code is not terminated, but instead
jumps into the except branch. This is the place where the handling of the exception
should take place. The general scheme for such a construction looks as follows:
:
# The code that always runs smoothly.
:
try:
:
# Risky code.
:
except:
:
# Crisis management takes place here.
:
:
# Back to normal.
:
2. If you need to handle more than one exception coming from the same try branch ,you
can add more than one except branch, but you have to label them with different
exception names, like this:
:
# The code that always runs smoothly.
:
try:
:
# Risky code.
:
except Except_1:
# Crisis management takes place here.
except Except_2:
# We save the world here.
:
# Back to normal.
:
At most, one of the except branches is executed – none of the branches is performed
when the raised exception doesn't match to the specified exceptions.
3. You cannot add more than one anonymous (unnamed) except branch after the named
ones.
:
# The code that always runs smoothly.
:
try:
:
# Risky code.
:
except Except_1:
# Crisis management takes place here.
except Except_2:
# We save the world here.
except:
# All other issues fall here.
:
# Back to normal.
:
Exercise 1
try:
print("Let's try to do this")
print("#"[2])
print("We succeeded!")
except:
print("We failed")
print("We're done")
Exercise 2
try:
print("alpha"[1/0])
except ZeroDivisionError:
print("zero")
except IndexingError:
print("index")
except:
print("some")
zero
Key takeaways
1. You cannot add more than one anonymous (unnamed) except branch after the named
ones.
:
# The code that always runs smoothly.
:
try:
:
# Risky code.
:
except Except_1:
# Crisis management takes place here.
except Except_2:
# We save the world here.
except:
# All other issues fall here.
:
# Back to normal.
:
2. All the predefined Python exceptions form a hierarchy, i.e. some of them are more
general (the one named BaseException is the most general one) while others are more or
less concrete (e.g. IndexError is more concrete than LookupError ).
You shouldn't put more concrete exceptions before the more general ones inside the
same except branche sequence. For example, you can do this:
try:
# Risky code.
except IndexError:
# Taking care of mistreated lists
except LookupError:
# Dealing with other erroneous lookups
but don't do that (unless you're absolutely sure that you want some part of your code to
be useless)
try:
# Risky code.
except LookupError:
# Dealing with erroneous lookups
except IndexError:
# You'll never get here
3. The Python statement raise ExceptionName can raise an exception on demand. The
same statement, but lacking ExceptionName, can be used inside the try branch only,
and raises the same exception which is currently being handled.
4. The Python statement assert expression evaluates the expression and raises the
AssertError exception when the expression is equal to zero, an empty string, or None .
You can use it to protect some critical parts of your code from devastating data.
Exercise 1
try:
print(1/0)
except ZeroDivisionError:
print("zero")
except ArithmeticError:
print("arith")
except:
print("some")
zero
Exercise 2
try:
print(1/0)
except ArithmeticError:
print("arith")
except ZeroDivisionError:
print("zero")
except:
print("some")
arith
Exercise 3
def foo(x):
assert x
return 1/x
try:
print(foo(0))
except ZeroDivisionError:
print("zero")
except:
print("some")
some
Key takeaways
1. Some abstract built-in Python exceptions are:
ArithmeticError ,
BaseException ,
LookupError .
AssertionError ,
ImportError ,
IndexError ,
KeyboardInterrupt ,
KeyError ,
MemoryError ,
OverflowError .
Exercise 1
Which of the exceptions will you use to protect your code from being interrupted through
the use of the keyboard?
KeyboardInterrupt
Exercise 2
BaseException
Exercise 3
Which of the exceptions will be raised through the following unsuccessful evaluation?
huge_value = 1E250 ** 2
OverflowError
Key takeaways (oop)
1. A class is an idea (more or less abstract) which can be used to create a number of
incarnations – such an incarnation is called an object.
2. When a class is derived from another class, their relation is named inheritance. The
class which derives from the other class is named a subclass. The second side of this
relation is named superclass. A way to present such a relation is an inheritance diagram,
where:
4. To define a Python class, you need to use the class keyword. For example:
class This_Is_A_Class:
pass
5. To create an object of the previously defined class, you need to use the class as if it
were a function. For example:
this_is_an_object = This_Is_A_Class()
Exercise 1
If we assume that pythons, vipers, and cobras are subclasses of the same superclass,
Snake, reptile, vertebrate, animal – all these answers are acceptable.
Exercise 2
Exercise 3
2. Implementing the stack in a procedural model raises several problems which can be
solved by the techniques offered by OOP (Object Oriented Programming):
3. A class method is actually a function declared inside the class and able to access all
the class's components.
4. The part of the Python class responsible for creating new objects is called the
constructor, and it's implemented as a method of the name __init__ .
5. Each class method declaration must contain at least one parameter (always the first
one) usually referred to as self , and is used by the objects to identify themselves.
6. If we want to hide any of a class's components from the outside world, we should start
its name with __ . Such components are called private.
Exercise 1
Assuming that there is a class named Snakes , write the very first line of the Python class
declaration, expressing the fact that the new class is actually a subclass of Snake.
class Python(Snakes):
Exercise 2
class Snakes:
def __init__():
self.sound = 'Sssssss'
The __init__() constructor lacks the obligatory parameter (we should name it self to
stay compliant with the standards).
Exercise 3
class Snakes:
def __init__(self):
self.venomous = True
class Snakes:
def __init__(self):
self.__venomous = True
2. An instance variable can be private when its name starts with __ , but don't forget that
such a property is still accessible from outside the class using a mangled name
constructed as _ClassName__PrivatePropertyName .
3. A class variable is a property which exists in exactly one copy, and doesn't need any
created object to be accessible. Such variables are not shown as __dict__ content.
All a class's class variables are stored inside a dedicated dictionary named __dict__ ,
contained in every class separately.
For example:
class Sample:
gamma = 0 # Class variable.
def __init__(self):
self.alpha = 1 # Instance variable.
self.__delta = 3 # Private instance variable.
obj = Sample()
obj.beta = 2 # Another instance variable (existing only inside the "obj" instance.
print(obj.__dict__)
output
Exercise 1
Which of the Python class properties are instance variables and which are class
variables? Which of them are private?
class Python:
population = 1
victims = 0
def __init__(self):
self.length_ft = 3
self.__venomous = False
population and victims are class variables, while length and __venomous are instance
variables (the latter is also private).
Exercise 2
You're going to negate the __venomous property of the version_2 object, ignoring the
fact that the property is private. How will you do this?
version_2 = Python()
version_2._Python__venomous = not version_2._Python__venomous
Exercise 3
Write an expression which checks if the version_2 object contains an instance property
named constrictor (yes, constrictor!.
hasattr(version_2, 'constrictor')
3. All classes (but not objects) contain a property named __name__ , which stores the
name of the class. Additionally, a property named __module__ stores the name of the
module in which the class has been declared, while the property named __bases__ is a
tuple containing a class's superclasses.
For example:
class Sample:
def __init__(self):
self.name = Sample.__name__
def myself(self):
print("My name is " + self.name + " living in a " + Sample.__module__)
obj = Sample()
obj.myself()
output
Exercise 1
The declaration of the Snake class is given below. Enrich the class with a method named
increment() , adding 1 to the __victims property.
class Snake:
def __init__(self):
self.victims = 0`
class Snake:
def __init__(self):
self.victims = 0
def increment(self):
self.victims += 1
Exercise 2
Redefine the Snake class constructor so that is has a parameter to initialize the victims
field with a value passed to the object during construction.
class Snake:
def __init__(self, victims):
self.victims = victims
Exercise 3
class Snake:
pass
class Python(Snake):
pass
Key takeaways
1. A method named __str__() is responsible for converting an object's contents into a
(more or less) readable string. You can redefine it if you want your object to be able to
present itself in a more elegant form. For example:
class Mouse:
def __init__(self, name):
self.my_name = name
def __str__(self):
return self.my_name
the_mouse = Mouse('mickey')
print(the_mouse) # Prints "mickey".
class LabMouse(Mouse):
pass
class Mouse:
pass
class LabMouse(Mouse):
pass
mickey = Mouse()
print(isinstance(mickey, Mouse), isinstance(mickey, LabMouse)) # Prints "True Fals
4. A operator called is checks if two variables refer to the same object. For example:
class Mouse:
pass
mickey = Mouse()
minnie = Mouse()
cloned_mickey = mickey
print(mickey is minnie, mickey is cloned_mickey) # Prints "False True".
class Mouse:
def __str__(self):
return "Mouse"
class LabMouse(Mouse):
def __str__(self):
return "Laboratory " + super().__str__()
doctor_mouse = LabMouse();
print(doctor_mouse) # Prints "Laboratory Mouse".
6. Methods as well as instance and class variables defined in a superclass are
automatically inherited by their subclasses. For example:
class Mouse:
Population = 0
def __init__(self, name):
Mouse.Population += 1
self.name = name
def __str__(self):
return "Hi, my name is " + self.name
class LabMouse(Mouse):
pass
class Mouse:
def __init__(self, name):
self.name = name
def __str__(self):
return "My name is " + self.name
class AncientMouse(Mouse):
def __str__(self):
return "Meum nomen est " + self.name
mus = AncientMouse("Caesar") # Prints "Meum nomen est Caesar"
print(mus)
Exercises
Scenario
Assume that the following piece of code has been successfully executed:
class Dog:
kennel = 0
def __init__(self, breed):
self.breed = breed
Dog.kennel += 1
def __str__(self):
return self.breed + " says: Woof!"
class SheepDog(Dog):
def __str__(self):
return super().__str__() + " Don't run away, Little Lamb!"
class GuardDog(Dog):
def __str__(self):
return super().__str__() + " Stay where you are, Mister Intruder!"
rocky = SheepDog("Collie")
luna = GuardDog("Dobermann")
Exercise 1
print(rocky)
`print(luna)
Exercise 2
Exercise 3
True False 2
Exercise 4
Define a SheepDog 's subclass named LowlandDog , and equip it with an __str__() method
overriding an inherited method of the same name. The new dog's __str__() method
should return the string "Woof! I don't like mountains!" .
class LowlandDog(SheepDog):
def __str__(self):
return Dog.__str__(self) + " I don't like mountains!"
4. The exception classes can be extended to enrich them with new capabilities, or to
adopt their traits to newly defined exceptions.
For example:
try:
assert __name__ == "__main__"
except:
print("fail", end=' ')
else:
print("success", end=' ')
finally:
print("done")
Exercise 1
import math
try:
print(math.sqrt(9))
except ValueError:
print("inf")
else:
print("fine")
3.0 fine
Exercise 2
import math
try: print(math.sqrt(-9))
except ValueError:
print("inf")
else:
print("fine")
finally:
print("the end")
Exercise 3
import math
class NewValueError(ValueError):
def __init__(self, name, color, state):
self.data = (name, color, state)
try:
raise NewValueError("Enemy warning", "Red alert", "High readiness")
except NewValueError as nve:
for arg in nve.args:
print(arg, end='! ')