Download presentation
Presentation is loading. Please wait.
Published byὈλυσσεύς Τρικούπη Modified over 6 years ago
1
Programming for Geographical Information Analysis: Core Skills
Functions
2
Review if (condition): # Statement/s else: while (condition): # Adjust condition for target in sequence/range/slice: # Statements using target
3
2D loops data = [ [0,1], [2,3], [4,5] ] for row in data:
for item in row: item = 10 for i,j in data: print (str(i) + " " + str(j)) for i in range(len(dataA)): for j in range(len(dataA[i])): dataA[i][j] = dataB[i-1][j-1]
4
This lecture Functions Function parameters Scope Documentation
This lecture we'll look at functions, how to make them, and how to document them so other people can use them.
5
Functions So far we've been using functions from the builtin module, which is automatically loaded in most cases. Occasionally we've accessed functions in other modules using import. import random a = random.random() We'll now look at building our own functions.
6
Basic function Functions are a block begun with a function declaration or header: def function_name (): # Suite of statements # Code calling function_name In a standard script, functions need to be defined before you use them.
7
Example def printit(): print("hello world") printit() # The function can be called printit() # as many times as you like. printit()
8
Passing info in Functions can be parameterised; that is, made flexible through variables which are set up when the function is called. Note that the arguments sent in are allocated to the parameter variables. def printit(text): print(text) printit("hello world") a = "hi you" printit(a)
9
Variable labels What is happening here is we're attaching two labels to the same variable: def printit(text): print(text) a = "hi you" printit(a) The effect this has depends whether the variable is mutable or immutable. "hi you" a text This is known as "pass by assignment", though sometimes people also use "pass by reference" for this (though there are also subtly different uses for that - for example, the reference to the variable is copied). The third type of passing is "pass by value", where the value is copied and passed. Python uses assignment.
10
Variable labels For mutable variables, changes inside the function change the variable outside: def printit(text): text[0] = text[0] + ", Pikachu" print(text[0]) a = ["hi you"] printit(a) # Call the function. print(a) # Check the value of a. As lists are mutable, the value in the list outside the method has changed to "hi you, Pikachu".
11
Variable labels For immutable variables, changes inside the function just create a new variable inside the function. Even though it has the same name, it isn't the same variable: def printit(text): text = text + ", Pikachu" # New text variable created. print(text) a = "hi you" printit(a) # Call the function. print(a) # Check the value of a. As text is immutable, the value in the list outside the method is still "hi you".
12
Passing info in Python (unlike many languages) doesn't worry about the type passed in def printit(var): print(str(var)) printit("hello world") printit(430) printit(430.0)
13
Getting values back By default, functions invisibly return None (you can do so explicitly, as is sometimes useful). But you can pass values back: def get_pi(): return 3.14 pi = get_pi() print(str(pi)) print(str(get_pi())) If something comes back, you either need to attach a label to it, or use it. You can find out the type of a returned object with type(variable_name). If you need to return more than one thing, return a tuple. For more detail on other ways to get stuff from functions, see:
14
This lecture Function parameters Functions Scope Documentation
This lecture we'll look at functions, how to make them, and how to document them so other people can use them.
15
Multiple in (single out)
def add(num1, num2): return num1 + num2 answer = add(20,30) Arguments are allocated to parameters like this just on position. These are called positional arguments.
16
Defaults You can set up default values if a parameter is missing: def add(num1 = 0, num2 = 0): return num1 + num2 answer = add(3) With this type of parameter, positional arguments are allocated left to right, so here, num1 is 3, and num2 is nothing.
17
Ordering In the absence of a default, an argument must be passed in. If we did this: def add(num1=0, num2): return num1 + num2 answer = add(3) print(answer) It wouldn't be clear if we wanted num1 or num2 to be 3. Because of this, parameters with defaults must come after any undefaulted parameters. All parameters to the right of a defaulted parameter must have defaults (except * and ** - which we'll see shortly).
18
Keyword arguments You can also name arguments, these are called keyword arguments or kwargs. Note that this use of "keywords" has nothing to do with the generic use for words you can't use as identifiers. def add(num1, num2): return num1 + num2 answer = add(num2 = 30, num1 = 50) Here we've swapped the order of the positional arguments by naming the parameters to assign their values to. It's fairly unfortunate that the same term is used for both types of keyword.
19
Mixed arguments def add(num1, num2): return num1 + num2
answer = add(3, num2 = 5) Note that once Python has started to deal with kwargs, it won't go back and start allocating positional arguments, so all positional arguments have to be left of the kwargs. You can force parameters to the right to be kwargs by putting a * in the parameters Note also that you can't allocate to the same parameter twice: answer = add(3, num1 = 5)
20
Flexible parameterisation
You can allow for more positional arguments than you have parameters using *tuple_name, thus: def sum (num1, num2, *others): sum = num1 sum += num2 for num in others: sum += num return sum answer = sum(1,2,3,4,5,6,7) *is known as the iterable un/packing operator. If nothing is allocated, the tuple is empty.
21
Iterable unpacking You can equally use the * operator with lists or tuples to generate parameters: def sum (num1, num2, num3, num4): return num1 + num2 + num3 + num4 a = [1,2,3,4] answer = sum(*a) # Note that these can also be in the middle. a = [10,20] answer = sum(1,*a, 2)
22
Iterable unpacking def sum(*nums): sum = 0 for num in nums: sum += num
You can, therefore, use these in both positions: def sum(*nums): sum = 0 for num in nums: sum += num return sum a = [1,2,3] answer = sum(*a)
23
Flexible parameterisation
The same can be done with **dict_name (** is the dictionary unpacking operator), which will make a dictionary from unallocated kwargs: def print_details (a, **details): first = details["first"] surname = details["surname"] print (first + " " + surname + " has " + a + " pounds") print_details("5", first="George", surname="Formby") Note that you can't use one of these to override other variables. If nothing is allocated, the dictionary is empty.
24
Unpacking dictionaries
You can also use ** to create keyword arguments: def print_details(a, first, surname): print (first + " " + surname + " has " + a + " pounds") d = {"first":"George","surname":"Formby"} print_details("5",**d)
25
Unpacking quirks Note also that just as with standard arguments *tuple_name must come before **dict_name if you use both. *tuple_name must come after positional parameters and **dict_name after kwargs. It is, therefore usual to place them after their associated variables or together at the end. def func(a,b,*c,d,**e) # d has to be a kwarg. def func(a,b,d,*c,**e) # abd,bd,or d can be kwargs. But this is also how you force variables to be kwargs. Note that attempts to do this: def a(**d): print(d) a(d={1:2}) End up with a dictionary variable inside the dictionary, with the same name: {d:{1,2}}
26
Summary of order Parameters: Arguments: Undefaulted parameters
*tuple_name Forced keyword parameters **dict_name Arguments: Positional arguments Keyword arguments Unpacked lists, tuples, or dicts may be anywherekis For more on unpacking operators, see:
27
Nested functions Note that there is nothing to stop you having functions within functions: def a(): print("1") def b(): print("2") b() a() One restriction is that if your function is inside a loop, you can't break inside the function.
28
This lecture Functions Function parameters Scope Documentation
29
Scope Scope is the space within which a variable label exists and can be used. Python talks of names being bound to the code in an area and that determining its scope. Binding in Python is usually through assignment. So far, we haven't had to deal with it. Loops/if compound statements don't effect it: a = [1] # "a" can be declared here for i in 1,2,3,4: a = [1] # or here. a[0] = i print (a) # 1 ,2 ,3 ,4 print (a) # 4 print (i) All the "a" here are treated as same object wherever they are first used, and "i" can be seen everywhere after it has been first declared.
30
Function scope Functions have more complicated scoping. Although the declaration of a function looks similar to a compound statement clause header, the function declaration generates a block, which has scoping rules. Starting with variables labels made inside a function: def a (): b = 10 print(b) a() This will run the print statement within the function, but fail for that outside because b has been first allocated inside the function. This makes b a local variable, only available within the function and functions within it.
31
Function scope One solution in a script is to define the variable outside the function: b = 10 def a (): print(b) a()
32
Function scope One solution in a script is to define the variable outside the function: b = 10 # b here is a "global variable" # it can be see everywhere there # isn't a variable of the same # name assigned. def a (): print(b) # b here is a "free variable" i.e. # defined outside the current block. a() print(b)
33
Function scope As soon as you declare a variable inside a block, it is a new variable label. Contrast: b = 10 def a (): b = 20 print(b) # Prints 20. print(b) # Prints 10. a() with: print(b) # Prints 10;
34
Global variables Variables outside of functions in scripts are global: in theory they can be seen anywhere. However, the rule about local assignments creating local variables undermines this. To force a local assignment to a global variable, use the global keyword, thus: b = 10 def a (): global b b = 20 print(b) # Prints 20. print(b) # Prints 10. a() print(b) # Now prints 20 as the function # changes the global b. For more on this, see:
35
Nested scopes With nested functions you can imagine situations where you don't want to use a global, but do want a variable across all the functions: a = 1 def f1(): a = 2 def f2(): a = 3 print(a) # Prints 3. f2() print (a) # Prints 2 - but we'd like 3. f1() print(a) # Prints 1.
36
Nested scopes We can achieve this with the nonlocal keyword, which propagates variable labels out one nest (unless there they are set to global). a = 1 def f1(): a = 2 def f2(): nonlocal a a = 3 print(a) # Prints 3. f2() print (a) # Prints 3. f1() print(a) # Prints 1.
37
Variable labels Note that variable labels in function declarations are also local to the function, but we're only talking about labels here, not values: def a(b): print(b) c = "hi you" a(c) Here, b is local to the function, but the "hi you" referred to can be seen anywhere there's a label attached to it. "hi you" c b
38
This lecture Documentation Scope Functions Function parameters
This lecture we'll look at functions, how to make them, and how to document them so other people can use them. We also need to understand testing, so we can prove to others our code works.
39
Style Generally style is built into Python, as it demands indents. However, good Python style is set out in PEP8: As soon as you start writing functions, there is the chance someone else might like to use your code. Given this, we need to understand good code style and documentation.
40
Style: main elements Use 4 spaces per indent, rather than tabs.
Use blank lines before function defs, and to separate logical code units. function_names; variable_names; keyword_named_variables_; ClassNames; modname; CONSTANT_NAMES Keep lines to 79 characters or less. aaaaaaaaaa aaaaaaaaaa aaaaaaaaaa aaaaaaaaaa aaaaaaaaaa aaaaaaaaaa aaaaaaaaaa aaaaaaaaa Spaces around operators and after commas, but not just inside (){}[]: a = (1, 2) Indent comments to the level of the code referred to. Note that it is bad practice to call a variable the same as a keyword, but you can use a trailing underscore to distinguish if you must.
41
Documentation Docstrings are automatically extracted to describe your code. They are triple-double quote enclosed comments after, for example, function declarations. For some peculiar reason, they are written as commands not descriptions def add(num1, num2): """Add two numbers.""" Here is an example of a multi-line docstring from the documentation: >>> >>> def my_function(): """Do nothing, but document it. ... No, really, it doesn't do anything. """ pass >>> print(my_function.__doc__) Do nothing, but document it. No, really, it doesn't do anything.
42
Example def add (num1, num2): """ Add two numbers. Keyword arguments: num1 -- an integer or double number (no default) num2 -- an integer or double number (no default) Returns: The sum of the numbers. return num1 + num2 Style details:
43
PyDoc PyDoc is the documentation system distributed with Python. Best way to invoke it is to import any code, and then type: >>> help(x) Where "x" is a function, module, dotted object method etc. If you want to see docstrings, then: print(function_name.__doc__) """ Add two random numbers together Requires no setup. import random def add (num1, num2): Add two numbers. Postional arguments: num1 -- an integer or double number (no default) num2 -- an integer or double number (no default) Returns: The sum of the numbers. return num1 + num2 numA = random.random() * 10 numB = random.random() * 10 print(add(numA, numB)) # Download this file # Run it with # python -i docs.py # This will run the file and leave you at the command prompt, with the file imported. # Now try # help(add)
44
PyDoc To generate a webpage from documentation, at command prompt:
pydoc -w filename (note without the .py) For more, see:
45
Other software There is a starter list of alternative software at: A popular one is Sphinx, which comes with Anaconda: apidoc Make a docs directory, and copy docs.py into it. Open a command prompt. sphinx-quickstart Root path for documentation[.]: <ENTER> Separate source and build directories(y/n)[n]: <ENTER> Name prefix for templates and static directories[_]: <ENTER> Project name: docstest Author: Your name Project version: 1 Project release: <ENTER> Project language [en]: <ENTER> Source file suffix [.rst] Name of your master document (without suffix)[index]: <ENTER> Do you want to use the epub builder (y/n) [n]: <ENTER> autodoc: automatically insert docstrings from modules (y/n) [n]: <ENTER> doctest: automatically test code snippets in doctest blocks (y/n) [n]: <ENTER> intersphinx: link between Sphinx documentation of different projects (y/n) [n]: <ENTER> todo: write "todo" entries that can be shown or hidden on build (y/n) [n]: <ENTER> coverage: checks for documentation coverage (y/n) [n]: <ENTER> pngmath: include math, rendered as PNG images (y/n) [n]: <ENTER> mathjax: include math, rendered in the browser by MathJax (y/n) [n]: <ENTER> ifconfig: conditional inclusion of content based on config values (y/n) [n]: <ENTER> viewcode: include links to the source code of documented Python objects (y/n) [n]: <ENTER> Create Makefile? (y/n) [y]: <ENTER IF ON MAC> Create Windows command file? (y/n) [y]: <ENTER IF ON WINDOWS> Make a directory _source Copy in docs.py sphinx-apidoc -o _source _source Copy index.rst into _source sphinx-build -c . -b html _source _build
46
DocTest DocTest runs short tests built into your documentation. """
Add two numbers together. >>> add(1,2) 3 def add (num1, num2): return num1 + num2 """ Add two random numbers together. Requires no setup. >>> add(1,2) 3 import random def add (num1, num2): Add two numbers. Postional arguments: num1 -- an integer or double number (no default) num2 -- an integer or double number (no default) Returns: The sum of the numbers. return num1 + num2 numA = random.random() * 10 numB = random.random() * 10 print(add(numA, numB))
47
DocTest To run: python -m doctest -v filename.py Or:
>>> import doctest >>> import filename >>> doctest.testmod(filename, verbose=True) See: """ Add two random numbers together Requires no setup. >>> add(1,2) 3 import random def add (num1, num2): Add two numbers. Postional arguments: num1 -- an integer or double number (no default) num2 -- an integer or double number (no default) Returns: The sum of the numbers. return num1 + num2 numA = random.random() * 10 numB = random.random() * 10 print(add(numA, numB))
48
Unit tests Write the tests first, defining success. Then write the code that satisfies the tests. For example: #docs.py def add(num1, num2): return num1 + num import docs def test_add(self): self.assertEqual(docs.add(1,2), 3) See: For a list of assertion functions:
49
Write a test class # test.py import unittest import docs class TestDocs(unittest.TestCase): def test_add(self): self.assertEqual(docs.add(1,2), 3) if __name__ == '__main__': unittest.main() Usual to write tests into their own class. We'll come back to what a class is later in the course. """ Add two random numbers together Requires no setup. import random def add (num1, num2): Add two numbers. Postional arguments: num1 -- an integer or double number (no default) num2 -- an integer or double number (no default) Returns: The sum of the numbers. return num1 + num2 numA = random.random() * 10 numB = random.random() * 10 print(add(numA, numB))
50
Write a test class Run it: python tests.py Or verbose: python -m unittest -v tests.py In the latter case you can delete the: if __name__ == '__main__': unittest.main()
51
Test Driven Development
Write the tests first, and then write the software to match the tests. Good for working in teams: if the code matches the test, it shouldn't matter how it does it. All team code uploaded is tested in a continuous integration process to make sure it works before integration.
Similar presentations
© 2025 SlidePlayer.com. Inc.
All rights reserved.