Basics Revision
Tuples, mutability
Dictionaries
Classes
__init__
, self
https://ngcm.github.io/summer-academy-2016-basics/basics_B.zip
# Run this cell before trying examples
import numpy as np
import matplotlib.pyplot as plt
%matplotlib inline
For
loopsappend
'lists = [10, 12, 14, 16, 18]
print(lists[0]) # Index starts at 0
print(lists[-1]) # Last index at -1
print(lists[0:3]) # Slicing: exclusive of end value
# i.e. get i=(0, 1, .. n-1)
print(lists[3:]) # "slice from i=3 to end"
10 18 [10, 12, 14] [16, 18]
# List construction example
a = []
print(a)
a.append('Hello world')
print(a)
[] ['Hello world']
a.extend([1, 2, 3, 4])
print(a)
a.remove(1) # Remove value 1 from a
print(a)
a.pop(0)
print(a)
['Hello world', 1, 2, 3, 4] ['Hello world', 2, 3, 4] [2, 3, 4]
# All methods
# a. # Tab complete behaviour?
var
is user defined) -
For var in iterable:powers = [0, 1, 2, 3]
for power in powers:
value = 10 ** (power)
print("10 to the power of {} is {}".format(power, value))
10 to the power of 0 is 1 10 to the power of 1 is 10 10 to the power of 2 is 100 10 to the power of 3 is 1000
# Better to use Pythons built in 'range' here:
for i in range(4):
print("10 to the power of {} is {}".format(i, 10**i))
10 to the power of 0 is 1 10 to the power of 1 is 10 10 to the power of 2 is 100 10 to the power of 3 is 1000
x = [i**2 for i in range(10)]
y = [i*10 for i in range(10)]
print(x)
print(y)
[0, 1, 4, 9, 16, 25, 36, 49, 64, 81] [0, 10, 20, 30, 40, 50, 60, 70, 80, 90]
#### How would you create a list from zero to 100 in increments of 5 in one line
def
followed by function name and parameters in a parenthesisreturn output_var(s)
ends the functiondef square_root(x):
"""Useful docstring: Calculates and returns square root of x"""
i = x ** 0.5
return i
x = 10
y = square_root(x)
print('The square root of {} is {}'.format(x, y))
The square root of 10 is 3.1622776601683795
# We can set a default value to the function
def square_root(x=20):
i = x ** 0.5
return i
print(square_root())
4.47213595499958
# Loops, functions and appending
mylist = []
for i in range(1,5):
mylist.append(square_root(i))
print(mylist)
[1.0, 1.4142135623730951, 1.7320508075688772, 2.0]
def update_integer(i):
# attempt to update i (integers) are immutable
i += 1
def update_list_end(arglist):
arglist[-1] = 50 # Lists are mutable: updates args directly!
a = 1
update_integer(a)
print(a)
mylist = [0, 1, 2, 3, 4]
update_list_end(mylist)
print(mylist)
1 [0, 1, 2, 3, 50]
np.array
import numpy as np
# basic usage: arange, linspace, array ops
x = np.linspace(0, 10, 11) # use 11 points
print(x)
y = np.arange(0, 10, 1) # use step size of 1
print(y)
print('The average of x is', np.average(x))
[ 0. 1. 2. 3. 4. 5. 6. 7. 8. 9. 10.] [0 1 2 3 4 5 6 7 8 9] The average of x is 5.0
M1 = np.array([[2,3],[6,3]])
M2 = np.array([[5,6],[2,9]])
print('M1:')
print(M1)
print('M2:')
print(M2)
M1: [[2 3] [6 3]] M2: [[5 6] [2 9]]
M3 = M1 * M2 # Element-wise multiplication
print(M3, '\n')
M4 = np.dot(M1, M2) # Matrix multiplication
print(M4)
[[10 18] [12 27]] [[16 39] [36 63]]
# Given array [0, np.pi/2., np.pi, 3*np.pi/4.] what would you
# expect passing it to np.sin ????
# live coding show some numpy functions.
x = np.linspace(0, 2*np.pi)
y = np.sin(x)
fig = plt.figure(figsize=(12, 5))
ax = fig.add_subplot(111)
ax.plot(x, y,'o-')
ax.margins(0.1)
ax.set_title('2D plot')
ax.set_xlabel('$x$')
ax.set_ylabel(r'$sin(x)$')
<matplotlib.text.Text at 0x110ccf6a0>
xtick_values = np.linspace(0, 2*np.pi, 5)
xtick_labels = ['$0$', r'$\frac{\pi}{2}$', r'$\pi$', r'$\frac{3\pi}{2}$',
r'$2\pi$']
fig = plt.figure(figsize=(12, 5))
ax = fig.add_subplot(111); ax.plot(x, y,'-o')
ax.set_title('2D plot')
ax.margins(0.1)
ax.set_xlabel('$x$'); ax.set_ylabel(r'$sin(x)$')
ax.set_xticks(xtick_values)
ax.set_xticklabels(xtick_labels, fontsize=25);
x = np.linspace(-1, 1, 101)
y = np.linspace(-1, 1, 101)
X, Y = np.meshgrid(x, y)
Z = np.sin(X + Y)**2
from mpl_toolkits.mplot3d import Axes3D
fig = plt.figure(figsize=(12, 5))
ax = fig.add_subplot(111, projection='3d')
surf = ax.plot_surface(X, Y, Z)
ax.set_xlabel(r'$X$')
ax.set_ylabel(r'$Y$')
ax.set_zlabel(r'$Z$')
plt.show()
# Live coding Bay....
# Create a 'Name, Age' Tuple using bracket notation
my_tuple = ('Dave', 42)
print(type(my_tuple))
print(my_tuple)
<class 'tuple'> ('Dave', 42)
# Create Tuple using bracket-less notation
my_tuple2 = 'Bob', 24
print(type(my_tuple2))
print(my_tuple2)
<class 'tuple'> ('Bob', 24)
# Tuple indexing
my_tuple = ('Dave', 42)
print(my_tuple[0])
print(my_tuple[1])
Dave 42
# Could make a list of tuples:
tups = [('Dave', 42), ('Bob', '24')]
# ... and then iterate over it
for tup in tups:
print("{} is {} years old".format(tup[0], tup[1]))
Dave is 42 years old Bob is 24 years old
# Store multiple variables using tuples:
my_tuple = 'Dave', 42
a, b = my_tuple
print('a = {}'.format(a))
print('b = {}'.format(b))
a = Dave b = 42
# Swap Variables using tuples:
b, a = a, b
print('a = {}'.format(a))
print('b = {}'.format(b))
a = 42 b = Dave
# extending or overwriting contents
my_tuple = 'Dave', 42
# my_tuple[0] = 'Steve' # Will give an error
# Sequences: Stick with a list
seq = [] # tuples have no append method, so need a list []
for i in range(10):
seq.append(i**2)
print(seq)
[0, 1, 4, 9, 16, 25, 36, 49, 64, 81]
# Or a numpy array:
print(np.arange(10)**2)
[ 0 1 4 9 16 25 36 49 64 81]
# Create a tuple of lists 'a' - can you change the values in
# the lists?
# Live coding Bay....
key
: value
pairsdict
keyword# Using the dict function:
fruit = [('apples', 2), ('bananas', 5), ('pears', 10)]
price_table = dict(fruit)
print(price_table)
{'apples': 2, 'pears': 10, 'bananas': 5}
# Short hand (Arguably neater)
price_table = {'apples': 2, 'pears': 10, 'bananas': 5}
print(price_table)
{'apples': 2, 'pears': 10, 'bananas': 5}
price_table = {'apples': 2, 'bananas': 5, 'pears': 10}
akey = 'apples'
print("The price of {} is {}p".format(akey, price_table[akey]))
The price of apples is 2p
# Iterating over the dictionary will iterate over its keys
price_table = {'apples': 2, 'bananas': 5, 'pears': 10}
for key in price_table:
print("{} cost {}p".format(key, price_table[key]))
apples cost 2p pears cost 10p bananas cost 5p
# Or use the items method:
for key, val in price_table.items():
print("{} cost {}p".format(key, val))
apples cost 2p pears cost 10p bananas cost 5p
# I don't like pears, so let's buy apples and bananas
shopping_list = [('apples', 50), ('bananas', 20)]
total = 0
for item, quantity in shopping_list:
price = price_table[item]
print('Adding {} {} at {}p each'.format(quantity, item, price))
total += price * quantity
print(total)
Adding 50 apples at 2p each Adding 20 bananas at 5p each 200
# Hoping for ordered data:
alpha_num = {'a': 0, 'b': 1, 'c': 2}
for i, key in enumerate(alpha_num.keys()):
print("{} has a value of {}".format(key, i)) # This is wrong
b has a value of 0 c has a value of 1 a has a value of 2
mydict = {'a':1, 'b':2, 'c':3}
def myFunc(a,b,c):
return a*2, b*2, c*2
myFunc(**mydict)
(2, 4, 6)
# Live coding Bay....
"Object-oriented programming (OOP) refers to a type of computer programming in which programmers define not only the data type of a data structure, but also the types of operations (functions) that can be applied to the data structure."
Source: Webopedia
C++
and Python designed for OOPC
and Fortran
Inheritance
Encapsulation
Abstraction
Polymorphism
with open('data/structured_data.txt', 'w') as f:
f.write('#Name Height Weight\n')
f.write('John 180 80.5\n')
f.write('Paul 172 75.1\n')
f.write('George 185 78.6\n')
f.write('Ringo 170 76.5\n')
# Notice that the argument is a list of tuples
dt = np.dtype([('Name', np.str_, 16), ('Height', np.int32),
('Weight', np.float64)])
data = np.loadtxt('data/structured_data.txt', dtype=dt)
print(data)
[("b'John'", 180, 80.5) ("b'Paul'", 172, 75.1) ("b'George'", 185, 78.6) ("b'Ringo'", 170, 76.5)]
print(data['Name'])
print("{} has weight {}".format(data[0]['Name'], data[0]['Weight']))
["b'John'" "b'Paul'" "b'George'" "b'Ringo'"] b'John' has weight 80.5
# Live coding Bay....
dtypes
¶# Numpy arrays are classes
import numpy as np
a = np.array([0, 1, 6, 8, 12])
print(a.__class__)
print(type(a))
<class 'numpy.ndarray'> <class 'numpy.ndarray'>
# We want to operate on the array: try numpy cumulative sum function
print(np.cumsum(a))
[ 0 1 7 15 27]
# np.cumsum('helloworld') # Should we expect this to work?
We only know what a cumulative sum means for a narrow scope of data types
Group them together with an object!
# cumsum is a method belonging to a
a.cumsum()
array([ 0, 1, 7, 15, 27])
class ClassName(object)
class Greeter(object):
def hello(self): # Method (more on 'self' later)
print("Hello World")
agreeter = Greeter() # 'Instantiate' the class
print(agreeter)
# agreeter. # Tab complete?
<__main__.Greeter object at 0x111e57f28>
# Note that we don't pass an argument to hello!
agreeter.hello()
Hello World
self
¶__init__
class methodself
= instanceClass
is like a type__init__
is not technically Construction (see: C++)__new__
'constructs' the instance before __init__
__init__
then initialises the contentclass A(object):
def __init__(self):
print("Hello")
a_instance = A()
print(type(a_instance))
Hello <class '__main__.A'>
self.attribute = value
class Container(object):
"""Simple container which stores an array as an instance attribute
and an instance method"""
def __init__(self, N):
self.data = np.linspace(0, 1, N)
def plot(self):
fig = plt.figure()
ax = fig.add_subplot(111)
ax.plot(self.data, 'bx')
mydata = Container(11) # 11 is passed as 'N' to __init__
print(mydata.__dict__) # __dict__ is where the attr: value
# pairs are stored!
mydata.plot()
{'data': array([ 0. , 0.1, 0.2, 0.3, 0.4, 0.5, 0.6, 0.7, 0.8, 0.9, 1. ])}
# Code solution here:
class Container(object):
data = np.linspace(0, 1, 5) # class attribute
def __init__(self):
pass
a, b = Container(), Container()
print(a.data)
print(b.data)
[ 0. 0.25 0.5 0.75 1. ] [ 0. 0.25 0.5 0.75 1. ]
a.data = 0 # Creates INSTANCE attribute
Container.data = 100 # Overwrites CLASS attribute
print(a.data)
print(b.data)
0 100
class Container(object):
def __init__(self, N):
self.data = np.linspace(0, 1, N)
def print_data(self):
print(self.data)
a = Container(11)
a.print_data() # <<< This is better
Container.print_data(a)
[ 0. 0.1 0.2 0.3 0.4 0.5 0.6 0.7 0.8 0.9 1. ] [ 0. 0.1 0.2 0.3 0.4 0.5 0.6 0.7 0.8 0.9 1. ]
_
' or '__
'Single underscore
Double underscore
instance._ClassName__Attribute
class Fruit(object):
def __init__(self):
self._hasjuice = True
def juice(self):
if not self.isfull(): raise ValueError('No juice!')
self._hasjuice = False
def isfull(self):
return self._hasjuice
orange = Fruit()
print(orange.isfull())
orange.juice()
print(orange.isfull())
True False
# orange. # tab completion behaviour?
# orange._ # tab completion behaviour now?
orange._hasjuice = True # bad!
orange.isfull()
True
class Fruit(object):
def __init__(self):
self.__hasjuice = True
def juice(self):
if not self.isfull(): raise ValueError('No juice!')
self.__hasjuice = False
def isfull(self):
return self.__hasjuice
apple = Fruit()
# apple._ # tab completion behaviour?
apple.juice()
apple._Fruit__hasjuice = False # Definitely bad!
apple.isfull()
False
# Live coding Bay....
Child
/Derived
class inherits from Parent
/Base
class Parent(object):
# Note the base __init__ is overridden in
# Child class
def __init__(self):
pass
def double(self):
return self.data*2
class Child(Parent):
def __init__(self, data):
self.data = data
achild = Child(np.array([0, 1, 5, 10]))
achild.double()
array([ 0, 2, 10, 20])
super
¶class Plottable(object):
def __init__(self, data):
self.data = data
def plot(self, ax):
ax.plot(self.data)
class SinWave(Plottable):
def __init__(self):
super().__init__(
np.sin(np.linspace(0, np.pi*2, 101)))
class CosWave(Plottable):
def __init__(self):
super().__init__(
np.cos(np.linspace(0, np.pi*2, 101)))
fig = plt.figure()
ax = fig.add_subplot(111)
mysin = SinWave(); mycos = CosWave()
mysin.plot(ax); mycos.plot(ax)
object
& builtin types come with many of these__init__
!dir(object)
['__class__', '__delattr__', '__dir__', '__doc__', '__eq__', '__format__', '__ge__', '__getattribute__', '__gt__', '__hash__', '__init__', '__le__', '__lt__', '__ne__', '__new__', '__reduce__', '__reduce_ex__', '__repr__', '__setattr__', '__sizeof__', '__str__', '__subclasshook__']
__lt__
:
__str__
All magics can be overridden
__str__
and __lt__
magic methods¶class Wave(object):
def __init__(self, freq):
self.freq = freq
self._data = np.sin(np.linspace(0, np.pi, 101)
* np.pi*2 * freq)
def __str__(self):
"""RETURNS the string for printing"""
return "Wave frequency: {}".format(self.freq)
def __lt__(self, wave2):
return self.freq < wave2.freq
wav_low = Wave(10)
wav_high = Wave(50) # A high frequency wave
print(wav_high)
wav_low < wav_high
Wave frequency: 50
True
# Live coding Bay....
object
in Python 3object
class OldSyntax:
pass
class NewSyntax(object): # This means 'inherit from object'
pass
print(type(OldSyntax)) # Would give <type 'classobj'>
# in Python 2
print(type(NewSyntax))
<class 'type'> <class 'type'>
object
in Py3Read
the packages you use dailyP.R.Chambers@soton.ac.uk