Python Language Intro (Part 2)

Agenda

  1. Language overview
  2. White space sensitivity
  3. Basic Types and Operations
  4. Statements & Control Structures
  5. Functions
  6. OOP (Classes, Methods, etc.)
  7. Immutable Sequence Types (Strings, Ranges, Tuples)
  8. Mutable data structures: Lists, Sets, Dictionaries
In [5]:
# by default, only the result of the last expression in a cell is displayed after evaluation.
# the following forces display of *all* self-standing expressions in a cell.

from IPython.core.interactiveshell import InteractiveShell
InteractiveShell.ast_node_interactivity = "all"

4. Statements & Control Structures

Assignment

In [2]:
# simple, single target assignment

a = 0
b = 'hello'
In [3]:
# can also assign to target "lists"

a, b, c = 0, 'hello', True
a
b
c
Out[3]:
0
Out[3]:
'hello'
Out[3]:
True
In [4]:
# note: expression on right is fully evaluated, then are assigned to
#       elements in the "target" list, from left to right

x, y, z = 1, 2, 3
x, y, z = x+y, y+z, x+y+z
x
y
z
Out[4]:
3
Out[4]:
5
Out[4]:
6
In [5]:
# easy python "swap"

a, b = 'apples', 'bananas'
a, b = b, a
a
b
Out[5]:
'bananas'
Out[5]:
'apples'
In [6]:
# note: order matters!

a, b, a = 1, 2, 3
a
b
Out[6]:
3
Out[6]:
2
In [7]:
# can also have multiple assignments in a row -- consistent with
# above: expression is evaluated first, then assigned to all targets
# from left to right (note: order matters!)

x = y = z = None
x
y
z

Augmented assignment

In [ ]:
a = 0
a += 2
a *= 3

pass

pass is the "do nothing" statement

In [ ]:
pass
In [ ]:
def foo():
    pass

if-else statements

In [11]:
from random import randint
dir(randint)
Out[11]:
['__call__',
 '__class__',
 '__delattr__',
 '__dir__',
 '__doc__',
 '__eq__',
 '__format__',
 '__func__',
 '__ge__',
 '__get__',
 '__getattribute__',
 '__gt__',
 '__hash__',
 '__init__',
 '__init_subclass__',
 '__le__',
 '__lt__',
 '__ne__',
 '__new__',
 '__reduce__',
 '__reduce_ex__',
 '__repr__',
 '__self__',
 '__setattr__',
 '__sizeof__',
 '__str__',
 '__subclasshook__']
In [ ]:
from random import randint
for i in range(20):
    score = randint(50, 100)
    grade = None
    if score >= 90:
        grade = 'A'
    elif score >= 80:
        grade = 'B'
    elif score >= 70:
        grade = 'C'
    elif score >= 60:
        grade = 'D'
    else:
        grade = 'E'
    print(score, grade)
52 E
98 A
99 A
80 B
95 A
69 D
86 B
93 A
61 D
61 D
71 C
76 C
61 D
81 B
75 C
85 B
89 B
79 C
60 D
69 D
In [2]:
import random
random
Out[2]:
<module 'random' from 'C:\\Users\\Bauer Laptop\\Anaconda3\\lib\\random.py'>
In [3]:
dir(random)
Out[3]:
['BPF',
 'LOG4',
 'NV_MAGICCONST',
 'RECIP_BPF',
 'Random',
 'SG_MAGICCONST',
 'SystemRandom',
 'TWOPI',
 '_BuiltinMethodType',
 '_MethodType',
 '_Sequence',
 '_Set',
 '__all__',
 '__builtins__',
 '__cached__',
 '__doc__',
 '__file__',
 '__loader__',
 '__name__',
 '__package__',
 '__spec__',
 '_acos',
 '_bisect',
 '_ceil',
 '_cos',
 '_e',
 '_exp',
 '_inst',
 '_itertools',
 '_log',
 '_pi',
 '_random',
 '_sha512',
 '_sin',
 '_sqrt',
 '_test',
 '_test_generator',
 '_urandom',
 '_warn',
 'betavariate',
 'choice',
 'choices',
 'expovariate',
 'gammavariate',
 'gauss',
 'getrandbits',
 'getstate',
 'lognormvariate',
 'normalvariate',
 'paretovariate',
 'randint',
 'random',
 'randrange',
 'sample',
 'seed',
 'setstate',
 'shuffle',
 'triangular',
 'uniform',
 'vonmisesvariate',
 'weibullvariate']
In [4]:
?random.random
In [ ]:
import random

while loops

In [5]:
f0 = 0
f1 = 1
while f0 < 100:
    print(f0)
    f0, f1 = f1, f0+f1
0
1
1
2
3
5
8
13
21
34
55
89
In [6]:
i = 0
to_find = 10
while i < 5:
    i += 1
    if i == to_find:
        print('Found; breaking early')
        break
else:
    print('Not found; terminated loop')
Not found; terminated loop
In [7]:
i = 0
to_find = 10
while i < 100:
    i += 1
    if i == to_find:
        print('Found; breaking early')
        break
else:
    print('Not found; terminated loop')
Found; breaking early

Exception Handling

In [8]:
raise Exception('Boom!')
---------------------------------------------------------------------------
Exception                                 Traceback (most recent call last)
<ipython-input-8-ba008919c160> in <module>()
----> 1 raise Exception('Boom!')

Exception: Boom!
In [9]:
raise NotImplementedError()
---------------------------------------------------------------------------
NotImplementedError                       Traceback (most recent call last)
<ipython-input-9-b34b57902732> in <module>()
----> 1 raise NotImplementedError()

NotImplementedError: 
In [12]:
try:
    x=1
    #raise Exception('Boom')
except:
    print('Exception encountered!')
In [13]:
try:
    raise ArithmeticError('Eeek!')
except LookupError as e:
    print('LookupError:', e)
except ArithmeticError as e:
    print('ArithmeticError:', e)
except Exception as e:
    print(e)
finally:
    print('Done')
ArithmeticError: Eeek!
Done

for loops (iteration)

In [14]:
for x in range(10):
    print(x)
0
1
2
3
4
5
6
7
8
9
In [15]:
for i in range(9, 81, 9):
    print(i)
9
18
27
36
45
54
63
72
In [16]:
for c in 'hello world':
    print(c)
h
e
l
l
o
 
w
o
r
l
d
In [21]:
for c in reversed('hello world'):
    print(c)
d
l
r
o
w
 
o
l
l
e
h
In [18]:
count=0
for c in 'hello world':
    print(count, c)
    count+=1
0 h
1 e
2 l
3 l
4 o
5  
6 w
7 o
8 r
9 l
10 d
In [19]:
word='hello world'
for i in range(len(word)):
    print(i, word[i])
0 h
1 e
2 l
3 l
4 o
5  
6 w
7 o
8 r
9 l
10 d
In [20]:
word='hello world'
for i in range(len(word)-1,-1,-1):
    print(i, word[i])
10 d
9 l
8 r
7 o
6 w
5  
4 o
3 l
2 l
1 e
0 h
In [ ]:
to_find = 50
for i in range(100):
    if i == to_find:
        break
else:
    print('Completed loop')

Generalized iteration (iter and next)

In [23]:
r = range(10)
it = iter(r)
r
it
Out[23]:
range(0, 10)
Out[23]:
<range_iterator at 0x6b75110>
In [24]:
type(it)
Out[24]:
range_iterator
In [36]:
next(it)   # execute multiple times
---------------------------------------------------------------------------
StopIteration                             Traceback (most recent call last)
<ipython-input-36-5af05682b10f> in <module>()
----> 1 next(it)   # execute multiple times

StopIteration: 
In [29]:
r
it
Out[29]:
range(0, 10)
Out[29]:
<range_iterator at 0x6b75110>
In [37]:
it = iter(r)
while True:
    try:
        x = next(it)
        print(x)
    except StopIteration:
        break
0
1
2
3
4
5
6
7
8
9
In [38]:
it = iter(r)
while True:
    try:
        x = next(it)
        y = next(it)
        print(x, y, x+y)
    except StopIteration:
        break
0 1 1
2 3 5
4 5 9
6 7 13
8 9 17
In [40]:
s='hello world'
a=iter(s)
b=iter(s)
while True:
    try:
        x = next(a)
        y = next(b)
        z = next(b)        
        print(x, y, z)
    except StopIteration:
        break
h h e
e l l
l o  
l w o
o r l

5. Functions

In [ ]:
def foo():
    pass
In [45]:
import math

def quadratic_roots(a=1, b=-5, c=6):
    disc = b**2-4*a*c
    if disc < 0:
        return None
    else:
        return (-b+math.sqrt(disc))/(2*a), (-b-math.sqrt(disc))/(2*a)
In [53]:
quadratic_roots(b=5)
Out[53]:
(-2.0, -3.0)
In [42]:
quadratic_roots(1, -5, 6) # eq = (x-3)(x-2)
Out[42]:
(3.0, 2.0)
In [43]:
quadratic_roots(a=1, b=-5, c=6)
Out[43]:
(3.0, 2.0)
In [44]:
quadratic_roots(c=6, a=1, b=-5)
Out[44]:
(3.0, 2.0)
In [54]:
def create_character(name, race, hitpoints, ability):
    print('Name:', name)
    print('Race:', race)
    print('Hitpoints:', hitpoints)
    print('Ability:', ability)
In [55]:
create_character('Legolas', 'Elf', 100, 'Archery')
Name: Legolas
Race: Elf
Hitpoints: 100
Ability: Archery
In [56]:
def create_character(name, race='Human', hitpoints=100, ability=None):
    print('Name:', name)
    print('Race:', race)
    print('Hitpoints:', hitpoints)
    if ability:
        print('Ability:', ability)
In [57]:
create_character('Michael')
Name: Michael
Race: Human
Hitpoints: 100
In [62]:
def create_character(name, race='Human', hitpoints=100, abilities=()):
    print('Name:', name)
    print('Race:', race)
    print('Hitpoints:', hitpoints)
    if abilities:
        print('Abilities:')
        for ability in abilities:
            print('  -', ability)
In [59]:
create_character('Gimli', race='Dwarf')
Name: Gimli
Race: Dwarf
Hitpoints: 100
In [60]:
create_character('Gandalf', hitpoints=1000)
Name: Gandalf
Race: Human
Hitpoints: 1000
In [61]:
create_character('Aragorn', abilities=('Swording', 'Healing'))
Name: Aragorn
Race: Human
Hitpoints: 100
Abilities:
  - Swording
  - Healing
In [63]:
def create_character(name, *abilities, race='Human', hitpoints=100):
    print('Name:', name)
    print('Race:', race)
    print('Hitpoints:', hitpoints)
    if abilities:
        print('Abilities:')
        for ability in abilities:
            print('  -', ability)
In [64]:
create_character('Michael')
Name: Michael
Race: Human
Hitpoints: 100
In [67]:
create_character('Michael', 'Coding', 'Teaching', 'Sleeping', 25)
Name: Michael
Race: Human
Hitpoints: 100
Abilities:
  - Coding
  - Teaching
  - Sleeping
  - 25

Functions as Objects

In [2]:
def foo():
    print('Foo called')
    
bar = foo
bar()
Foo called
In [6]:
def foo():
    print('Foo called')
    
bar = foo()
bar()
Foo called
---------------------------------------------------------------------------
TypeError                                 Traceback (most recent call last)
<ipython-input-6-7c00d0d139f2> in <module>()
      3 
      4 bar = foo()
----> 5 bar()

TypeError: 'NoneType' object is not callable
In [7]:
def foo(f):
    f()
    
def bar():
    print('Bar called')
    
foo(bar)
Bar called
In [8]:
foo = lambda: print('Anonymous function called')

foo()
Anonymous function called
In [9]:
f = lambda x,y: x+y

f(1,2)
Out[9]:
3
In [10]:
def my_map(f, it):
    for x in it:
        print(f(x))
In [13]:
my_map(lambda x: x*2 +5, range(1,10))
7
9
11
13
15
17
19
21
23
In [14]:
my_map(lambda x: x+1, "Matt")
---------------------------------------------------------------------------
TypeError                                 Traceback (most recent call last)
<ipython-input-14-35409c06f795> in <module>()
----> 1 my_map(lambda x: x+1, "Matt")

<ipython-input-10-00156a604fbe> in my_map(f, it)
      1 def my_map(f, it):
      2     for x in it:
----> 3         print(f(x))

<ipython-input-14-35409c06f795> in <lambda>(x)
----> 1 my_map(lambda x: x+1, "Matt")

TypeError: must be str, not int
In [15]:
map(lambda x: x*2, range(1,10))
Out[15]:
<map at 0x58c1fd0>
In [16]:
for x in map(lambda x: x*2, range(1,10)):
    print(x)
2
4
6
8
10
12
14
16
18
In [17]:
list(range(1,10))
Out[17]:
[1, 2, 3, 4, 5, 6, 7, 8, 9]
In [18]:
list(map(lambda x: x*2, range(1,10)))
Out[18]:
[2, 4, 6, 8, 10, 12, 14, 16, 18]
In [27]:
"".join(list(map(lambda x: str(x*2), range(1,10))))
Out[27]:
'24681012141618'
In [25]:
a=[]
for i in map(lambda x: x*2, range(1,10)):
    a.append(str(i))
"".join(a)
Out[25]:
'24681012141618'
In [28]:
def foo():
    print('Foo called')

type(foo)
Out[28]:
function
In [29]:
dir(foo)
Out[29]:
['__annotations__',
 '__call__',
 '__class__',
 '__closure__',
 '__code__',
 '__defaults__',
 '__delattr__',
 '__dict__',
 '__dir__',
 '__doc__',
 '__eq__',
 '__format__',
 '__ge__',
 '__get__',
 '__getattribute__',
 '__globals__',
 '__gt__',
 '__hash__',
 '__init__',
 '__init_subclass__',
 '__kwdefaults__',
 '__le__',
 '__lt__',
 '__module__',
 '__name__',
 '__ne__',
 '__new__',
 '__qualname__',
 '__reduce__',
 '__reduce_ex__',
 '__repr__',
 '__setattr__',
 '__sizeof__',
 '__str__',
 '__subclasshook__']
In [30]:
foo.__call__()
Foo called

6. OOP (Classes, Methods, etc.)

In [ ]:
class Foo:
    pass

Basic Class Example

In [1]:
class Foo:
    def __init__(self, newX=1):
        self.x=newX 
        print('I got constructed')
    def __repr__(self):
        return str(self.x)
    def __str__(self): 
        return "x="+ str(self.x)
    def __eq__(self, that): 
        return self.x==that.x
In [2]:
i=Foo()
i
print(i)
I got constructed
x=1
In [3]:
j=Foo(5)
j
j.x=3
j
I got constructed
Out[3]:
3
In [7]:
j=Foo(1)
j==i
i.__eq__(j)   
i.__str__()
I got constructed
Out[7]:
True
Out[7]:
True
Out[7]:
'x=1'

Inheritance Example

In [2]:
class Shape:
    def __init__(self, name):
        self.name = name
        
    def __repr__(self):
        return self.name
    
    def __str__(self):
        return self.name.upper()
    
    def area(self):
        raise NotImplementedError()
In [51]:
s = Shape('circle')
In [52]:
s
Out[52]:
circle
In [53]:
str(s)
Out[53]:
'CIRCLE'
In [54]:
s.area()
---------------------------------------------------------------------------
NotImplementedError                       Traceback (most recent call last)
<ipython-input-54-dd459394c615> in <module>()
----> 1 s.area()

<ipython-input-50-6769065d7b69> in area(self)
     10 
     11     def area(self):
---> 12         raise NotImplementedError()

NotImplementedError: 
In [3]:
class Circle(Shape):
    def __init__(self, radius):
        super().__init__('circle')
        self.radius = radius
        
    def __str__(self):
        return super().__str__()+" radius="+str(self.radius)
  
    def area(self):
        return 3.14 * self.radius ** 2
    
    def __eq__(self, that):
        if isinstance(that,Circle):
            return self.radius==that.radius
        else:
            return none
  
    def __add__(self, that):
        if isinstance(that,Circle):
            return Circle(self.radius+that.radius)
        else:
            return none  
        
    def area(self):
        return 3.14 * self.radius ** 2          
In [6]:
c = Circle(5.0)
c
c.area()
d = Circle(5.1)
c==d
f=c+d
print(f)
Out[6]:
circle
Out[6]:
78.5
Out[6]:
False
CIRCLE radius=10.1
In [9]:
c1 = Circle(2.0)
c2 = Circle(4.0)
c3 = Circle(2.0)
c1.area()

c1, c2, c3
c1 == c2
c1 == c3
print(c1 + c2)
Out[9]:
12.56
Out[9]:
(circle, circle, circle)
Out[9]:
False
Out[9]:
True
CIRCLE radius=6.0
In [10]:
(5).__eq__(5)    # need parens around (5) because 5.__eq starts like a float
Out[10]:
True