Values

Python: everything is an object

An object is some value + its operations bundled together - we will see below

Numbers

Integers


In [1]:
(-1 + 0) * 3 ** (3 - 1) / (1 + 2)


Out[1]:
-3

Warning: by default division is integer division in Python 2!


In [2]:
1 / 2


Out[2]:
0

In [3]:
# an integer is an object, operators are accessible via "magic methods" - magic because of their name and the translation
1 - 2
print 'sub', (1).__sub__(2)
3 / 2
print 'div', (3).__div__(2)
print 'truediv', (3).__truediv__(2)  # true division is in the __future__
# from __future__ import division


sub -1
div 1
truediv 1.5

Floats


In [4]:
1.2


Out[4]:
1.2

In [5]:
2 / 3.


Out[5]:
0.6666666666666666

Warning: they are inexact representations of the number:


In [6]:
0.1


Out[6]:
0.1

In [7]:
0.1 * 8


Out[7]:
0.8

In [8]:
0.1 + 0.1 + 0.1 + 0.1 + 0.1 + 0.1 + 0.1 + 0.1


Out[8]:
0.7999999999999999

In [9]:
# float -> integet
int(1.9)


Out[9]:
1

In [10]:
import math
int(math.ceil(1.5))


Out[10]:
2

In [11]:
math.asin(0.5) * 6


Out[11]:
3.1415926535897936

In [12]:
# integer -> float
float(1)


Out[12]:
1.0

Sequences

Tuples


In [13]:
(1, 2)


Out[13]:
(1, 2)

In [14]:
# corner case awkwardness, tuple with only one item: mandatory comma inside paren
(1,)


Out[14]:
(1,)

In [15]:
# arbitrary nesting:
((2, (3, 4, 5)), 2)


Out[15]:
((2, (3, 4, 5)), 2)

operations


In [16]:
len((1, 2, 3))


Out[16]:
3

In [17]:
len(((2, (3, 4, 5)), 2))


Out[17]:
2
concatenation

In [18]:
(1, 2, 3) + (5, 6)


Out[18]:
(1, 2, 3, 5, 6)

In [19]:
# multiplication = self concatenation
(1, 2) * 3


Out[19]:
(1, 2, 1, 2, 1, 2)
indexing

In [20]:
(1, 2, 3)[1]


Out[20]:
2
slicing

In [21]:
(1, 2, 3)[1:2]


Out[21]:
(2,)

In [22]:
(1, 2, 3)[1:]


Out[22]:
(2, 3)

In [23]:
(1, 2, 3)[:2]


Out[23]:
(1, 2)

In [24]:
(1, 2, 3)[:]


Out[24]:
(1, 2, 3)

List comprehension - limited support for set-builder notation: {f(x) | x in Set : pred(x)}


In [25]:
tuple(x * 2 for x in (1, 2, 3, 9) if x > 2)


Out[25]:
(6, 18)

In [26]:
sum(1 for x in (1, 2, 3, 5, 2, 11, 4, 1, -1) if x < 3)


Out[26]:
5

Strings

Operations on strings


In [27]:
# concatenation
u'string1' + u'string2'


Out[27]:
u'string1string2'

In [28]:
# Indexing - extracting single "character"
u'string'[1]


Out[28]:
u't'

In [29]:
# extracting substring (slicing)
u'string'[1:3]


Out[29]:
u'tr'

In [30]:
# more slicing
u'string'[1:]


Out[30]:
u'tring'

In [31]:
# even more slicing
u'string'[:3]


Out[31]:
u'str'

In [32]:
# check for substring
(u'str' in u'string', u'sing' in u'string')


Out[32]:
(True, False)

In [33]:
# converting a string to a tuple of its characters
tuple(u'string')


Out[33]:
(u's', u't', u'r', u'i', u'n', u'g')

In [34]:
# converting something to a string
str(3)


Out[34]:
'3'

In [35]:
str(str)


Out[35]:
"<type 'str'>"

In [36]:
str(u'tuple')


Out[36]:
'tuple'

In [37]:
str(max)


Out[37]:
'<built-in function max>'

In [38]:
# another, subtly different way to get at the string representation of something that sometimes works, when str does not:
repr(max)


Out[38]:
'<built-in function max>'

In [39]:
# embed values into strings
u'The {} opened {} {}'.format(u'drunkard', 2, u'bottles')


Out[39]:
u'The drunkard opened 2 bottles'

More than one string type - source of problem!

Python 2: there are two string types,

from __future__ import unicode_literals

changes the default meaning of strings to be unicode instead of byte string

  • string of bytes

In [40]:
'árvíztűrő tükörfúrógép'


Out[40]:
'\xc3\xa1rv\xc3\xadzt\xc5\xb1r\xc5\x91 t\xc3\xbck\xc3\xb6rf\xc3\xbar\xc3\xb3g\xc3\xa9p'

In [41]:
len('árvíztűrő tükörfúrógép')


Out[41]:
31

In [42]:
print 'árvíztűrő tükörfúrógép'


árvíztűrő tükörfúrógép
  • string of "unicode code points"

In [43]:
u'árvíztűrő tükörfúrógép'


Out[43]:
u'\xe1rv\xedzt\u0171r\u0151 t\xfck\xf6rf\xfar\xf3g\xe9p'

In [44]:
len(u'árvíztűrő tükörfúrógép')


Out[44]:
22

In [45]:
print u'árvíztűrő tükörfúrógép'


árvíztűrő tükörfúrógép
conversion between byte-string an unicode string:

In [47]:
str(u'árvíztűrő tükörfúrógép')


---------------------------------------------------------------------------
UnicodeEncodeError                        Traceback (most recent call last)
<ipython-input-47-e866397d4bfd> in <module>()
----> 1 str(u'árvíztűrő tükörfúrógép')

UnicodeEncodeError: 'ascii' codec can't encode character u'\xe1' in position 0: ordinal not in range(128)

In [48]:
'\xc3\xa1rv\xc3\xadzt\xc5\xb1r\xc5\x91 t\xc3\xbck\xc3\xb6rf\xc3\xbar\xc3\xb3g\xc3\xa9p'.decode('utf-8')


Out[48]:
u'\xe1rv\xedzt\u0171r\u0151 t\xfck\xf6rf\xfar\xf3g\xe9p'

In [49]:
u'árvíztűrő tükörfúrógép'.encode('utf-8')


Out[49]:
'\xc3\xa1rv\xc3\xadzt\xc5\xb1r\xc5\x91 t\xc3\xbck\xc3\xb6rf\xc3\xbar\xc3\xb3g\xc3\xa9p'

Alternative ways to write strings:


In [50]:
u'''
multiline
strings
'''


Out[50]:
u'\nmultiline\nstrings\n'

In [51]:
u"in double-quote"


Out[51]:
u'in double-quote'

In [52]:
u"""
multiline 
strings in double-quote
"""


Out[52]:
u'\nmultiline \nstrings in double-quote\n'

In [53]:
print u"""
multiline 
strings in double-quote
"""


multiline 
strings in double-quote

Names


In [54]:
a_name = u'árvíztűrő tükörfúrógép'

In [55]:
print a_name


árvíztűrő tükörfúrógép

In [56]:
len(a_name)


Out[56]:
22

In [57]:
print a_name + u' ' + a_name


árvíztűrő tükörfúrógép árvíztűrő tükörfúrógép

In [58]:
print a_name[2:5]


víz

Functions

built in functions/functions in standard library - see https://docs.python.org/2/library/functions.html


In [59]:
def fact(n):
    ''' n! '''
    return n * fact(n - 1) if n > 1 else 1

assert fact(3) == 6
assert fact(4) == 24

In [60]:
def sin0(x):
    ''' sin calculated by Taylor series for sin around 0 '''
    return  x - (1./fact(3))*x**3 + (1./fact(5))*x**5 - (1./fact(7))*x**7 # + (1./fact(9))*x**9

assert sin0(0) == 0
# check at angle 30 - should be very close to 0.5
error = abs(sin0(3.14159265358 / 6) - 0.5)
assert error < 1e-8, 'error is {} - greater than expected!'.format(error)

In [61]:
import math

def sindiff(a):
    return math.sin(a) - sin0(a)

print sindiff(0.1)
print sindiff(math.pi / 5)
print sindiff(math.pi / 4)
print sindiff(math.pi / 3)
print sindiff(math.pi / 2)

print sindiff(-math.pi)
print sindiff(2 * math.pi)
print sindiff(3 * math.pi)


2.74780198595e-15
4.19081288561e-08
3.11611369375e-07
4.13212806594e-06
0.000156898600501
-0.0752206159036
30.1591274102
821.005131035

Binomials


In [62]:
def nk(n, k):
    ''' select k from n values '''
    return fact(n) / (fact(k) * fact(n - k))

In [63]:
# Pascal's triangle - n-th row = nk for all k
def pascal(n):
    return add_tuples(shift_left(pascal(n - 1)), shift_right(pascal(n - 1))) if n > 0 else (1,)

def shift_left(t):
    return t + (0,)

def shift_right(t):
    return (0,) + t

def add_tuples(t1, t2):
    return (t1[0] + t2[0],) + add_tuples(t1[1:], t2[1:]) if t1 else ()

pascal(12)


Out[63]:
(1, 12, 66, 220, 495, 792, 924, 792, 495, 220, 66, 12, 1)

In [64]:
nk(12, 3)


Out[64]:
220

In [65]:
# problem: it slows down radically as n goes up - time complexity
pascal(20)


Out[65]:
(1,
 20,
 190,
 1140,
 4845,
 15504,
 38760,
 77520,
 125970,
 167960,
 184756,
 167960,
 125970,
 77520,
 38760,
 15504,
 4845,
 1140,
 190,
 20,
 1)

In [66]:
# fix: calculate the previous value only once
def fast_pascal(n):
    if n > 0:
        prev = fast_pascal(n - 1)
        return add_tuples(shift_left(prev), shift_right(prev))
    else:
        return (1,)

fast_pascal(30)


Out[66]:
(1,
 30,
 435,
 4060,
 27405,
 142506,
 593775,
 2035800,
 5852925,
 14307150,
 30045015,
 54627300,
 86493225,
 119759850,
 145422675,
 155117520,
 145422675,
 119759850,
 86493225,
 54627300,
 30045015,
 14307150,
 5852925,
 2035800,
 593775,
 142506,
 27405,
 4060,
 435,
 30,
 1)

Higher order functions - functions taking functions as parameters

replace operation + for composing new row with another function


In [67]:
def op_pascal(op, n):
    if n > 0:
        prev = op_pascal(op, n - 1)
        return compose_tuples(op, shift_left(prev), shift_right(prev))
    else:
        return (1,)

def compose_tuples(op, t1, t2):
    return (op(t1[0], t2[0]),) + compose_tuples(op, t1[1:], t2[1:]) if t1 else ()

# ops to try:
def addp(a, b):
    return a + b + 1

def minp(a, b):
    return min(a, b) + 1

def minpx2(a, b):
    return (min(a, b) + 1) * 2

op_pascal(minpx2, 8)


Out[67]:
(2, 6, 14, 30, 62, 30, 14, 6, 2)

In [68]:
import operator

def fact2(n):
    return reduce(operator.mul, range(1, n + 1))

assert fact2(5) == fact(5)

print fact2(5), fact2(6), operator.mul


120 720 <built-in function mul>

Creating objects


In [69]:
class Auto(object):
    
    def __init__(self, brand, type, color):
        self.brand = brand
        self.type = type
        self.color = color
    
    def repaint(self, color):
        return Auto(self.brand, self.type, color)

    def __repr__(self):
        return u'{0.__class__.__name__}({0.brand}, {0.type}, {0.color})'.format(self)

# Auto is a ~function that returns Auto objects

In [70]:
yellow_suzuki = Auto('Suzuki', 'Ignis', 'yellow')
print yellow_suzuki
print yellow_suzuki.color


Auto(Suzuki, Ignis, yellow)
yellow

In [73]:
red_suzuki = yellow_suzuki.repaint('red')
red_suzuki


Out[73]:
Auto(Suzuki, Ignis, red)

Imperative programming

= I am a micromanaging boss, I tell you what to do, and how

Variables

Mutable data

lists - like tuples, but changeable


In [74]:
l = [1, 2, (u'tuple-string',)]
l


Out[74]:
[1, 2, (u'tuple-string',)]

In [75]:
l[2] = u'I do not like tuples of length 1 syntax'
l


Out[75]:
[1, 2, u'I do not like tuples of length 1 syntax']

In [76]:
del l[1:]
l


Out[76]:
[1]

In [77]:
l.append(u'new item')
l


Out[77]:
[1, u'new item']

Re-assigned names


In [78]:
data = (1, 2)
data


Out[78]:
(1, 2)

In [79]:
old_data = data
data = data + (3, 4)
print old_data
print data


(1, 2)
(1, 2, 3, 4)

Loops


In [80]:
i = 5
l = []
while i < 10:
    l.append(i)
    i = i + 1
print l


[5, 6, 7, 8, 9]

In [81]:
l = []
for i in range(5, 10):
    l.append(i * 3 + 1)
print l


[16, 19, 22, 25, 28]

State

why? distributing responsibility

Generators - sequences - with state


In [82]:
# this is list comprehension as above!
g = (x*2 for x in (1, 2, 3, 9) if x > 2)
g


Out[82]:
<generator object <genexpr> at 0x1fdac30>

In [83]:
tuple(g)


Out[83]:
(6, 18)

In [84]:
tuple(g)


Out[84]:
()

In [85]:
g = (x*2 for x in (1, 2, 3, 9) if x > 2)
g


Out[85]:
<generator object <genexpr> at 0x1fdacd0>

In [86]:
next(g)


Out[86]:
6

In [87]:
next(g)


Out[87]:
18

In [89]:
next(g)


---------------------------------------------------------------------------
StopIteration                             Traceback (most recent call last)
<ipython-input-89-5f315c5de15b> in <module>()
----> 1 next(g)

StopIteration: 

In [90]:
g = (x*2 for x in (1, 2, 3, 9) if x > 2)
for x in g:
    print x


6
18

In [91]:
def gen2x(nums):
    for x in nums:
        if x > 2:
            yield x * 2

gen2x((1, 2, 4, 0, 1, 12, 1))


Out[91]:
<generator object gen2x at 0x1fdae60>

In [92]:
tuple(gen2x((1, 2, 4, 0, 1, 12, 1)))


Out[92]:
(8, 24)