Das with
Statement¶
[1]:
def f():
l = [1, 2, 3]
[2]:
f()
[3]:
def f():
try:
openfile = open('/etc/passwd')
except ValueError as e:
# hanlde err
raise
finally:
openfile.close()
f()
[4]:
def f():
with open('/etc/passwd') as openfile:
# do soemthing with openfile
# no closing necessary
pass # Python requires me to write at least one syntactically correct statement
import zipfile
¶
[5]:
import zipfile
[6]:
zf = zipfile.ZipFile('demo.zip')
Read Contents …¶
[7]:
zf.namelist()
[7]:
['tmp/zipdemo/', 'tmp/zipdemo/group', 'tmp/zipdemo/passwd']
Extract One Member¶
[8]:
extracted_name = zf.extract(member='tmp/zipdemo/group', path='/tmp/my-extraction-directory')
extracted_name
[8]:
'/tmp/my-extraction-directory/tmp/zipdemo/group'
All in One, Using with
¶
[9]:
with zipfile.ZipFile('demo.zip') as zf:
print(zf.namelist())
print(zf.extract(member='tmp/zipdemo/group', path='/tmp/my-extraction-directory'))
['tmp/zipdemo/', 'tmp/zipdemo/group', 'tmp/zipdemo/passwd']
/tmp/my-extraction-directory/tmp/zipdemo/group
Classes¶
Question 31
Select the true statements:
(select all that apply)
The
class
keyword marks the beginning of the class definition
An object cannot contain any references to other objects
A class may define an object
A constructor is used to instantiate an object
An object variable is a variable that is stored separately in every object
[10]:
class Person:
pass
[11]:
p = Person() # instantiation
p.first = 'Joerg'
p.last = 'Faschingbauer'
p.address = 'Prankergasse 33, 8020 Graz'
[12]:
class Person:
def __init__(self, first, last, address):
self.first = first
self.last = last
self.address = address
p = Person('Joerg', 'Faschingbauer', 'Prankergasse 33, 8020 Graz') # instantiation
[13]:
p.first
[13]:
'Joerg'
[14]:
isinstance(p, Person)
[14]:
True
[15]:
isinstance(p, str)
[15]:
False
[16]:
p.__dict__
[16]:
{'first': 'Joerg',
'last': 'Faschingbauer',
'address': 'Prankergasse 33, 8020 Graz'}
Inheritance¶
Question 32
Select the true statements:
(select all that apply)
Inheritance means passing attributes and methods from a superclass to a subclass
issubclass(class1, class2)
is an example of a function that returnsTrue
ifclass2
is a subclass ofclass1
Multiple inheritance means that a class has more than one superclass
Polymorphism is the situation in which a subclass is able to modify its superclass behavior
A single inheritance is always more difficult to maintain than a multiple inheritance
[17]:
class Employee(Person):
def __init__(self, first, last, address, salary):
self.salary = salary
super().__init__(first, last, address)
[18]:
e = Employee('Selina', 'Orgl', 'Somewhere 666, 8010 Graz', 2000) # instantiation
[19]:
e.first
[19]:
'Selina'
[20]:
e.salary
[20]:
2000
[21]:
e.__dict__
[21]:
{'salary': 2000,
'first': 'Selina',
'last': 'Orgl',
'address': 'Somewhere 666, 8010 Graz'}
[22]:
isinstance(e, Employee)
[22]:
True
[23]:
isinstance(e, Person)
[23]:
True
[24]:
issubclass(Employee, Person)
[24]:
True
Functionality: Methods¶
[25]:
import time
class Person:
def __init__(self, first, last, address):
self.first = first
self.last = last
self.address = address
self.birth = time.time()
def die(self):
self.death = time.time()
def lifetime(self):
return self.death - self.birth
Employee inherits everything but salary
[26]:
class Employee(Person):
def __init__(self, first, last, address, salary):
self.salary = salary
super().__init__(first, last, address)
[27]:
joerg = Person('Joerg', 'Faschingbauer', 'Prankergasse 33, 8020 Graz')
[28]:
joerg.birth
[28]:
1622102062.4778333
joerg.die() joerg.death
[29]:
joerg.die()
joerg.lifetime()
[29]:
0.019427776336669922
[30]:
selina = Employee('Selina', 'Orgl', 'Somewhere 666, 8010 Graz', 2000)
[31]:
selina.die()
selina.lifetime()
[31]:
0.009679079055786133
[32]:
daniel = Employee('Daniel', 'Ortner', 'Blah 42', 1000)
type(daniel)
[32]:
__main__.Employee
[33]:
isinstance(daniel, Employee)
[33]:
True
[34]:
isinstance(daniel, Person)
[34]:
True
Class Attributes vs. Instance Attributes (not Variables)¶
Instance Attributes¶
[35]:
daniel.first
[35]:
'Daniel'
[36]:
selina.first
[36]:
'Selina'
[37]:
joerg.first
[37]:
'Joerg'
Class Attributes¶
How many Employees exist?
[38]:
class Employee(Person):
num_employees = 0 # class attribute
def __init__(self, first, last, address, salary):
self.salary = salary
Employee.num_employees += 1 # class attribute can be accessed via class Employee
super().__init__(first, last, address)
def die(self):
'''Acts like Person.die(), and in addition keeps track of num. employees'''
self.num_employees -= 1 # class attributes can be access via any object of the class
super().die()
[39]:
selina = Employee('Selina', 'Orgl', 'Somewhere 666, 8010 Graz', 2000)
daniel = Employee('Daniel', 'Ortner', 'Blah 42', 1000)
[40]:
Employee.num_employees
[40]:
2
[41]:
selina.die()
[42]:
Employee.num_employees
[42]:
2
Public, Protected, Private¶
“Private” is actually only name mangling
[43]:
class Person:
def __init__(self, first, last, address):
self.__first = first
self.__last = last
self.__address = address
p = Person('Joerg', 'Faschingbauer', 'Prankergasse 33, 8020 Graz')
[44]:
try:
p.__first
except Exception as e:
print(e)
'Person' object has no attribute '__first'
[45]:
p.__dict__
[45]:
{'_Person__first': 'Joerg',
'_Person__last': 'Faschingbauer',
'_Person__address': 'Prankergasse 33, 8020 Graz'}
[46]:
p._Person__first
[46]:
'Joerg'
This is maybe “Protected”?
[47]:
class Person:
def __init__(self, first, last, address):
self._first = first
self._last = last
self._address = address
p = Person('Joerg', 'Faschingbauer', 'Prankergasse 33, 8020 Graz')
[48]:
p.__dict__
[48]:
{'_first': 'Joerg',
'_last': 'Faschingbauer',
'_address': 'Prankergasse 33, 8020 Graz'}
Properties¶
[49]:
class Person:
def __init__(self, first, last, address):
self._first = first
self._last = last
self._address = address
@property
def first(self):
return self._first
@first.setter
def first(self, name):
'Only allow Persons with firstname Joerg to be renamed'
if self._first == 'Joerg':
self._first = name
else:
raise RuntimeError('nix rename')
p = Person('Joerg', 'Faschingbauer', 'Prankergasse 33, 8020 Graz')
[50]:
p.first
[50]:
'Joerg'
[51]:
p.first = 'Eugenie'
Functions, Positional and Keyword Arguments¶
[52]:
def velocity(length_m, time_s, do_debug):
val = length_m/time_s
if do_debug:
print('velocity=', val)
return val
Positional Arguments¶
[53]:
velocity(5, 10, True)
velocity= 0.5
[53]:
0.5
[54]:
velocity(10, 5, False)
[54]:
2.0
Keyword Arguments¶
[55]:
velocity(time_s=10, length_m=5, do_debug=False)
[55]:
0.5
Mixing Positional and Keyword Arguments¶
[56]:
velocity(5, do_debug=True, time_s=10) # keyword args only after positional
velocity= 0.5
[56]:
0.5
The range()
Function¶
[57]:
row = ['Language', 'bloh', '', '666', '']
[58]:
len(row)
[58]:
5
[59]:
for i in range(2, len(row)):
print(i)
2
3
4
[60]:
r = range(3) # same as range(0,3)
r
[60]:
range(0, 3)
[61]:
it = iter(r)
[62]:
next(it)
[62]:
0
[63]:
next(it)
[63]:
1
[64]:
next(it)
[64]:
2
[65]:
try:
next(it)
except StopIteration:
print('there is nothing more')
there is nothing more
This is exactly the same a using the for
loop to iterate over a range (or anything that is iterable)
[66]:
for element in range(3):
print(element)
0
1
2
Functional Programming, Iteration, yield
, map()
, filter()
, …¶
map()
¶
[67]:
l = ['1.2', '3.4', '666.42']
Convert strings to floats:
[68]:
l_float = []
for element in l:
l_float.append(float(element))
l_float
[68]:
[1.2, 3.4, 666.42]
Do the same using a list comprehension
[69]:
l_float = [float(element) for element in l]
l_float
[69]:
[1.2, 3.4, 666.42]
Do the same using map()
[70]:
l_float = map(float, l)
for el in l_float:
print(el)
1.2
3.4
666.42
Do the same using a generator expression
[71]:
l_float = (float(element) for element in l)
l_float
[71]:
<generator object <genexpr> at 0x7fd9881b04a0>
[72]:
for el in l_float:
print(el)
1.2
3.4
666.42
Iterable¶
list
is iterable
[73]:
l = [0, 1, 2]
for element in l:
print(element)
0
1
2
str
is iterable
[74]:
s = 'abc'
for element in s:
print(element)
a
b
c
range()
objects are iterable
[75]:
r = range(3)
for element in r:
print(element)
0
1
2
dict
is iterable
[76]:
d = {1:'one', 2: 'two'}
for element in d:
print(element)
1
2
list()
, and iterable?¶
What does the list
constructor do with its parameter if you pass it one?
[77]:
l = list()
l
[77]:
[]
[78]:
r = range(3)
list(r)
[78]:
[0, 1, 2]
[79]:
list('abc')
[79]:
['a', 'b', 'c']
[80]:
l = [1,2,3]
for element in l:
print(element)
1
2
3
[81]:
list(l)
[81]:
[1, 2, 3]
Tuple Unpacking and the Rest¶
[82]:
l = [1, 2, 3, 4, 5, 6]
a, b, *rest = l
print(a, b, rest)
1 2 [3, 4, 5, 6]
Decorators, etc.¶
[83]:
import functools
def debug(func):
@functools.wraps(func)
def wrapper(*args, **kwargs):
print(f'{func.__name__} called: args={args}, kwargs={kwargs}')
return func(*args, **kwargs)
return wrapper
# add = debug(add)
@debug
def add(a, b):
'das ist der docstring der extrem komplexen funktion add()'
return a+b
@debug
def square(a):
return a**2
print(f'function add has name {add.__name__}, and is documented like so: {add.__doc__}')
print('add (args)', add(1, 2))
print('add (kwargs)', add(a=1, b=2))
print('square', square(10))
function add has name add, and is documented like so: das ist der docstring der extrem komplexen funktion add()
add called: args=(1, 2), kwargs={}
add (args) 3
add called: args=(), kwargs={'a': 1, 'b': 2}
add (kwargs) 3
square called: args=(10,), kwargs={}
square 100
NoneType
and None
¶
Remember NULL
in C?
[84]:
a = None
[85]:
type(a)
[85]:
NoneType
[86]:
d = { 1:'one', 2:'two' }
d[1]
[86]:
'one'
Index Operator crashes if key not there …
[87]:
try:
d[3]
except KeyError as e:
print(type(e), e)
<class 'KeyError'> 3
[88]:
value = d.get(3)
print(value)
None
[89]:
value = d.get(3)
if value is None:
print('not there')
else:
print(value)
not there
[90]:
def f():
pass # does nothing, not even return anything
result = f()
print(result)
None
File I/O¶
[91]:
f = open('testfile.txt')
[92]:
f.read()
[92]:
'zeile 1\nzeile 2\nzeile 3\n'
[93]:
f.seek(0)
[93]:
0
[94]:
f.readline()
[94]:
'zeile 1\n'
[95]:
f.readline()
[95]:
'zeile 2\n'
[96]:
f.readline()
[96]:
'zeile 3\n'
[97]:
f.readline() # EOF
[97]:
''
[98]:
f.seek(0)
[98]:
0
Files are iterable
[99]:
for line in f:
print(line)
zeile 1
zeile 2
zeile 3
Iteration, yield
, Recursion¶
Recursion¶
[106]:
def nth_fibo(n):
if n < 1:
raise RuntimeError('No fibonacci number below 1')
if n == 1 or n == 2:
return 1
return nth_fibo(n-1) + nth_fibo(n-2)
[107]:
nth_fibo(7)
[107]:
13
[108]:
nth_fibo(2)
[108]:
1
[113]:
try:
nth_fibo(0)
except RuntimeError as e:
print(e)
No fibonacci number below 1
Iteratively Calculating Fibonacci Numbers -> yield
¶
[119]:
N = 10 - 2 # 2 to accomodate first and second
first, second = 1, 1
print(first)
print(second)
while N > 0:
N -= 1
third = first + second
print(third)
first, second = second, third
1
1
2
3
5
8
13
21
34
55
[121]:
first, second = 1, 1
print(first)
print(second)
for i in range(8):
third = first + second
print(third)
first, second = second, third
1
1
2
3
5
8
13
21
34
55
Function to calculate a list of first N fibonacci numbers …
[122]:
def fibo(n):
'Returns a list of first n fibonacci numbers'
nums = []
first, second = 1, 1
nums.append(first)
nums.append(second)
for i in range(n-2):
third = first + second
nums.append(third)
first, second = second, third
return nums
# user can do what she wants with those numbers. for example, print them
for n in fibo(10):
print(n)
1
1
2
3
5
8
13
21
34
55
[123]:
def fibo(n):
'Returns an iterable that generates the first n fibonacci numbers'
first, second = 1, 1
yield first
yield second
for i in range(n-2):
third = first + second
yield third
first, second = second, third
# user can do what she wants with those numbers. for example, print them
for n in fibo(10):
print(n)
1
1
2
3
5
8
13
21
34
55
[124]:
f = fibo(4)
f
[124]:
<generator object fibo at 0x7fd9801cec10>
[125]:
it = iter(f)
[126]:
next(it)
[126]:
1
[127]:
next(it)
[127]:
1
[128]:
next(it)
[128]:
2
[129]:
next(it)
[129]:
3
[131]:
try:
next(it)
except StopIteration:
pass
[ ]:
map()
, filter()
, zip()
, enumerate()
…¶
map()
, and several other ways to do the same¶
[132]:
l = ['1.2', '3.4', '666.0']
Use a list comprehension to transform l
to floats
[134]:
lf = [float(element) for element in l]
lf
[134]:
[1.2, 3.4, 666.0]
Use map()
to do the same
[135]:
lf = map(float, l)
lf
[135]:
<map at 0x7fd9801b8d60>
Ah, have to iterate over the iterable to see the generated items
[136]:
for e in lf:
print(e)
1.2
3.4
666.0
Use a generator expression to do the same
[138]:
lf = (float(e) for e in l)
lf
[138]:
<generator object <genexpr> at 0x7fd9801b4900>
[139]:
for e in lf:
print(e)
1.2
3.4
666.0
zip()
¶
[140]:
l1 = [1, 2, 3, 4]
l2 = ['one', 'two', 'three', 'four']
[142]:
zipped = zip(l1, l2)
zipped
[142]:
<zip at 0x7fd98014cd40>
[143]:
for element in zipped:
print(element)
(1, 'one')
(2, 'two')
(3, 'three')
(4, 'four')
[144]:
dict(zip(l1, l2))
[144]:
{1: 'one', 2: 'two', 3: 'three', 4: 'four'}
What if both are not of equal lengths?
[145]:
l1 = [1, 2, 3, 4, 5]
l2 = ['one', 'two', 'three', 'four']
[146]:
for l, r in zip(l1, l2):
print(l, r)
1 one
2 two
3 three
4 four
filter()
, and several other methods to do the same¶
[147]:
numbers = range(10)
We want even numbers only:
[148]:
def even(n):
return n%2 == 0
for e in filter(even, numbers):
print(e)
0
2
4
6
8
[150]:
for e in filter(lambda n: n%2 == 0, range(10)):
print(e)
0
2
4
6
8
Use a list comprehension to do the same
[158]:
[element for element in range(10) if element%2 == 0]
[158]:
[0, 2, 4, 6, 8]
Use a generator expression to achieve ultimate beauty and performance
[160]:
gen = (element for element in range(10) if element%2 == 0)
for e in gen:
print(e)
0
2
4
6
8
enumerate()
¶
[151]:
l = ['one', 'two', 'three', 'four']
for element in enumerate(l):
print(element)
(0, 'one')
(1, 'two')
(2, 'three')
(3, 'four')
[152]:
l = ['one', 'two', 'three', 'four']
for sequenceno, s in enumerate(l):
print(sequenceno, s)
0 one
1 two
2 three
3 four
[153]:
l = ['one', 'two', 'three', 'four']
for sequenceno, s in enumerate(l, 1):
print(sequenceno, s)
1 one
2 two
3 three
4 four
[154]:
l = ['one', 'two', 'three', 'four']
for kv_pair in enumerate(l, 1):
print(kv_pair)
(1, 'one')
(2, 'two')
(3, 'three')
(4, 'four')
[155]:
dict(enumerate(l, 1))
[155]:
{1: 'one', 2: 'two', 3: 'three', 4: 'four'}
OSError
, errno
¶
[164]:
import os
try:
os.remove('/etc/passwd')
except PermissionError as e:
print(e)
print(f'errno is, btw, {e.errno}')
[Errno 13] Permission denied: '/etc/passwd'
errno is, btw, 13
[168]:
import errno
errno.EACCES
[168]:
13
[169]:
issubclass(PermissionError, OSError)
[169]:
True
[170]:
issubclass(FileNotFoundError, OSError)
[170]:
True
The platform
Module, sys.path
¶
[171]:
import platform
[175]:
platform.architecture()
[175]:
('64bit', 'ELF')
[178]:
platform.machine()
[178]:
'x86_64'
[183]:
platform.processor()
[183]:
'x86_64'
[186]:
platform.version()
[186]:
'#1 SMP Wed Apr 21 13:18:33 UTC 2021'
[188]:
platform.python_implementation()
[188]:
'CPython'
[190]:
platform.python_version_tuple()
[190]:
('3', '9', '4')
Module search path
[191]:
import sys
sys.path
[191]:
['/home/jfasch/work/jfasch-home/trainings/log/detail/2021-05-25',
'/home/jfasch/work/openheating',
'/home/jfasch/work/jfasch-home',
'/home/jfasch/work/jfasch-home/trainings/log/detail/2021-05-25',
'/usr/lib64/python39.zip',
'/usr/lib64/python3.9',
'/usr/lib64/python3.9/lib-dynload',
'',
'/home/jfasch/venv/homepage/lib64/python3.9/site-packages',
'/home/jfasch/venv/homepage/lib/python3.9/site-packages',
'/home/jfasch/venv/homepage/lib64/python3.9/site-packages/IPython/extensions',
'/home/jfasch/.ipython']
os.path
¶
[193]:
import os.path
[194]:
os.path.exists('/etc/passwd')
[194]:
True
[196]:
os.path.isfile('/etc/passwd')
[196]:
True
[197]:
os.path.isdir('/etc/passwd')
[197]:
False
[198]:
os.sep
[198]:
'/'
[200]:
my_path = '..' + os.sep + 'lib'
my_path
[200]:
'../lib'
[201]:
my_path = os.path.join('..', 'lib')
my_path
[201]:
'../lib'
Exceptions und so (assert()
)¶
[202]:
try:
open('some-file-that-hopefully-does-not-exist')
except FileNotFoundError as e:
the_exception = e
[203]:
the_exception
[203]:
FileNotFoundError(2, 'No such file or directory')
[204]:
isinstance(the_exception, FileNotFoundError)
[204]:
True
[205]:
the_exception.__class__
[205]:
FileNotFoundError
[207]:
the_exception.__class__.__bases__
[207]:
(OSError,)
Ah , OSError
[209]:
OSError.__bases__
[209]:
(Exception,)
[210]:
Exception.__bases__
[210]:
(BaseException,)
[211]:
BaseException.__bases__
[211]:
(object,)
And StopIteration
? Where is that in the hierarchy?
[221]:
StopIteration.__bases__
[221]:
(Exception,)
AssertionError¶
“To assert” stands for “make sure that a condition holds”
[212]:
l = []
l.append(1)
l.append(2)
l.append(3)
Here the list must have three elements, we know that for sure.
Distrusting the list
class (Guido is incompetent)
[216]:
assert len(l) == 3 # well, that precondition holds
[219]:
# being too stupid for distrust altogether (I appended three items, not four)
try:
assert len(l) == 4
except AssertionError as e:
print('Shit happened:', type(e))
Shit happened: <class 'AssertionError'>
[220]:
AssertionError.__bases__
[220]:
(Exception,)
Random Questions¶
[1]:
for i in range(1,3):
print(i)
1
2
[2]:
my_list = [0 for i in range(1,3)]
my_list
[2]:
[0, 0]
Slicing¶
[3]:
s = 'abdefg'
s[1::2]
[3]:
'beg'
[5]:
s[1:]
[5]:
'bdefg'
OO¶
Note: self
is the object
[8]:
class Point:
def __init__(self, x, y):
self.x = x
self.y = y
p = Point(2, 5)
print(p.x, p.y)
2 5
How to stringify an object?
[11]:
i = 42
print(i)
42
[13]:
str(i)
[13]:
'42'
[15]:
i.__str__()
[15]:
'42'
And class Point
?
[16]:
p = Point(1, 2)
print(p)
<__main__.Point object at 0x7ffa9c2ac3a0>
[21]:
class Point:
def __init__(self, x, y):
self.x = x
self.y = y
def __str__(self):
return f'({self.x}, {self.y})'
p = Point(2, 5)
print(p)
(2, 5)
String Formatting¶
[22]:
formatstring = 'das ist ein ding: {}, und noch eins: {}'
[25]:
formatierter_string = formatstring.format(666, 'eugenie')
formatierter_string
[25]:
'das ist ein ding: 666, und noch eins: eugenie'
[28]:
formatstring = 'das ist ein ding: {a}, und noch eins: {b}'
formatstring.format(b=666, a='eugenie')
[28]:
'das ist ein ding: eugenie, und noch eins: 666'
[31]:
'das ist {a} ' + 'und {b} ist auch noch dabei'.format(a=666, b='eugenie')
[31]:
'das ist {a} und eugenie ist auch noch dabei'
import datetime
¶
[44]:
'{a}'.format(a=1)
[44]:
'1'
[46]:
'{a:5}'.format(a=1)
[46]:
' 1'
[48]:
'{a:<5}'.format(a=1)
[48]:
'1 '
[49]:
'{a:>5}'.format(a=1)
[49]:
' 1'
[50]:
'{a:02}'.format(a=1)
[50]:
'01'