Random Live Hacking¶
Data Types¶
Variables are just names that point to objects
Variables are not typed - the type is in the objects they points to
[1]:
i = 1
[2]:
type(i)
[2]:
int
[3]:
i = 1.5
[4]:
type(i)
[4]:
float
[5]:
i = [1,2,3]
[6]:
type(i)
[6]:
list
int
is also a variable - a name. It points to an object containing the type for all int objects.
[7]:
print(int)
<class 'int'>
What is the type of that int
type object? What’s the type of type objects, generally?
[8]:
print(type(int))
<class 'type'>
Aha …
[9]:
int('42')
[9]:
42
int
is just a name, so let another name point to the same object (type int
)
[10]:
franziska = int
[11]:
franziska('42')
[11]:
42
Assignments to int
are possible. Although rarely useful.
[12]:
int = 42
[13]:
try:
int('42')
except BaseException as e:
print(e)
'int' object is not callable
Remove the last name for type int
, so it is gone forever …
[14]:
del franziska
Ah, it is still there: 1
is an integer literal, so it has to carry a reference to its type. This comes to our rescue: we can restore the name int
to point to what is should.
[15]:
type(1)
[15]:
int
[16]:
int = type(1)
[17]:
int('42')
[17]:
42
Pooh!
Mutable, Immutable¶
[18]:
l1 = [1, 2, 3]
l2 = l1
print(hex(id(l1)))
print(hex(id(l2)))
0x7f03d41165f0
0x7f03d41165f0
[19]:
l1.append(4)
l1
[19]:
[1, 2, 3, 4]
[20]:
l2
[20]:
[1, 2, 3, 4]
Exception, demonstrated using dict access¶
[21]:
d = {1:'one', 2:'two'}
[22]:
d
[22]:
{1: 'one', 2: 'two'}
[23]:
d[1]
[23]:
'one'
[24]:
d[2]
[24]:
'two'
Here we access a nonexisting dictionary member, demonstrating how we react on the error that ensues. * Catch the exception by type (KeyError
) * Use the logging
module to format the output (stack trace etc.). See its docs for more.
[25]:
import sys
import logging
try:
d[3]
except KeyError as e:
print('Jessas:', e)
logging.exception('verdammt!')
ERROR:root:verdammt!
Traceback (most recent call last):
File "<ipython-input-25-bd3a1af9b474>", line 5, in <module>
d[3]
KeyError: 3
Jessas: 3
[26]:
print(type(KeyError))
<class 'type'>
All Exceptions are derived from BaseException
[27]:
issubclass(KeyError, BaseException)
[27]:
True
Even the ones that you define yourself have to be derived from BaseException
. Better yet, derive them from Exception
which should the base for all user-defined exceptions.
[28]:
try:
raise 'bummer!'
except BaseException as e:
print('Cannot raise str:', e)
Cannot raise str: exceptions must derive from BaseException
Indices and Slicing¶
[29]:
l = ['Peter', 'Paul', 'Mary']
[30]:
peter = l[0]
peter
[30]:
'Peter'
[31]:
peter[0:3]
[31]:
'Pet'
[32]:
l[0][0:3]
[32]:
'Pet'
[33]:
peter[:3]
[33]:
'Pet'
[34]:
l = [2,3,4]
l[0:0]
[34]:
[]
[35]:
l[0:0] = [0,1]
l
[35]:
[0, 1, 2, 3, 4]
for loops¶
Lists are perfectly iterable
[36]:
for item in ['blah', 'bloh', 'blech']:
print(item)
blah
bloh
blech
range
is not a list, but a generator. A list would contain (allocate in memory) all that it has. range
produces the next element on demand - as the for
loop iterates.
[37]:
for i in range(10):
print(i)
0
1
2
3
4
5
6
7
8
9
[38]:
for i in range(5,10):
print(i)
5
6
7
8
9
[39]:
range(10)
[39]:
range(0, 10)
[40]:
list(range(10))
[40]:
[0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
[41]:
r = range(10)
Iterator protocol¶
[42]:
iterator = iter(r)
iterator
[42]:
<range_iterator at 0x7f03d402e570>
[43]:
next(iterator)
next(iterator)
next(iterator)
next(iterator)
next(iterator)
next(iterator)
next(iterator)
next(iterator)
next(iterator)
[43]:
8
[44]:
next(iterator)
[44]:
9
[45]:
# final element already consumed, consume another one
try:
next(iterator)
except StopIteration as e:
print(e)