Presentation is loading. Please wait.

Presentation is loading. Please wait.

Programming for Geographical Information Analysis: Core Skills

Similar presentations


Presentation on theme: "Programming for Geographical Information Analysis: Core Skills"— Presentation transcript:

1 Programming for Geographical Information Analysis: Core Skills
Exceptions / Functional Programming

2 This lecture Exceptions Functional programming

3 Problems Debugging is fine and dandy, but remember we divided problems into compile-time problems and runtime problems? Debugging only copes with the former. What about problems at runtime? To understand the framework for dealing with this, we need to understand Exceptions.

4 Exceptions When something goes wrong we don’t want the program to crash. We want some way of doing something about it. When Python detects an problem, it generates an Exception object at that point in the running code which represents that problem. We can catch these and do something with them.

5 try-catch If we suspect code might generate an exception, we can surround it with a try-catch compound statement. Python terminates the execution where the exception is raised and jumps to the except block (no other code in the try block happens): import random try: a = 1/random.random() # Random generates number in # range 0,1 so # ZeroDivisionError possible except: a = 0 print("Done") However, this catches all and any exception, including those generated by ending the program normally or with the keyboard. As the documentation notes: “Python uses the “termination” model of error handling: an exception handler can find out what happened and continue execution at an outer level, but it cannot repair the cause of the error and retry the failing operation”

6 Try-Catch One of the few examples of manifest typing in Python.
import random try: a = 1/random.random() # Random generates number in # range 0,1 so # ZeroDivisionError possible except ZeroDivisionError : a = 0 print("Done") One of the few examples of manifest typing in Python. For a list of built in exceptions, see: If you want to catch a generic exception, catch Exception as this won't catch normal shutdown operations.

7 unhandled exceptions If the exception isn't handled, it moves to wherever the code was called from, and repeats this through each object and function. If there isn't a handler on the way, the code breaks and a stack back trace (or "stacktrace") and error message is printed. The stacktrace lists all the places the exception has bounced through. The exception to generating the stacktrace is the SystemExit exception, which is generated when the program closes.

8 If you want to catch all exceptions, catch the generic Exception
If you want to catch all exceptions, catch the generic Exception. which encompasses most exceptions within its remit. This catches everything except SystemExit, which happens when a program ends, and KeyboardInterrupt, which happens when a user ends a program with CTRL-C or equivalent. If you want to catch all exceptions including these, catch BaseException. We'll come back to how this works when we look at class inheritance.

9 Catch more than one error type symultaneously
import random try: a = 1/random.random() except (ZeroDivisionError, SystemError) : a = 0 print("Done") Not that a SystemError is likely here!

10 Catch more than one error type alone
import random try: a = 1/random.random() except ZeroDivisionError: a = 0 except SystemError: a = None print("Done")

11 User-defined exceptions
The raise keyword, on its own inside a except clause, will raise the last exception. You can raise your own exceptions: a = random.random() if a == 0: raise ZeroDivisionError else: a = 1/a Or you can make your own types. We'll see how when we see how to make subclasses.

12 Interrogating the exception
import random try: a = 1/random.random() except ZeroDivisionError: a = 0 except SystemError as se: print(se) print("Done")

13 Else If there's something that must happen only if exceptions aren't raised, put in a else clause: import random try: a = 1/random.random() except: a = 0 else: print(a) # Exceptions here not caught. print("Done") As the documentation notes: "The optional else clause is executed if and when control flows off the end of the try clause. [2] Exceptions in the else clause are not handled by the preceding except clauses."

14 Finally If there's something that absolutely must happen, put in a finally clause and Python will do its best to do it exception raised or not. import random try: a = 1/random.random() except: a = 0 finally: if a == None: a = -1 print("Done") For more on try statements, see:

15 Finally details During this period, any exceptions are saved, to be re-raised after the finally. Finally clauses will always happen, even if there's a return beforehand. Exceptions in the finally clause are nested within the prior saved exception. Break and return statements within finally will delete the saved exception. If the try is in a loop, continue will execute the finally clause before restarting the loop, and break will execute the finally before leaving. As the documentation notes: "If finally is present, it specifies a ‘cleanup’ handler. The try clause is executed, including any except and else clauses. If an exception occurs in any of the clauses and is not handled, the exception is temporarily saved. The finally clause is executed. If there is a saved exception it is re-raised at the end of the finally clause. If the finally clause raises another exception, the saved exception is set as the context of the new exception. If the finally clause executes a return or break statement, the saved exception is discarded…"

16 Exiting the system If you want to exit the system at any point, call: import sys sys.exit() This has an option: sys.exit(arg) Which is an error number to report to the system. Zero is usually regarded as exiting with no issues. For more on this, see: See also errno, a special module on system error codes:

17 Making your own exceptions
Use either Exception or one of the subclasses of BaseException as the base class for an exception class. The documentation suggests avoiding extending BaseException directly, presumably because it is too generic. Then: raise ExceptionClass This will generate a new object of that type, calling the zero-argument constructor. For other complexities, such as exception chaining and attaching stacktraces, see: Catches catch subclasses of the caught class, and not its superclasses, so a catch catches only more specific sub-exceptions.

18 This lecture Exceptions Functional programming: Functions as objects. Functions that work with functions.

19 Declarative Languages
Declarative Languages usually describe the end point first and the program works to get there. One subset of these are Functional Languages where one passes one procedure to another in ever more complex nesting in order to calculate some end result defined by the first layer of nesting. Generally stateful elements like variables are discouraged; the code is constructed from expressions rather than full statements. To return to declarative languages… some declarative languages are also Fifth Generation Languages, though frequently they are not as they involved coding. The general idea is the same though – to describe what you want to end up with, and work from that to generate the solution.

20 text = input("Type a number to add to four")
value = int(text) answer = 4 + value print (answer) print ( 4 + int( input( "Type a number to add to four“ ) Generally stateful elements like variables are discouraged; the code is constructed from expressions rather than full statements. In the above, for example (which is a functional version of the original shown in grey), we start with the fact that we want to print something, and work down adding code to generate the components of the solution. Note, in particular, that we have got rid of the stateful elements – the variables. There is not record of the answers beyond their use (so called “side effects”). This cuts out the opportunity for code elsewhere to alter the state of the solution in error, but also makes coding much harder. It doesn’t especially matter if you don’t follow this now, we will come back to it.

21 Python Functional languages are increasingly popular as everything is contained within the nesting; this means it can be split up easily so different nests run on different processors on machines with more than one processor (which is usual now) – we’ll come back to this. Python is a third generation imperative language, but with functional bits. Such hybrids are generally the direction of travel. Most languages are converging on something which is a mix of functional and imperative programming. For example, R, a functional language, comes across as very imperative, whereas Java, which is very imperative by tradition, has introduced many functional elements. Other declarative programming styles have had less effect.

22 Functional programming
In general, Python is functional-ish in places. The functional elements concentrate on the application of functions directly to e.g. sequences:

23 Functions as objects In Python, pretty much everything is an object, including functions. This means you can do this: def func(): pass a = funct a() print(a)

24 Efficiency Indeed, it is more efficient. If we do: a = B() a.c() This needs to construct a new reference to the function c each it is called. If we do this: d = a.c # Note lack of paretheses when talk about name. d() We just use the old call. Function calls are amongst the most processor intensive standard language elements, so this can make considerable savings.

25 Function 'fun' This means it is possible to assign functions to objects. (With the types library it is also possible to assign them to classes currently being used) class A(): def prt(self): print("hi") def prrrt(): print("changed") a = A() a.prt() a.prt = prrrt

26 Now I am become Death, the destroyer of prints
>>> print = 0 >>> print() TypeError: 'int' object not callable.

27 This lecture Functions as objects. Functions that work with functions.

28 Functions Also means functions can be passed into other functions: sort (list, key=str.upper)

29 Builtins sorted(iterable, key=None, reverse=False) As we've seen, returns a copy of iterable, sorted. The optional arguments must be kwargs. "reverse" will sort in reverse if set to True. "key" should be a function within the data of one argument that generates a value for comparing during sorting. e.g. key = str.lower Will sort as if all strings are lowercase (otherwise all capitals are more important than all lowercase) . Basically, it calls the method and passes in each item, and sorts the returned values.

30 Builtins: lists map(function, iterable, …)
Applies function to iterable as an iterator: for value in map(ord, ["A","B","C"]): # ord returns ACSII numbers print(value)

31 Lambdas "headless" or "anonymous" functions: i.e. short functions without names. Can only contain expressions, not statements (e.g. variable assignments). for square in map(lambda x: x**2, [1,2,3,4,5]): print(str(square)) # 1, 4, 9, 16, 25 for added in map(lambda x,y: x+y, [1,2,3,4,5],[1,2,3,4,5]): print(str(added)) # 2, 4, 6, 8, 10 b = "fire walk with me".split(" ") for a in map(lambda x: x[::-1], b): print(a) # erif klaw htiw em Beware adding lambdas to sequences as they get added as functions, not the result of functions:

32 Reduce functools.reduce(function, iterable)
Applies function cumulatively, so: def power(a, b): return a ** b import functools c = [2,3,4,5] d = functools.reduce(power, c) # (((2*3)*4)5) print(d) or reduce(lambda x, y: x+y, [1, 2, 3, 4, 5]) # ((((1+2)+3)+4)+5) x is accumulator, y is each subsequent value. functools.update_wrapper: wraps around functions, passing in and returning parameters from another function that wraps around it.

33 Filter filter(function, sequence) filters out anything in sequence which function says is false. See also various iterators that apply functions to sequences in:

34 Decorators Enhance functions by wrapping them in code.
e.g. Least Recently Used cache: @lru_cache(maxsize=32) def get_lat_long(location): return lookup(location) for c in "Leeds", "Manchester", "Sheffield", "Liverpool", "Leeds": print(get_lat_long(c)) Saves the last 32 runs of this function for quick repeat; "memoizing" For more, see:

35 List comprehensions Essentially slightly easier and more flexible lambdas for setting up lists: listA = [1,2,3,4] listB = [2*x for x in listA] print(listB) # [2, 4, 6, 8] listB = [2* x for x in range(1:5)] print(listB) # [2, 4, 6, 8] listB = [2*x for x in listA if x % 2 == 0] # Even numbers only print(listB) # [4, 8] Can also be used to apply a method to everything in a list. listB = [ord(x) for x in listA]

36 Combinatorics List comprehensions can be used to generate combinations of values: listA = [(x, y) for x in [1,2,3] for y in ["a","b","c"]] print(listA) [(1, 'a'), (1, 'b'), (1, 'c'), (2, 'a'), (2, 'b'), (2, 'c'), (3, 'a'), (3, 'b'), (3, 'c')] Essentially like developing tuples from nested for-each loops. For a tuple, you have to use a parenthesis here or you get a syntax error.

37 Generator expressions
Generator expressions: exactly like list comprehensions, but generate an item at a time, so more memory efficient: listA = [1,2,3,4] notListB = (2* x for x in range(1,5)) for i in notListB : print(i) # Works print(notListB) # Doesn't print contents as don't exist.

38 Scope The scope of variables created inside lambdas and list comprehesions is the function outlined, which some people like, as it doesn't create free variables slooping around the code afterwards. From the docs: “A scope is a textual region of a Python program where a namespace is directly accessible. “Directly accessible” here means that an unqualified reference to a name attempts to find the name in the namespace.” “For example, assume we want to create a list of squares, like: >>>>>> squares = [] >>> for x in range(10): ... squares.append(x**2) ... >>> squares [0, 1, 4, 9, 16, 25, 36, 49, 64, 81] Note that this creates (or overwrites) a variable named x that still exists after the loop completes. We can calculate the list of squares without any side effects using: squares = list(map(lambda x: x**2, range(10))) or, equivalently: squares = [x**2 for x in range(10)] which is more concise and readable. A list comprehension consists of brackets containing an expression followed by a for clause, then zero or more for or if clauses. [(x, y) for x in [1,2,3] for y in [3,1,4] if x != y] If the expression is a tuple (e.g. the (x, y) in the previous example), it must be parenthesized."

39 Generator functions def count(): a = 0 while True:
yield a # Returns control and waits next call. a = a + 1 b = count() print (next(b)) Where the condition is just set to True, they will generate infinite sequences. Must be attached to a variable so they are called from the same instance each time and the method level variables aren't wiped; this doesn't work: print (next(count())) Can also be used: for i in count(): print(i) To pass values into these, see:

40 Review List comprehensions
listA = [1,2,3,4] listB = [2*x for x in listA] print(listB) # [2, 4, 6, 8] listB = [2* x for x in range(1:5)] print(listB) # [2, 4, 6, 8] listB = [2*x for x in listA if x % 2 == 0] # Even numbers only print(listB) # [4, 8] Can be used to apply a method to everything in a list.

41 Review Lambdas and Map for square in map(lambda x: x**2, [1,2,3,4,5]): print(str(square)) # 1, 4, 9, 16, 25 for added in map(lambda x,y: x+y, [1,2,3,4,5],[1,2,3,4,5]): print(str(added)) # 2, 4, 6, 8, 10 b = "fire walk with me".split(" ") for a in map(lambda x: x[::-1], b): print(a) # erif klaw htiw em Beware adding lambdas to sequences as they get added as functions, not the result of functions:


Download ppt "Programming for Geographical Information Analysis: Core Skills"

Similar presentations


Ads by Google