#!/usr/bin/env python
# coding: utf-8
# # Lecture 2: Python Syntax
# # Lecture 2: Python Syntax
# - Syntax and basics of Python
# - The Python interpreter
# - Goal: kickstart your Python knowledge
# to the same level as M-students have in Matlab
#
# Recommended reading:
# - [1] Chapter 1.6, 2.1-2.2, 3, 4, 5, 6, 8
# - [2] Section 3, 4, 5
#
# [1] C. Horstmann: Python for everyone
# [2] Python tutorial: https://docs.python.org/3/tutorial/
# ## Contents
# - Basic Types
# - `int`, `float`, `str`, `type`
# - type conversion
# - Data structures
# - `str`, `tuple`, `list`, `set`, `dict`
# - indexing and slicing, `del`
# - Control flow statements
# - Conditionals `if`, `elif`, `else`
# - `while`, `for`, `enumerate`, `zip`, `itertools`, iterators
# - list comprehensions, one-liners
#
# - Operators
# - Numerical operations, inplace operations
# - Division in Python 2 and 3
# - Boolean operators, bitwise operators
# - Functions
# - `def`, `lambda`, `return`, scope
# - `*args`, positional arguments
# - `**kwargs`, keyword arguments (with default values)
# - argument expansion, arbitrary argument list
# - References and copies
# - Introspection and help
# - `dir`, `help`
# - doc-strings
# - Packages and modules
# - `import`, `from x import y as z`
# - Error messages
# # Basic Types
# - `int`, `float`, `str`
# - `type` and type conversion
# # Values and data types
# You can check the type of almost any value
# In[ ]:
type(3.14)
# In[ ]:
type('Hello World')
# In[ ]:
type(100)
# In[ ]:
type(True)
# The types `float`, `int`, `str`, `bool`, etc. have the type: `type`
# In[ ]:
type(int)
# ## Type conversion
# * Changing types is possible through the built in functions
# In[ ]:
x = int('123')
type(x)
# In[ ]:
x = str(45.3)
type(x)
print(x, type(x))
# But only if there the conversion makes sense
# In[ ]:
x = int('Carl')
# # Data structures
# - `str`
# - `tuple`, `list`, `set`, `dict`
# - indexing and slicing
# - `del`
# ## Strings: `str`
# - Similar to Matlab and other languages.
# In[ ]:
a = 'Hello World'
b = "Hello World"
c = """Hello World"""
d = '''Hello World'''
# - Strings are immutable in Python
# Immutable /ɪˈmjuːtəb(ə)l/:
*unchanging over time or unable to be changed*
# In[ ]:
a = 'Hello World'
a[0] = 'Y'
# * Line breaks and special characters just like in Matlab
# In[ ]:
a = 'Hello\nWorld'
print(a.upper())
# * More on strings in Lecture 3
# * E.g. string methods
# In[ ]:
', '.join(s for s in dir('') if not '__' in s) # Brain-teaser! :)
# ## Tuples: `tuple()`, `()`
# - Ordered (immutable) collection of objects
# - Often used to store objects of different types
# In[ ]:
x = (3.14, int, 'John')
x = 3.14, int, 'John' # paranthesis are optional
print(x)
# * Tuple-methods
# In[ ]:
', '.join(s for s in dir(tuple()) if '__' not in s)
# In[ ]:
help(tuple().index)
# In[ ]:
t = 'A', 'B', 'C'
t.index('C')
# - Tuples are immutable (like strings)
# and can _not_ be changed in place
# In[ ]:
x = (3.14, int, 'John')
x[0] = 3.1415
# ## Tuple unpacking
# * A convenient syntax for splitting up all the components of a tuple
# In[ ]:
x = 3.14, int, 'John'
a, b, c = x
print(a)
# * Also works for `list`, `set` and `dict`
though this usage is more rare
# In[ ]:
x = ['Hello', 'Foo', 'World']
a, b, c = x
print(a, c)
x = {1, 2, 2}
a, b = x
print(a, b)
x = {'John': 37, 'Sara': 25, 'Lisa': 45}
a, b, c = x.items()
print(a, c)
# ## Lists: `list()`, `[]`
# - Lists are your standard storage option
# - Mixing object types is possible but strongly discouraged (use `tuple`)
# In[ ]:
x = [43, 10, 15]
len(x)
# * Can store any object (including other lists)
# In[ ]:
y = ['Hello', 'World']
z = [[1, 2, 3], [4, 2], [9, 4, 2]]
# * Lists can be changed in place
# In[ ]:
z[2] = 41
print(z)
# ## `list` methods
# In[ ]:
', '.join(s for s in dir(list()) if '__' not in s)
# In[ ]:
help(list().sort)
# In[ ]:
x = [4,67,2,10]
x.sort()
print(x)
# ## Indexing and slicing
# - Using square brackets `[]`
# In[ ]:
x = [43, 10, 15]
x[1]
# - **Note:** indexing starts from 0
# Index measures distance from start.
# - Negative indices are counted from the end
# - `-1` is the index of the last element
# In[ ]:
x[-1]
# ### Slicing
# - Similar to that of Matlab, but in the order -`start:end:increment`*
# ```
# Matlab Python
# x(a:b:c) ~ x[(a-1):c:b]
# end ~ -1 or nothing:
# ```
# - True for single index
# ```
# x(end) ~ x[-1]
# ```
# - But `-1+1 = 0` so when using slicing, the syntax is to leave it out
# ```
# x(1:2:end) ~ x[::2]
# x(7:end) ~ x[6:]
# ```
# ## Slicing examples
# - Note that ranges are given in a half open interval description, `a:b` $= [a,b)$
# In[ ]:
x = ['my', 'short', 'list', 'of', 'strings']
# In[ ]:
x[0:2]
# In[ ]:
x[1:]
# In[ ]:
x[:1]
# In[ ]:
x[:]
# In[ ]:
x[0::2]
# In[ ]:
x[0:2:3]
# In[ ]:
x[::-1]
# ## Sets: `set()`, `{}`
# * Sets are containers of unique items
# In[ ]:
x = {1, 2, 3, 2, 7, 2, 3, 2, 6}
print(x)
# In[ ]:
x.add(8)
print(x)
# In[ ]:
y = x.intersection({1, 4, 5, 6, 10})
print(y)
# - `set`-methods
# In[ ]:
', '.join(s for s in dir(set()) if '__' not in s)
# In[ ]:
help(set().intersection)
# - Useful for "set operations": E.g. Find common elements in two lists
# In[ ]:
A = ['Ada', 'Beda', 'Emil', 'Emilia']
B = ['Ada', 'Emilia', 'Eva', 'Greta']
s = set(A).intersection(B)
l = list(s)
print(l)
# ## Dictionaries: `dict()`, `{}`
# - Important data structure that is very common in Python programs
# - Set of "key-value" pairs
# - Also called maps
# ```python
# x[key] = value
# ```
# representing the mapping of $key \to value$
# In[ ]:
x = {'John': 37, 'Sara': 25, 'Lisa': 45}
# In[ ]:
x['Lisa']
# It is very common to create an empty dictionary and
add new entries as required (e.g. values are read from a file)
# In[ ]:
x = dict() # or x = {}
x['John'] = 37
x['Sara'] = 25
x['Lisa'] = 45
# In[ ]:
print(x['John'])
# * Check for existence of keys using `in` and `not in`
# In[ ]:
print( 'Lisa' in x )
print( 'Jeff' not in x )
# * You get access the `keys`, `values`, or `(key,value)` pairs
as iterators (more on this later):
# In[ ]:
print( x.keys() )
print( x.values() )
print( x.items() )
# ## Deleting: `del`
#
# - `del` can be used to remove elements in `list`, `dict`
# In[ ]:
x = [4,5,6,4,3,5,1]
del x[3:]
print(x)
# In[ ]:
x = {'John': 37, 'Sara': 25, 'Lisa': 45}
del x['John']
print(x)
# There are also methods for "consuming" elements, like `.pop`
# In[ ]:
sara_age = x.pop('Sara')
print(sara_age)
print(x)
# - `del` can also be used to remove an object
(i.e. force garbage collection)
# In[ ]:
x = 10
del x
print(x)
# # Control flow statements
# - Conditionals `if`, `elif`, `else`
# - Indentation
# - `while`, `for`, `enumerate`, `zip`, `itertools`, `.items()`
# - Iterators `type(enumerate(['A', 'B', 'C']))`
# - List comprehension
# - Single statement, single line;
# ## Conditionals: `if`, `elif`, `else`
# - Works as expected
# - Built in boolean values: `True`, `False`
# In[ ]:
if False:
print('Stuff here')
elif True:
print("Let's print")
print('many lines')
else:
print('Good bye!')
# - Can be used directly in assignments
# In[ ]:
a = 1
x = 1 if a > 2 else
print(x)
# ## Indentation
# - Indentation is **not** optional in Python.
# - It determines the scope in control flow!
# - Using 4 spaces per indentation is **strongly** encouraged.
# - Statements ending with colon `:` require a following indented block
# Matlab conditionals
# ```matlab
# if x
# f();
# elseif y
# g();
# end
# for x = y
# disp('foo');
# end
# if q
# end
# ```
# Python conditionals
# ```python
# if x:
# f()
# elif y:
# g()
# for x in y:
# print('foo')
# if q:
# pass # no-operation, needed to avoid syntax errors
# ```
# ## Boolean expressions
# - `in`, `not`, `and`, `or`
# In[ ]:
print( 1 in [1,2,3] )
print( 7 not in (4,3,5) )
print( 'W' in 'World' )
# In[ ]:
print( 6 != 5, 6 == 5 )
# In[ ]:
print( 6 >= 5, 6 <= 5 )
# In[ ]:
print( 6 > 5, 6 < 5 )
# In[ ]:
print( True or False )
print( True and False )
print( not True )
# ## While loops: `while`, `break`, `continue`
# - Syntax
# ```python
# while condition:
# do_stuff
# ```
# - `break` and `continue` work as in Matlab
# - Q: What is printed below?
# In[ ]:
x = 0
while x < 10:
x = x + 1
if x == 3:
continue
print(x, end=', ')
if x > 6:
break
# ## For-loops: `for`
# - Loop directly over the content in iteratable containers
# - `tuple`, `list`, `set`, `dict`, etc.
# In[ ]:
foo = ['This', 'is', 'a', 'list']
for x in foo:
print(x)
# - `range(start, end+1, step)` iterator over integer ranges
# - C.f. Matlab's colon operator: `range(a, b, c) ~ a : c : (b-1) `
# In[ ]:
for i in range(5):
print(i, end=', ')
# In[ ]:
for i in range(1, 10, 2):
print(i, end=', ')
# ### `for` ... `dict`
# * Looping over dictionaries loops over the keys
# In[ ]:
foo = {'John': 37, 'Sara': 25, 'Lisa': 45}
for x in foo:
print(x)
# * unless you specify otherwise
# In[ ]:
foo = {'John': 37, 'Sara': 25, 'Lisa': 45}
for x in foo.items():
print(x)
# * We can also directly unpack variables in for-loops
# In[ ]:
for key, value in foo.items():
print(key, "is", value, "years old")
# ### `enumerate` and `zip`
# - To get both the index and the value use `enumerate`
# In[ ]:
foo = ['This', 'is', 'a', 'list', 'of', 'strings']
for i, x in enumerate(foo):
print('counter is', i, 'and value is', x)
# - To loop over 2 (or more) sequences together use `zip`
# - `zip` "zips" the sequences into a sequence of tuples
# In[ ]:
x = [3, 7, 4, 9, 3, 0]
y = "qwerty"
z = {1, 2, 3}
for i, c, i2 in zip(x, y, z):
print(i, c, i2)
# ## Iterators
# - For performance, `zip` doesn't actually create a list
# - It returns an "iterator" that dynamically constructs tuples
# In[ ]:
z = zip(x,y)
print(z)
print(type(z))
# - If a list is **really** needed use `list`
# In[ ]:
z = list(zip(x,y))
print(z)
# ## Single statement, single line
# - `if`, `while`, `for` etc. can all be written on a single line
# when there is just 1 statement inside the block. E.g.
# In[ ]:
if 3 > 0: print('It works!')
# In[ ]:
for s in ['This', 'works', 'too!']: print(s, end=' ')
# ## List comprehensions
# - Short, convenient, and fast syntax for creating lists
# - Use with care, _will_ make code unreadable
# In[ ]:
l = list()
for i in range(5):
l.append(i**2)
print(l)
# In[ ]:
l = [k**2 for k in range(5)]
print( l )
# In[ ]:
l = [k**2 for k in range(10) if k % 2 != 0]
print( l )
# * Also works for `set` and `dict`
# In[ ]:
d = {k**2 for k in range(10)}
print(d)
# In[ ]:
l = ['John', 'Jeff', 'Carl']
d = { key : idx**2 for idx, key in enumerate(l) }
print(d)
d = {}
for idx, key in enumerate(l):
d[key] = idx**2
print(d)
# # Operators
# - Numerical operations; `+, -, *, /, **`
# - Acting on: `list`, `tuple`, `str`
# - Division in Python 2 and 3
# - Inplace; `+=, -=, *=, /=, //=`
# - Boolean operators; `==, is, in, >, <, >=, >=`
# - Bitwise operators; `>>, <<, &, |, ^`
# ## Binary operators
# In[ ]:
print('4 + 3 =', 4 + 3)
print('4 - 3 =', 4 - 3)
print('4 * 3 =', 4 * 3)
print('4 / 3 =', 4 / 3)
# ### Less obvious operators
# * `a ** b` $= a^b$
# * `a // b` $= \lfloor a/b \rfloor$
# * `a % b ` $= a \,\text{mod}\, b$
# In[ ]:
print('5 ** 3 =', 5 ** 3)
print('5 // 3 =', 5 // 3)
print('5 % 3 =', 5 % 3)
# ## **Note:** Division in Python v2 and v3
# - In Python 3
# - `a / b` is floating point division
# - `a // b` is integer division
# - In Python 2
# - `a / b` is **integer division** (unexpected!?)
# - `a / float(b)` is floating point division
# - In Python 2 `a / b` can be made floating point division by
# ```python
# from __future__ import division
# ```
# Here: Python 3
# In[ ]:
4 / 3
# In[ ]:
4 // 3
# ## Inplace assignment operators
# In[ ]:
x = 1
x += 2 # x = x + 2
x *= 3 # x = x * 3
x /= 4 # etc.
print(x)
# In[ ]:
x = 1
x //= 2
print(x)
# ## Bitwise operators
# - Binary representations:
# $$41 = 2^5 + 2^3 + 2^0 = 101001_b$$
# $$28 = 2^4 + 2^3 + 2^2 = 011100_b$$
# In[ ]:
a = 0b101001
b = 0b011100
print('a = {:d} = 0b{:06b}'.format(a, a))
print('b = {:d} = 0b{:06b}'.format(b, b))
# - Bit wise and `&`, or `|`, xor `^`
# In[ ]:
a = 0b101001
b = 0b011100
# In[ ]:
print('a & b = 0b{:06b}'.format( a & b ))
# In[ ]:
print('a | b = 0b{:06b}'.format( a | b ))
# In[ ]:
print('a ^ b = 0b{:06b}'.format( a ^ b ))
# - Bit shift operators `>>` and `<<`
# In[ ]:
print("41 >> 1 =", 41 >> 1)
print("41 << 1 =", 41 << 1)
# - `X >> 1` shifts the bits one step to the right
# $$41 >> 1 = 101001_b >> 1 = 010100_b = 2^4 + 2^2 = 20$$
#
# - This is equal to integer division by 2, `X >> 1 == X // 2`
#
# - In general: `a >> b == a // (2**b)`
# ## Operators acting on non-scalars
# - Operators are **not** the same as in Matlab
# - Different types work differently with operators
# - `string`
# In[ ]:
'Hello World' * 3
# In[ ]:
'Hello' + ' ' + 'World'
# - `list`, `tuple`
# In[ ]:
[1, 2, 3] * 3
# In[ ]:
(1, 2, 3) + (4, 5)
# # Functions
# - `def`
# - `return`
# - scope
# - `args`, positional arguments
# - `kwargs`, keyword arguments (with default values)
# - `*args`, `**kwargs`, positional and keyword argument expansion
# - arbitrary argument list
# ## Function definition: `def`
# In[ ]:
def print_value(x):
print('The value is:', x)
# In[ ]:
print_value(13)
# ## Return value(s): `return`
# In[ ]:
def my_sum(a, b):
return a + b
# In[ ]:
print(my_sum(1, 2))
# ### Multiple return values
# - Using `tuple` expansion (as advertised)
# In[ ]:
def multi_return_function(x):
return x, x**2, x**3
# In[ ]:
x = multi_return_function(3)
print(x)
# In[ ]:
a, b, c = multi_return_function(3)
print('a = ', a, ', b = ', b, ', c = ', c, sep='')
# * Wrong number of arguments in the tuple expansion is an error
# In[ ]:
a, b = multi_return_function(3)
# ### Optional arguments
# - Has to be in the end of the function argument list
# In[ ]:
def my_function(a, b=2, c=3): # b and c have default values and are optional
return a + b + c
# * These all give the same result (and will all have `a=1, b=2` and `c=3`)
# In[ ]:
print(my_function(1, 2, 3))
print(my_function(1, 2))
print(my_function(1))
# ### Key-word arguments
# - Set function parameters by name
# In[ ]:
print(my_function(a=1, b=2, c=3))
print(my_function( 1, b=2, c=3))
print(my_function( 1, 2, c=3))
print(my_function( 1, c=3, b=2))
print(my_function( 1, b=2))
print(my_function( 1, c=3))
print(my_function(a=1, c=3))
# * Parameters without a name **must** come before named parameters!
# In[ ]:
my_function(a=1, 2, 3) # Not OK!
# In[ ]:
my_function(1, b=2, 3) # Not OK!
# ### Argument expansion
# - Expand `list` and `tuple` to positional arguments using the `*` operator
# In[ ]:
def my_sum(a, b, c):
return a + b + c
# In[ ]:
x = [2, 3, 4]
s = my_sum(*x) # Uses: "a, b, c = x"
print(s)
# ### Key-word argument expansion
# - Key-word arguments can be passed using a `dict`
# - The key-value pairs are expanded using the `**` operator
# In[ ]:
def pet_info(name, kind=None, owner=None):
"""Returns a string summarizing the info on the pet."""
out = name
if kind is not None: out += ' is a ' + kind
if owner is not None: out += ' owned by ' + owner
out += '.'
return out
# In[ ]:
pets = [
dict(name='Snoopy', kind='dog', owner='Charlie'),
dict(name='Garfield', kind='cat', owner='Jon'),
dict(name='Bamse', kind='bear'),
]
for pet in pets:
print(pet_info(**pet)) # Expand dict to key-word arguments
# ### Arbitrary arguments
# - Functions taking any number of positional and key-word arguments
# - Sometimes useful, but makes the function hard to read
# In[ ]:
def my_general_argument_function(*args, **kwargs):
print('Got positional arguments:', args)
print('Got key-word arguments:', kwargs)
# In[ ]:
my_general_argument_function(
'one', 'argument', 'at', 'a', 'time',
key='words', are='very', important='!')
# ## Function variable scope
# - Functions inherit the local scope
# In[ ]:
c = 123
def sum_1(a):
return c + a
print(sum_1(1))
# In[ ]:
def sum_2(a):
c = 14 # c is a local variable (different from c above)
return c + a
print(sum_2(1))
print(c) # c, in the outer scope is unchanged
# In[ ]:
def sum_3(a):
c += 17 # no modification of variables from the outer scope
return c + a
print(sum_3(1))
# ## Anonymous functions
# - Also known as `lambda` functions
# - C.f. Matlabs anonymous functions `@(x) x^2`
# In[ ]:
def axpy(a, x, y): # trivia: axpy is a standard name for a*x + y (in linear algebra packages)
return a*x + y
print(axpy(2, 3, 4))
# In[ ]:
axpy_lambda = lambda a, x, y : a*x + y
print(axpy_lambda(2, 3, 4))
# - Both are functions
# - The only difference is the syntax
# In[ ]:
print(type(axpy), type(axpy_lambda))
# ## Everything is an object
# ## Functions are objects!
# In[ ]:
def add_me(a, b): return a + b
def mul_me(a, b): return a * b
def div_me(a, b): return a / b
functions = [add_me, mul_me, div_me]
for f in functions:
print(f(1, 2))
# # References and copies
# - Assignment of primitive types gives copies
# In[ ]:
a = 10
b = a # b is a new int variable
b += 1
print(a, b)
# - Assignment of containers `list`, `dict` etc. are **references**
# In[ ]:
a = [10]
b = a # b is a reference to the list a
b[0] += 1
print(a, b)
# - Important corner cases
# In[ ]:
x = [1, 2, 3] # Create a list object and binds x to that object.
y = x # Binds y to that same object x
y += [4, 5] # += for lists means "append to list"
print(x)
# In[ ]:
x = [1, 2, 3] # Create a list object and bind x to that object.
y = x # Bind y to that same object
y = y + [4, 5] # Create a new object from the list, use + list operation and re-bind y to that new object
print(x)
# In[ ]:
x = [1, 2, 3] # Create a list object and bind x to that object
z = (x, x) # Create a tuple object which contains references which are bound to the same object
print('1:', z)
x += [4, 5] # Append to list
print('2:', z)
x = [7, 8] # Rebind name x to a new list
print('3:', z)
# - Well, the variable `z` has actually not changed (tuples are immutable)
# - It's still pointing to the same two list-objects
# In[ ]:
x = [1, 2, 3]
y = [x, x, [7, 8, 9]]
print(y)
# What will happen if we do:
# In[ ]:
y[0] = [4, 5, 6]
print(x)
print(y)
# The equal sign is the "rebind variable" operation!
# To really make a copy, use the slice `:`, or the `.copy()` method
# In[ ]:
x = [1, 2, 3]
y = x[:]
y += [4, 5]
print(x, y)
# In[ ]:
x = [1, 2, 3]
z = x.copy()
z += [6, 7]
print(x, z)
# ## Functions and references
# - Same behavior with references when it comes to function arguments
# In[ ]:
def foo(x):
x[0] = 42
y = [1, 2, 3]
foo(y) # This means foo(x = y), and x = y behaves as described earlier
print(y)
# * The equal sign `=` is variable name rebinding,
it doesn't modify the object that `x` was bound to previously.
# In[ ]:
def foo(x):
x = [4, 5, 6]
y = [1, 2, 3]
foo(y)
print(y)
# # Modules
# - In Python have few functions in the global scope (namespace)
# - C.f. Matlab were everything is available
# - Import modules to access functionality
# - Even basic things in the Python standard library
# ## Module namespace
# - Each module has its own namespace (defaults to the library name)
# - Namespaces are good, let's use them!
# - They avoid name-collisions between common function names
# ## Importing modules
# In[ ]:
import numpy
numpy.sqrt(5)
# ## Abbreviating the namespace
# In[ ]:
import numpy as np
np.sqrt(5)
# ## Selective importing
# Example: Solve $x^2 - 1 = 0$ numerically
#
# This is colloquially called: "Root solving"
# In[ ]:
from scipy.optimize import root
root(lambda x: x**2 - 1, x0=0.1)
# In[ ]:
from scipy.optimize import root as scipy_root
res = scipy_root(lambda x: x**2 - 1, x0=0.1)
print(res.x)
# ## Wildcards
# Using the wildcard, you import everything in that scope
# ```python
# from numpy import *
# ```
# but this practice is discouraged!! Why?
# - The `numpy` module contains many functions, e.g. a function called `all`.
# - If `all` is defined in the program before the wildcard import, **it is lost!**
# - Very hard to debug...
# In[ ]:
def all(): return 'Hi all!'
print(all())
# In[ ]:
from numpy import *
print(all())
# ## The Python Standard Library
# - Python comes with "batteries included"
# -
#
# Examples:
# In[ ]:
from datetime import datetime
print(datetime.now())
# In[ ]:
from itertools import permutations
for pair in permutations(['A', 'B', 'C'], 2):
print(pair, end=', ')
# In[ ]:
from multiprocessing import Pool
def my_calc(x): return x*x
p = Pool(4)
l = p.map(my_calc, [1, 2, 3, 4, 5, 6, 7, 8, 9])
print(l)
# # Documentation
# ## Reference manuals
# PDF versions of the manuals for:
# - [Python 3](https://docs.python.org/3/),
# - [Numpy and SciPy](https://docs.scipy.org/doc/), and
# - [Matplotlib](https://matplotlib.org/contents.html), as well as
# - the [Lecture Notes](https://chalmers.instructure.com/courses/8757/modules)
#
# will be available on the exam. Check them out!
# # Built in `help`
# - `help` is a built in help function
# - You can run `help` on most things, functions, libraries, variables
# - Note that you need to pass the function or method (i.e. without parenthesis)
# In[ ]:
help(len)
# ### Documenting your own functions
# - Document your functions using "doc-strings"
# - `help` will show this documentation
# - NumPy has a good [style guide]()
# In[ ]:
def my_sum(a, b):
"""The sum of two numbers.
Parameters
----------
a : int
First number
b : int
Second number
Returns
-------
int
Sum of a and b
"""
assert( type(a) == int )
assert( type(b) == int )
return a + b
# In[ ]:
help(my_sum)
# # Errors
# - Many examples in this lecture
# - Syntax errors are probably going to be common
# In[ ]:
x = 123 + /34
# In[ ]:
132/0
# In[ ]:
x = [1,2,3]
x[8]
# # The many ways of Python
# There are many ways to use and interact with Python
# - Running `.py` on the command line, using the `python` interpreter
# - Interactively using the `python` interpreter
# - Interactively using the "smart" `ipython` interpreter
#
# Demo!
# - Interactively using the Jupyter notebook interface
# - Using a development GUI like `PyCharm`, perfect for developing GUI applications!
#
# We warmy recommend `PyCharm` for learning in this course! (the free version)
# (It is possible to [apply](https://www.jetbrains.com/student/) for a "full" version as a student)
# # Lecture 2: The End