\n",
"('Bob', 24)\n"
]
}
],
"source": [
"# Create Tuple using bracket-less notation\n",
"my_tuple2 = 'Bob', 24\n",
"\n",
"print(type(my_tuple2))\n",
"print(my_tuple2)"
]
},
{
"cell_type": "markdown",
"metadata": {
"slideshow": {
"slide_type": "subslide"
}
},
"source": [
"#### Example: Usage"
]
},
{
"cell_type": "code",
"execution_count": 27,
"metadata": {
"collapsed": false,
"scrolled": true,
"slideshow": {
"slide_type": "fragment"
}
},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"Dave\n",
"42\n"
]
}
],
"source": [
"# Tuple indexing\n",
"my_tuple = ('Dave', 42)\n",
"print(my_tuple[0])\n",
"print(my_tuple[1])"
]
},
{
"cell_type": "code",
"execution_count": 28,
"metadata": {
"collapsed": false,
"scrolled": true,
"slideshow": {
"slide_type": "fragment"
}
},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"Dave is 42 years old\n",
"Bob is 24 years old\n"
]
}
],
"source": [
"# Could make a list of tuples:\n",
"tups = [('Dave', 42), ('Bob', '24')]\n",
"# ... and then iterate over it\n",
"for tup in tups:\n",
" print(\"{} is {} years old\".format(tup[0], tup[1]))"
]
},
{
"cell_type": "markdown",
"metadata": {
"slideshow": {
"slide_type": "subslide"
}
},
"source": [
"#### Example: Tuple Unpacking"
]
},
{
"cell_type": "code",
"execution_count": 29,
"metadata": {
"collapsed": false,
"scrolled": true,
"slideshow": {
"slide_type": "fragment"
}
},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"a = Dave\n",
"b = 42\n"
]
}
],
"source": [
"# Store multiple variables using tuples:\n",
"my_tuple = 'Dave', 42\n",
"a, b = my_tuple\n",
"\n",
"print('a = {}'.format(a))\n",
"print('b = {}'.format(b))"
]
},
{
"cell_type": "code",
"execution_count": 30,
"metadata": {
"collapsed": false,
"scrolled": true,
"slideshow": {
"slide_type": "fragment"
}
},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"a = 42\n",
"b = Dave\n"
]
}
],
"source": [
"# Swap Variables using tuples:\n",
"b, a = a, b\n",
"\n",
"print('a = {}'.format(a))\n",
"print('b = {}'.format(b))"
]
},
{
"cell_type": "markdown",
"metadata": {
"slideshow": {
"slide_type": "subslide"
}
},
"source": [
"#### Example: When NOT to use a Tuple (1)"
]
},
{
"cell_type": "code",
"execution_count": 31,
"metadata": {
"collapsed": false,
"scrolled": true,
"slideshow": {
"slide_type": "fragment"
}
},
"outputs": [],
"source": [
"# extending or overwriting contents\n",
"my_tuple = 'Dave', 42\n",
"\n",
"# my_tuple[0] = 'Steve' # Will give an error"
]
},
{
"cell_type": "markdown",
"metadata": {
"slideshow": {
"slide_type": "subslide"
}
},
"source": [
"#### Example: When NOT to use a Tuple (2)"
]
},
{
"cell_type": "code",
"execution_count": 32,
"metadata": {
"collapsed": false,
"slideshow": {
"slide_type": "fragment"
}
},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"[0, 1, 4, 9, 16, 25, 36, 49, 64, 81]\n"
]
}
],
"source": [
"# Sequences: Stick with a list\n",
"seq = [] # tuples have no append method, so need a list [] \n",
"for i in range(10):\n",
" seq.append(i**2)\n",
"\n",
"print(seq)"
]
},
{
"cell_type": "code",
"execution_count": 33,
"metadata": {
"collapsed": false,
"scrolled": true,
"slideshow": {
"slide_type": "fragment"
}
},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"[ 0 1 4 9 16 25 36 49 64 81]\n"
]
}
],
"source": [
"# Or a numpy array:\n",
"print(np.arange(10)**2)"
]
},
{
"cell_type": "code",
"execution_count": 34,
"metadata": {
"collapsed": false,
"slideshow": {
"slide_type": "subslide"
}
},
"outputs": [],
"source": [
"# Create a tuple of lists 'a' - can you change the values in \n",
"# the lists?\n"
]
},
{
"cell_type": "code",
"execution_count": 35,
"metadata": {
"collapsed": true,
"slideshow": {
"slide_type": "subslide"
}
},
"outputs": [],
"source": [
"# Live coding Bay....\n"
]
},
{
"cell_type": "markdown",
"metadata": {
"slideshow": {
"slide_type": "slide"
}
},
"source": [
"## Dictionaries\n",
"\n",
"* Set of `key` : `value` pairs\n",
"* Ordering follows hash table rules, not so intuitive to humans\n",
"* Use curly braces - {} or `dict` keyword"
]
},
{
"cell_type": "markdown",
"metadata": {
"slideshow": {
"slide_type": "subslide"
}
},
"source": [
"#### Example: Fruit Prices Lookup Table - Construction"
]
},
{
"cell_type": "code",
"execution_count": 36,
"metadata": {
"collapsed": false,
"scrolled": true,
"slideshow": {
"slide_type": "fragment"
}
},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"{'apples': 2, 'pears': 10, 'bananas': 5}\n"
]
}
],
"source": [
"# Using the dict function:\n",
"fruit = [('apples', 2), ('bananas', 5), ('pears', 10)]\n",
"price_table = dict(fruit)\n",
"print(price_table)"
]
},
{
"cell_type": "code",
"execution_count": 37,
"metadata": {
"collapsed": false,
"scrolled": true,
"slideshow": {
"slide_type": "fragment"
}
},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"{'apples': 2, 'pears': 10, 'bananas': 5}\n"
]
}
],
"source": [
"# Short hand (Arguably neater)\n",
"price_table = {'apples': 2, 'pears': 10, 'bananas': 5}\n",
"print(price_table)"
]
},
{
"cell_type": "markdown",
"metadata": {
"slideshow": {
"slide_type": "notes"
}
},
"source": [
"**Note**: notice that the consistent order on printing of the dictionaries, even though the inputs are reordered. The ordering of [hash tables](https://en.wikipedia.org/wiki/Hash_table) is well defined, but not in a human-intuitive sense. We should therefore treat the data as if it was unordered."
]
},
{
"cell_type": "markdown",
"metadata": {
"slideshow": {
"slide_type": "subslide"
}
},
"source": [
"#### Example: Accessing values from keys"
]
},
{
"cell_type": "code",
"execution_count": 38,
"metadata": {
"collapsed": false,
"slideshow": {
"slide_type": "fragment"
}
},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"The price of apples is 2p\n"
]
}
],
"source": [
"price_table = {'apples': 2, 'bananas': 5, 'pears': 10}\n",
"\n",
"akey = 'apples'\n",
"print(\"The price of {} is {}p\".format(akey, price_table[akey]))"
]
},
{
"cell_type": "markdown",
"metadata": {
"slideshow": {
"slide_type": "subslide"
}
},
"source": [
"#### Example: Iterating over a dictionary"
]
},
{
"cell_type": "code",
"execution_count": 39,
"metadata": {
"collapsed": false,
"slideshow": {
"slide_type": "fragment"
}
},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"apples cost 2p\n",
"pears cost 10p\n",
"bananas cost 5p\n"
]
}
],
"source": [
"# Iterating over the dictionary will iterate over its keys\n",
"price_table = {'apples': 2, 'bananas': 5, 'pears': 10}\n",
"\n",
"for key in price_table:\n",
" print(\"{} cost {}p\".format(key, price_table[key]))"
]
},
{
"cell_type": "code",
"execution_count": 40,
"metadata": {
"collapsed": false,
"slideshow": {
"slide_type": "fragment"
}
},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"apples cost 2p\n",
"pears cost 10p\n",
"bananas cost 5p\n"
]
}
],
"source": [
"# Or use the items method:\n",
"for key, val in price_table.items():\n",
" print(\"{} cost {}p\".format(key, val))"
]
},
{
"cell_type": "markdown",
"metadata": {
"slideshow": {
"slide_type": "subslide"
}
},
"source": [
"#### Example: Shopping list using dictionary price lookup"
]
},
{
"cell_type": "code",
"execution_count": 41,
"metadata": {
"collapsed": false,
"slideshow": {
"slide_type": "fragment"
}
},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"Adding 50 apples at 2p each\n",
"Adding 20 bananas at 5p each\n",
"200\n"
]
}
],
"source": [
"# I don't like pears, so let's buy apples and bananas\n",
"shopping_list = [('apples', 50), ('bananas', 20)]\n",
"total = 0\n",
"for item, quantity in shopping_list:\n",
" price = price_table[item]\n",
" print('Adding {} {} at {}p each'.format(quantity, item, price))\n",
" total += price * quantity\n",
"\n",
"print(total)"
]
},
{
"cell_type": "markdown",
"metadata": {
"slideshow": {
"slide_type": "subslide"
}
},
"source": [
"#### Example: When NOT to use a Dictionary"
]
},
{
"cell_type": "code",
"execution_count": 42,
"metadata": {
"collapsed": false,
"slideshow": {
"slide_type": "fragment"
}
},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"b has a value of 0\n",
"c has a value of 1\n",
"a has a value of 2\n"
]
}
],
"source": [
"# Hoping for ordered data:\n",
"alpha_num = {'a': 0, 'b': 1, 'c': 2}\n",
"\n",
"for i, key in enumerate(alpha_num.keys()):\n",
" print(\"{} has a value of {}\".format(key, i)) # This is wrong"
]
},
{
"cell_type": "markdown",
"metadata": {
"slideshow": {
"slide_type": "subslide"
}
},
"source": [
"#### Example: Dictionary unpacking using '**'"
]
},
{
"cell_type": "code",
"execution_count": 43,
"metadata": {
"collapsed": false,
"slideshow": {
"slide_type": "fragment"
}
},
"outputs": [
{
"data": {
"text/plain": [
"(2, 4, 6)"
]
},
"execution_count": 43,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"mydict = {'a':1, 'b':2, 'c':3}\n",
"\n",
"def myFunc(a,b,c):\n",
" return a*2, b*2, c*2\n",
"\n",
"myFunc(**mydict)"
]
},
{
"cell_type": "code",
"execution_count": 44,
"metadata": {
"collapsed": false,
"slideshow": {
"slide_type": "subslide"
}
},
"outputs": [],
"source": [
"# Live coding Bay....\n"
]
},
{
"cell_type": "markdown",
"metadata": {
"slideshow": {
"slide_type": "subslide"
}
},
"source": [
"## Exercise: Tuples and Dictionaries\n",
"\n",
"[Tuples and Dictionaries Exercise](exercises/01-Tuples_Dictionaries.ipynb)"
]
},
{
"cell_type": "markdown",
"metadata": {
"slideshow": {
"slide_type": "slide"
}
},
"source": [
"# Intro to Python OOP\n",
"\n",
"#### (For The Classy Programmer)\n",
"\n",
">\n",
"\"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.\"\n",
">
\n",
"\n",
"Source: [Webopedia](http://www.webopedia.com/TERM/O/object_oriented_programming_OOP.html)"
]
},
{
"cell_type": "markdown",
"metadata": {
"slideshow": {
"slide_type": "subslide"
}
},
"source": [
"## Why OOP?\n",
"\n",
"* Naturally structured data\n",
"* Functions used in context\n",
"* Reduce duplicate code\n",
"* Maintainability in large codes/software\n",
"* [Many other reasons](http://inventwithpython.com/blog/2014/12/02/why-is-object-oriented-programming-useful-with-an-role-playing-game-example/)"
]
},
{
"cell_type": "markdown",
"metadata": {
"slideshow": {
"slide_type": "subslide"
}
},
"source": [
"### OOP in Scientific Computing\n",
"* Java, `C++` and Python designed for OOP\n",
"* **Everything** in Python is an object\n",
"* Scientific libraries, visualisation tools etc.\n",
"* Pseudo Object Orientation in `C` and `Fortran`"
]
},
{
"cell_type": "markdown",
"metadata": {
"slideshow": {
"slide_type": "subslide"
}
},
"source": [
"### What will I learn about OOP here?\n",
"* Language in OOP is very different\n",
" - Learn language used in eg. C++, Java\n",
"* Ability to **read** code is essential\n",
"* Write/migrate code for community library\n",
" - Better world! Work recognition etc..."
]
},
{
"cell_type": "markdown",
"metadata": {
"slideshow": {
"slide_type": "subslide"
}
},
"source": [
"### OOP: Four Fundamental Concepts\n",
"\n",
"* *Inheritance*\n",
" - Reuse code by deriving from existing classes\n",
" \n",
"* *Encapsulation*\n",
" - Data hiding"
]
},
{
"cell_type": "markdown",
"metadata": {
"slideshow": {
"slide_type": "subslide"
}
},
"source": [
"### OOP: Four Fundamental Concepts (2)\n",
"\n",
"* *Abstraction* \n",
" - Simplified representation of complexity\n",
" \n",
"* *Polymorphism*\n",
" - API performs differently based on data type"
]
},
{
"cell_type": "markdown",
"metadata": {
"slideshow": {
"slide_type": "notes"
}
},
"source": [
"**Note:** Encapsulation is sometimes also used in OOP to describe the grouping of data with methods. It is however more common for texts to use it to describe the hiding of data as will be done here.\n",
"\n",
"Useful explanations of these concepts for Python can also be found [here](http://zetcode.com/lang/python/oop/)"
]
},
{
"cell_type": "markdown",
"metadata": {
"slideshow": {
"slide_type": "slide"
}
},
"source": [
"## Structured data: Numpy dtypes\n",
"* Not a class, but motivational example\n",
"* Structured associative data\n",
"* Multiple data accessible from a single data type\n",
"* Identifiers to indicate data type"
]
},
{
"cell_type": "markdown",
"metadata": {
"slideshow": {
"slide_type": "subslide"
}
},
"source": [
"#### Example: Data about people"
]
},
{
"cell_type": "code",
"execution_count": 45,
"metadata": {
"collapsed": false,
"scrolled": true,
"slideshow": {
"slide_type": "fragment"
}
},
"outputs": [],
"source": [
"with open('data/structured_data.txt', 'w') as f:\n",
" f.write('#Name Height Weight\\n')\n",
" f.write('John 180 80.5\\n')\n",
" f.write('Paul 172 75.1\\n')\n",
" f.write('George 185 78.6\\n')\n",
" f.write('Ringo 170 76.5\\n')"
]
},
{
"cell_type": "code",
"execution_count": 46,
"metadata": {
"collapsed": false,
"scrolled": true,
"slideshow": {
"slide_type": "fragment"
}
},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"[(\"b'John'\", 180, 80.5) (\"b'Paul'\", 172, 75.1) (\"b'George'\", 185, 78.6)\n",
" (\"b'Ringo'\", 170, 76.5)]\n"
]
}
],
"source": [
"# Notice that the argument is a list of tuples\n",
"dt = np.dtype([('Name', np.str_, 16), ('Height', np.int32),\n",
" ('Weight', np.float64)])\n",
"data = np.loadtxt('data/structured_data.txt', dtype=dt)\n",
"\n",
"print(data)"
]
},
{
"cell_type": "code",
"execution_count": 47,
"metadata": {
"collapsed": false,
"scrolled": true,
"slideshow": {
"slide_type": "fragment"
}
},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"[\"b'John'\" \"b'Paul'\" \"b'George'\" \"b'Ringo'\"]\n",
"b'John' has weight 80.5\n"
]
}
],
"source": [
"print(data['Name'])\n",
"print(\"{} has weight {}\".format(data[0]['Name'], data[0]['Weight']))"
]
},
{
"cell_type": "code",
"execution_count": 48,
"metadata": {
"collapsed": true,
"slideshow": {
"slide_type": "subslide"
}
},
"outputs": [],
"source": [
"# Live coding Bay....\n"
]
},
{
"cell_type": "markdown",
"metadata": {
"slideshow": {
"slide_type": "subslide"
}
},
"source": [
"## Exercise: Numpy `dtypes`\n",
"\n",
"[Exercise: Load image data with dtype structured array](exercises/03-dtypes.ipynb)"
]
},
{
"cell_type": "markdown",
"metadata": {
"slideshow": {
"slide_type": "fragment"
}
},
"source": [
"* Data is structured, but not elegant\n",
"* no methods etc"
]
},
{
"cell_type": "markdown",
"metadata": {
"slideshow": {
"slide_type": "slide"
}
},
"source": [
"## Classes: Basics\n",
"* Attributes (data)\n",
"* Methods (Functions operating on the attributes)\n",
"* 'First class citizens': Same rights as core types\n",
" - pass to functions, store as variables etc."
]
},
{
"cell_type": "markdown",
"metadata": {
"slideshow": {
"slide_type": "subslide"
}
},
"source": [
"#### Example: Numpy arrays showing how it's done"
]
},
{
"cell_type": "code",
"execution_count": 49,
"metadata": {
"collapsed": false,
"slideshow": {
"slide_type": "fragment"
}
},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"\n",
"\n"
]
}
],
"source": [
"# Numpy arrays are classes\n",
"import numpy as np\n",
"a = np.array([0, 1, 6, 8, 12])\n",
"print(a.__class__)\n",
"print(type(a))"
]
},
{
"cell_type": "code",
"execution_count": 50,
"metadata": {
"collapsed": false,
"slideshow": {
"slide_type": "fragment"
}
},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"[ 0 1 7 15 27]\n"
]
}
],
"source": [
"# We want to operate on the array: try numpy cumulative sum function\n",
"print(np.cumsum(a))"
]
},
{
"cell_type": "code",
"execution_count": 51,
"metadata": {
"collapsed": false,
"slideshow": {
"slide_type": "fragment"
}
},
"outputs": [],
"source": [
"# np.cumsum('helloworld') # Should we expect this to work?"
]
},
{
"cell_type": "markdown",
"metadata": {
"slideshow": {
"slide_type": "subslide"
}
},
"source": [
"#### Example: Numpy arrays showing how it's done (ctd.)\n",
"\n",
"* We only know what a cumulative sum means for a narrow scope of data types\n",
"\n",
"* Group them together with an object!"
]
},
{
"cell_type": "code",
"execution_count": 52,
"metadata": {
"collapsed": false,
"slideshow": {
"slide_type": "fragment"
}
},
"outputs": [
{
"data": {
"text/plain": [
"array([ 0, 1, 7, 15, 27])"
]
},
"execution_count": 52,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"# cumsum is a method belonging to a\n",
"a.cumsum()"
]
},
{
"cell_type": "markdown",
"metadata": {
"slideshow": {
"slide_type": "subslide"
}
},
"source": [
"#### Example: Simple class\n",
"* For now, assume all classes defined by: `class ClassName(object)`"
]
},
{
"cell_type": "code",
"execution_count": 53,
"metadata": {
"collapsed": false,
"slideshow": {
"slide_type": "fragment"
}
},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"<__main__.Greeter object at 0x111e57f28>\n"
]
}
],
"source": [
"class Greeter(object):\n",
" def hello(self): # Method (more on 'self' later)\n",
" print(\"Hello World\")\n",
"\n",
"agreeter = Greeter() # 'Instantiate' the class\n",
"print(agreeter)\n",
"# agreeter. # Tab complete?"
]
},
{
"cell_type": "markdown",
"metadata": {
"slideshow": {
"slide_type": "notes"
}
},
"source": [
"There's a few things here which I haven't introduced, but all will become clear in the remainder of this workshop."
]
},
{
"cell_type": "code",
"execution_count": 54,
"metadata": {
"collapsed": false,
"slideshow": {
"slide_type": "fragment"
}
},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"Hello World\n"
]
}
],
"source": [
"# Note that we don't pass an argument to hello!\n",
"agreeter.hello()"
]
},
{
"cell_type": "markdown",
"metadata": {
"slideshow": {
"slide_type": "slide"
}
},
"source": [
"## Classes: Initialisation and `self`\n",
"\n",
"* ```__init__``` class method\n",
"* Called on creation of an instance\n",
"* Convention: `self` = instance\n",
"* **Implicit passing** of self, **explicit receive**\n"
]
},
{
"cell_type": "markdown",
"metadata": {
"slideshow": {
"slide_type": "notes"
}
},
"source": [
"**Note:** Passing of `self` is done implicitly in other languages e.g. C++ and Java, and proponents of those languages may argue that this is better. \"*Explicit is better than implicit*\" is [simply the python way](https://www.python.org/dev/peps/pep-0020/)."
]
},
{
"cell_type": "markdown",
"metadata": {
"slideshow": {
"slide_type": "subslide"
}
},
"source": [
"### What is an \"instance\"?\n",
"\n",
"* `Class` is like a type\n",
"* Instance is a specific realisation of that type\n",
"* eg. \"Hello World\" is an instance of string\n",
"* Instances attributes are not shared\n"
]
},
{
"cell_type": "markdown",
"metadata": {
"slideshow": {
"slide_type": "subslide"
}
},
"source": [
"### Classes: Initialisation vs Construction\n",
"* Initialisation changes the instance when it is made\n",
"* ... `__init__` is not *technically* Construction (see: C++)\n",
"* `__new__` '*constructs*' the instance before `__init__`\n",
"* `__init__` then initialises the content"
]
},
{
"cell_type": "markdown",
"metadata": {
"slideshow": {
"slide_type": "notes"
}
},
"source": [
"**More info:** The Constructor *creates* the instance, and the Initialiser *Initialises* its contents. Most languages e.g. C++ refer to these interchangably and perform these steps together, however the new style classes in Python splits the process.\n",
"\n",
"The difference is quite fine, and for most purposes we do not need to redefine the behaviour of `__new__`. This is discussed in several Stack Overflow threads, e.g.\n",
"\n",
"* [Python (and Python C API): `__new__` versus `__init__`](http://stackoverflow.com/questions/4859129/python-and-python-c-api-new-versus-init)\n",
"\n",
"* [Python's use of `__new__` and `__init__`?](http://stackoverflow.com/questions/674304/pythons-use-of-new-and-init)"
]
},
{
"cell_type": "markdown",
"metadata": {
"slideshow": {
"slide_type": "subslide"
}
},
"source": [
"#### Example: Class Initialisation"
]
},
{
"cell_type": "code",
"execution_count": 55,
"metadata": {
"collapsed": false,
"slideshow": {
"slide_type": "fragment"
}
},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"Hello\n",
"\n"
]
}
],
"source": [
"class A(object):\n",
" def __init__(self):\n",
" print(\"Hello\")\n",
"\n",
"a_instance = A()\n",
"print(type(a_instance))"
]
},
{
"cell_type": "markdown",
"metadata": {
"slideshow": {
"slide_type": "subslide"
}
},
"source": [
"### Instance attributes and Class attributes\n",
"* Instance attributes definition: `self.attribute = value`\n",
"* Class attributes defined outside functions (*class scope*)\n",
"* Class attributes are shared by all instances\n",
"* Be careful with class attributes"
]
},
{
"cell_type": "markdown",
"metadata": {
"slideshow": {
"slide_type": "subslide"
}
},
"source": [
"#### Example: Defining Instance attributes/methods"
]
},
{
"cell_type": "code",
"execution_count": 56,
"metadata": {
"collapsed": false,
"slideshow": {
"slide_type": "fragment"
}
},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"{'data': array([ 0. , 0.1, 0.2, 0.3, 0.4, 0.5, 0.6, 0.7, 0.8, 0.9, 1. ])}\n"
]
},
{
"data": {
"image/png": "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\n",
"text/plain": [
""
]
},
"metadata": {},
"output_type": "display_data"
}
],
"source": [
"class Container(object):\n",
" \"\"\"Simple container which stores an array as an instance attribute\n",
" and an instance method\"\"\"\n",
" def __init__(self, N):\n",
" self.data = np.linspace(0, 1, N)\n",
" \n",
" def plot(self):\n",
" fig = plt.figure()\n",
" ax = fig.add_subplot(111)\n",
" ax.plot(self.data, 'bx')\n",
"\n",
"mydata = Container(11) # 11 is passed as 'N' to __init__\n",
"print(mydata.__dict__) # __dict__ is where the attr: value\n",
" # pairs are stored!\n",
"mydata.plot()"
]
},
{
"cell_type": "markdown",
"metadata": {
"slideshow": {
"slide_type": "subslide"
}
},
"source": [
"#### Your turn!\n",
"* Implement a class which takes an input number 'N', and doubles and stores it as an instance attribute\n",
"* Test it!"
]
},
{
"cell_type": "code",
"execution_count": 57,
"metadata": {
"collapsed": false,
"slideshow": {
"slide_type": "fragment"
}
},
"outputs": [],
"source": [
"# Code solution here:\n"
]
},
{
"cell_type": "markdown",
"metadata": {
"slideshow": {
"slide_type": "subslide"
}
},
"source": [
"#### Example: Class attributes vs instance attributes"
]
},
{
"cell_type": "code",
"execution_count": 58,
"metadata": {
"collapsed": false,
"slideshow": {
"slide_type": "fragment"
}
},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"[ 0. 0.25 0.5 0.75 1. ]\n",
"[ 0. 0.25 0.5 0.75 1. ]\n"
]
}
],
"source": [
"class Container(object):\n",
" data = np.linspace(0, 1, 5) # class attribute\n",
" def __init__(self):\n",
" pass\n",
"\n",
"a, b = Container(), Container()\n",
"print(a.data)\n",
"print(b.data)"
]
},
{
"cell_type": "code",
"execution_count": 59,
"metadata": {
"collapsed": false,
"slideshow": {
"slide_type": "fragment"
}
},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"0\n",
"100\n"
]
}
],
"source": [
"a.data = 0 # Creates INSTANCE attribute\n",
"Container.data = 100 # Overwrites CLASS attribute\n",
"print(a.data)\n",
"print(b.data)"
]
},
{
"cell_type": "markdown",
"metadata": {
"slideshow": {
"slide_type": "subslide"
}
},
"source": [
"### Class vs Instance attributes: priority\n",
"\n",
"![Class vs instance attribute priority diagram](images/class_vs_instance_priority.png)\n",
"\n",
"* Source: [toptotal.com](https://www.toptal.com/python/python-class-attributes-an-overly-thorough-guide)"
]
},
{
"cell_type": "markdown",
"metadata": {
"slideshow": {
"slide_type": "notes"
}
},
"source": [
"**Note**: There's a couple of things going on in this example which are worth elaborating on. By specifying `ClassName.attribute`, in this case `Container.data = 100` we've overwritten the value of `data` that EVERY instance of the `Container` class will access. Hence printing `b.data` gives the expected result.\n",
"\n",
"By setting `a.data` at the same time, we have set an *instance* attribute, which is given priority and called first even though we overwrote the class attribute after assigning this.\n",
"\n",
"This could create a hard to track bug. To avoid it:\n",
"\n",
"* Stick to instance variables unless you specifically need to share data e.g. constants, total number or list of things that are shared\n",
"* Don't overwrite things you know are class attributes with `instance.attr` unless you really know what you're doing (even then, it's probably better and more readable to make it an instance attribute)\n",
"\n",
"For a really in depth explanation of class vs instance attributes, see either of the following links:\n",
"\n",
"* [python3_class_and_instance_attributes](http://www.python-course.eu/python3_class_and_instance_attributes.php)\n",
"\n",
"* [python-class-attributes-an-overly-thorough-guide](https://www.toptal.com/python/python-class-attributes-an-overly-thorough-guide)"
]
},
{
"cell_type": "markdown",
"metadata": {
"slideshow": {
"slide_type": "subslide"
}
},
"source": [
"#### Example: Implicit vs Explicit passing Instance"
]
},
{
"cell_type": "code",
"execution_count": 60,
"metadata": {
"collapsed": false,
"slideshow": {
"slide_type": "fragment"
}
},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"[ 0. 0.1 0.2 0.3 0.4 0.5 0.6 0.7 0.8 0.9 1. ]\n",
"[ 0. 0.1 0.2 0.3 0.4 0.5 0.6 0.7 0.8 0.9 1. ]\n"
]
}
],
"source": [
"class Container(object):\n",
" def __init__(self, N):\n",
" self.data = np.linspace(0, 1, N)\n",
"\n",
" def print_data(self):\n",
" print(self.data)\n",
"\n",
"a = Container(11)\n",
"\n",
"a.print_data() # <<< This is better\n",
"Container.print_data(a)"
]
},
{
"cell_type": "markdown",
"metadata": {
"slideshow": {
"slide_type": "subslide"
}
},
"source": [
"## Exercise: Basics and Initialisation\n",
"\n",
"[Python OOP 1 Basics and Initialisation with images](exercises/04-Classes_basics.ipynb)"
]
},
{
"cell_type": "markdown",
"metadata": {
"slideshow": {
"slide_type": "slide"
}
},
"source": [
"## Classes: Encapsulation\n",
"* Hiding data from users (and developers)\n",
"* Use underscores '`_`' or '`__`'\n",
"* Useful if data changing should be controlled\n",
"* **Convention only in Python** - not enforced!"
]
},
{
"cell_type": "markdown",
"metadata": {
"slideshow": {
"slide_type": "subslide"
}
},
"source": [
"### Single vs Double Underscore\n",
"\n",
"* Single underscore\n",
" - Nobody outside this class or derived classes should access or change\n",
" \n",
"* Double underscore\n",
" - Stronger attempt to enforce the above\n",
" - Also 'mangles' the attribute name with \n",
" \n",
"`instance._ClassName__Attribute`"
]
},
{
"cell_type": "markdown",
"metadata": {
"slideshow": {
"slide_type": "subslide"
}
},
"source": [
"#### Example: Data hiding \"protected\", single underscore"
]
},
{
"cell_type": "code",
"execution_count": 61,
"metadata": {
"collapsed": false,
"slideshow": {
"slide_type": "fragment"
}
},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"True\n",
"False\n"
]
}
],
"source": [
"class Fruit(object):\n",
" def __init__(self):\n",
" self._hasjuice = True\n",
"\n",
" def juice(self):\n",
" if not self.isfull(): raise ValueError('No juice!')\n",
" self._hasjuice = False\n",
" \n",
" def isfull(self):\n",
" return self._hasjuice\n",
"\n",
"orange = Fruit()\n",
"print(orange.isfull())\n",
"orange.juice()\n",
"print(orange.isfull())"
]
},
{
"cell_type": "code",
"execution_count": 62,
"metadata": {
"collapsed": false,
"slideshow": {
"slide_type": "fragment"
}
},
"outputs": [
{
"data": {
"text/plain": [
"True"
]
},
"execution_count": 62,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"# orange. # tab completion behaviour?\n",
"# orange._ # tab completion behaviour now?\n",
"orange._hasjuice = True # bad!\n",
"orange.isfull()"
]
},
{
"cell_type": "markdown",
"metadata": {
"slideshow": {
"slide_type": "subslide"
}
},
"source": [
"#### Example: Data hiding \"private\", double underscore"
]
},
{
"cell_type": "code",
"execution_count": 63,
"metadata": {
"collapsed": true,
"slideshow": {
"slide_type": "fragment"
}
},
"outputs": [],
"source": [
"class Fruit(object):\n",
" def __init__(self):\n",
" self.__hasjuice = True\n",
"\n",
" def juice(self):\n",
" if not self.isfull(): raise ValueError('No juice!')\n",
" self.__hasjuice = False\n",
" \n",
" def isfull(self):\n",
" return self.__hasjuice\n",
"\n",
"apple = Fruit()"
]
},
{
"cell_type": "code",
"execution_count": 64,
"metadata": {
"collapsed": false,
"slideshow": {
"slide_type": "fragment"
}
},
"outputs": [
{
"data": {
"text/plain": [
"False"
]
},
"execution_count": 64,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"# apple._ # tab completion behaviour?\n",
"apple.juice()\n",
"apple._Fruit__hasjuice = False # Definitely bad!\n",
"apple.isfull()"
]
},
{
"cell_type": "markdown",
"metadata": {
"slideshow": {
"slide_type": "notes"
}
},
"source": [
"**Note**: This behaviour can be over used in Python. Programmers from C++ or Java backgrounds may want to make all data hidden or `private` and access the data with 'getter' or 'setter' functions, however it's generally accepted by Python programmers that getters and setters are unnecessary. The Pythonista phrase is \"we are all consenting adults here\", meaning you should trust the programmer to interact with your classes and they should trust you to document/indicate which parts of the data not to touch unless they know what they're doing (hence the underscore convention). See the top answer on [this Stack Overflow thread](http://stackoverflow.com/questions/6930144/underscore-vs-double-underscore-with-variables-and-methods).\n",
"\n",
"For an entertaining view of encapsulation, see [this blog](http://radek.io/2011/07/21/private-protected-and-public-in-python/)"
]
},
{
"cell_type": "code",
"execution_count": 65,
"metadata": {
"collapsed": true,
"slideshow": {
"slide_type": "subslide"
}
},
"outputs": [],
"source": [
"# Live coding Bay....\n"
]
},
{
"cell_type": "markdown",
"metadata": {
"slideshow": {
"slide_type": "slide"
}
},
"source": [
"## Classes: Inheritance\n",
"\n",
"* Group multiple objects and methods\n",
"* `Child`/`Derived` class inherits from `Parent`/`Base` \n",
"* Reduce duplicate code\n",
"* Maintanable: changes to base falls through to all\n",
"* Beware multiple inheritance rules - we won't cover this"
]
},
{
"cell_type": "markdown",
"metadata": {
"collapsed": true,
"scrolled": true,
"slideshow": {
"slide_type": "subslide"
}
},
"source": [
"#### Example: Simple inheritance"
]
},
{
"cell_type": "code",
"execution_count": 66,
"metadata": {
"collapsed": false,
"slideshow": {
"slide_type": "fragment"
}
},
"outputs": [
{
"data": {
"text/plain": [
"array([ 0, 2, 10, 20])"
]
},
"execution_count": 66,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"class Parent(object):\n",
" # Note the base __init__ is overridden in\n",
" # Child class\n",
" def __init__(self): \n",
" pass\n",
" def double(self):\n",
" return self.data*2\n",
"\n",
"class Child(Parent):\n",
" def __init__(self, data):\n",
" self.data = data\n",
"\n",
"achild = Child(np.array([0, 1, 5, 10]))\n",
"achild.double()"
]
},
{
"cell_type": "markdown",
"metadata": {
"collapsed": true,
"slideshow": {
"slide_type": "subslide"
}
},
"source": [
"#### Example: Calling parent methods with `super`"
]
},
{
"cell_type": "code",
"execution_count": 67,
"metadata": {
"collapsed": false,
"slideshow": {
"slide_type": "fragment"
}
},
"outputs": [
{
"data": {
"image/png": "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\nIQAAAABJRU5ErkJggg==\n",
"text/plain": [
""
]
},
"metadata": {},
"output_type": "display_data"
}
],
"source": [
"class Plottable(object):\n",
" def __init__(self, data): \n",
" self.data = data\n",
" def plot(self, ax):\n",
" ax.plot(self.data)\n",
" \n",
"class SinWave(Plottable):\n",
" def __init__(self):\n",
" super().__init__(\n",
" np.sin(np.linspace(0, np.pi*2, 101)))\n",
"\n",
"class CosWave(Plottable):\n",
" def __init__(self):\n",
" super().__init__(\n",
" np.cos(np.linspace(0, np.pi*2, 101)))\n",
"\n",
"fig = plt.figure()\n",
"ax = fig.add_subplot(111)\n",
"mysin = SinWave(); mycos = CosWave()\n",
"mysin.plot(ax); mycos.plot(ax)"
]
},
{
"cell_type": "markdown",
"metadata": {
"slideshow": {
"slide_type": "notes"
}
},
"source": [
"**Notes:** \n",
"* We didn't need any arguments to `super` here as Python 3 allows this\n",
"* `super` requires additional arguments in Python 2, e.g.\n",
"\n",
" super(Class, self).method(args...)\n",
"\n",
"If you were wondering why we should use `super().method` instead of `BaseClass.method`, other than the convenience of renaming classes, it relates to multiple inheritance which is beyond the scope of this course. If you need to write programs with multiple inheritance (and there are strong arguments against this), you may want to look at [this blog](http://sixty-north.com/blog/pythons-super-not-as-simple-as-you-thought) for advanced use of `super`."
]
},
{
"cell_type": "markdown",
"metadata": {
"slideshow": {
"slide_type": "slide"
}
},
"source": [
"## Classes: Magic Methods\n",
"* The workhorse of how things 'just work' in Python\n",
"* `object` & builtin types come with many of these\n",
"* Surrounded by double underscores\n",
"* We've seen one already: **`__init__`!**"
]
},
{
"cell_type": "markdown",
"metadata": {
"collapsed": true,
"scrolled": true,
"slideshow": {
"slide_type": "subslide"
}
},
"source": [
"#### Example: The magic methods of object"
]
},
{
"cell_type": "code",
"execution_count": 68,
"metadata": {
"collapsed": false,
"slideshow": {
"slide_type": "fragment"
}
},
"outputs": [
{
"data": {
"text/plain": [
"['__class__',\n",
" '__delattr__',\n",
" '__dir__',\n",
" '__doc__',\n",
" '__eq__',\n",
" '__format__',\n",
" '__ge__',\n",
" '__getattribute__',\n",
" '__gt__',\n",
" '__hash__',\n",
" '__init__',\n",
" '__le__',\n",
" '__lt__',\n",
" '__ne__',\n",
" '__new__',\n",
" '__reduce__',\n",
" '__reduce_ex__',\n",
" '__repr__',\n",
" '__setattr__',\n",
" '__sizeof__',\n",
" '__str__',\n",
" '__subclasshook__']"
]
},
"execution_count": 68,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"dir(object)"
]
},
{
"cell_type": "markdown",
"metadata": {
"slideshow": {
"slide_type": "subslide"
}
},
"source": [
"### Magic Methods: Closer look\n",
"* `__lt__`:\n",
" - Called when evaluating\n",
" a < b\n",
"\n",
"* `__str__`\n",
" - Called by the print function\n",
"\n",
"* All magics can be overridden"
]
},
{
"cell_type": "markdown",
"metadata": {
"slideshow": {
"slide_type": "subslide"
}
},
"source": [
"#### Example: overriding `__str__` and `__lt__` magic methods"
]
},
{
"cell_type": "code",
"execution_count": 69,
"metadata": {
"collapsed": false,
"slideshow": {
"slide_type": "fragment"
}
},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"Wave frequency: 50\n"
]
},
{
"data": {
"text/plain": [
"True"
]
},
"execution_count": 69,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"class Wave(object):\n",
" def __init__(self, freq):\n",
" self.freq = freq\n",
" self._data = np.sin(np.linspace(0, np.pi, 101)\n",
" * np.pi*2 * freq)\n",
" \n",
" def __str__(self):\n",
" \"\"\"RETURNS the string for printing\"\"\"\n",
" return \"Wave frequency: {}\".format(self.freq)\n",
" \n",
" def __lt__(self, wave2):\n",
" return self.freq < wave2.freq\n",
"\n",
"wav_low = Wave(10)\n",
"wav_high = Wave(50) # A high frequency wave\n",
"\n",
"print(wav_high)\n",
"wav_low < wav_high"
]
},
{
"cell_type": "code",
"execution_count": 70,
"metadata": {
"collapsed": false,
"slideshow": {
"slide_type": "subslide"
}
},
"outputs": [],
"source": [
"# Live coding Bay....\n"
]
},
{
"cell_type": "markdown",
"metadata": {
"slideshow": {
"slide_type": "notes"
}
},
"source": [
"**Note**: Magic methods are very briefly introduced here. For an extensive overview of magic methods for Python classes, view [Rafe Kettlers blog](http://www.rafekettler.com/magicmethods.html)"
]
},
{
"cell_type": "markdown",
"metadata": {
"slideshow": {
"slide_type": "subslide"
}
},
"source": [
"## Exercise: Inheritance and Magic Methods \n",
"\n",
"[Python OOP2 Inheritance and Magic Methods using shapes](exercises/05-Classes_pt2.ipynb)"
]
},
{
"cell_type": "markdown",
"metadata": {
"slideshow": {
"slide_type": "slide"
}
},
"source": [
"## Python 2 vs 3\n",
"\n",
"* Dont need to inherit `object` in Python 3\n",
"* New classes inherit from `object`\n",
"* Inheritance behaviour is different\n",
"* Old classes removed in Python 3"
]
},
{
"cell_type": "markdown",
"metadata": {
"slideshow": {
"slide_type": "subslide"
}
},
"source": [
"#### Example: New class default Python 3"
]
},
{
"cell_type": "code",
"execution_count": 71,
"metadata": {
"collapsed": false,
"slideshow": {
"slide_type": "fragment"
}
},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"\n",
"\n"
]
}
],
"source": [
"class OldSyntax:\n",
" pass\n",
"\n",
"class NewSyntax(object): # This means 'inherit from object'\n",
" pass\n",
" \n",
"print(type(OldSyntax)) # Would give \n",
" # in Python 2\n",
"print(type(NewSyntax))"
]
},
{
"cell_type": "markdown",
"metadata": {
"slideshow": {
"slide_type": "fragment"
}
},
"source": [
"* Backwards compatibility: inherit `object` in Py3"
]
},
{
"cell_type": "markdown",
"metadata": {
"slideshow": {
"slide_type": "notes"
}
},
"source": [
"Notes: There are other differences affecting classes which we have not included, such as metaclasses and iterator behaviour, but here is a link to a more complete comparison:\n",
"\n",
"* http://python3porting.com/differences.html"
]
},
{
"cell_type": "markdown",
"metadata": {
"slideshow": {
"slide_type": "slide"
}
},
"source": [
"## Extra Material\n",
"\n",
"* Harder exercise (if time)\n",
"\n",
"* Should test your knowledge from this course\n",
"\n",
"> [Exercise: Predator Prey ](exercises/06-Predator_prey.ipynb)\n"
]
},
{
"cell_type": "markdown",
"metadata": {
"slideshow": {
"slide_type": "subslide"
}
},
"source": [
"* Other things worth looking at (an incredibly biased opinion):\n",
" - [Building Pythonic Packages with setuptools](https://pythonhosted.org/an_example_pypi_project/setuptools.html)\n",
" - [Unit testing with py.test](https://pytest.org/latest/contents.html)\n",
" - [Conda Environments](http://conda.pydata.org/docs/using/envs.html)\n",
" - Play with a visualisation/GUI package!\n"
]
},
{
"cell_type": "markdown",
"metadata": {
"slideshow": {
"slide_type": "slide"
}
},
"source": [
"## Summary\n",
"* Refreshed your basic Python skills\n",
"* Python builtins not included in standard basics course\n",
" - Tuples, Dictionaries, Generators\n",
"* Software data structuring"
]
},
{
"cell_type": "markdown",
"metadata": {
"slideshow": {
"slide_type": "subslide"
}
},
"source": [
"## Summary (ctd)\n",
"* Covered the language and concepts for Object Orientation (Transferable!)\n",
"* OOP Implementation in Python \n",
"* `Read` the packages you use daily\n",
"* Create maintainable packages for yourself and scientific community"
]
},
{
"cell_type": "markdown",
"metadata": {
"collapsed": true,
"slideshow": {
"slide_type": "subslide"
}
},
"source": [
"# Thank You\n",
"\n",
"P.R.Chambers@soton.ac.uk"
]
}
],
"metadata": {
"celltoolbar": "Slideshow",
"kernelspec": {
"display_name": "Python 3",
"language": "python",
"name": "python3"
},
"language_info": {
"codemirror_mode": {
"name": "ipython",
"version": 3
},
"file_extension": ".py",
"mimetype": "text/x-python",
"name": "python",
"nbconvert_exporter": "python",
"pygments_lexer": "ipython3",
"version": "3.5.1"
}
},
"nbformat": 4,
"nbformat_minor": 0
}