In [ ]:
from __future__ import print_function

Jupyter

We'll be using Jupyter for all of our examples -- this allows us to run python in a web-based notebook, keeping a history of input and output, along with text and images.

For Jupyter help, visit: https://jupyter.readthedocs.io/en/latest/content-quickstart.html

We interact with python by typing into cells in the notebook. By default, a cell is a code cell, which means that you can enter any valid python code into it and run it. Another important type of cell is a markdown cell. This lets you put text, with different formatting (italics, bold, etc) that describes what the notebook is doing.

You can change the cell type via the menu at the top, or using the shortcuts:

  • ctrl-m m : mark down cell
  • ctrl-m y : code cell

Some useful short-cuts:

  • shift+enter = run cell and jump to the next (creating a new cell if there is no other new one)
  • ctrl+enter = run cell-in place
  • alt+enter = run cell and insert a new one below

ctrl+m h lists other commands

A "markdown cell" enables you to typeset LaTeX equations right in your notebook. Just put them in $ or $$:

$$\frac{\partial \rho}{\partial t} + \nabla \cdot (\rho U) = 0$$
**Important**: when you work through a notebook, everything you did in previous cells is still in memory and _known_ by python, so you can refer to functions and variables that were previously defined. Even if you go up to the top of a notebook and insert a cell, all the information done earlier in your notebook session is still defined -- it doesn't matter where physically you are in the notebook. If you want to reset things, you can use the options under the _Kernel_ menu.

Quick Exercise:

Create a new cell below this one. Make sure that it is a code cell, and enter the following code and run it:

print("Hello, World")

print() is a function in python that takes arguments (in the ()) and outputs to the screen. You can print multiple quantities at once like:


In [ ]:
print(1, 2, 3)

Basic Datatypes

Now we'll look at some of the basic datatypes in python -- these are analogous to what you will find in most programming languages, including numbers (integers and floating point), and strings.

Some examples come from the python tutorial: http://docs.python.org/3/tutorial/

integers

Integers are numbers without a decimal point. They can be positive or negative. Most programming languages use a finite-amount of memory to store a single integer, but in python will expand the amount of memory as necessary to store large integers.

The basic operators, +, -, *, and / work with integers


In [ ]:
2+2+3

In [ ]:
2*-4

Note: integer division is one place where python 2 and python 3 different

In python 3.x, dividing 2 integers results in a float. In python 2.x, dividing 2 integers results in an integer. The latter is consistent with many strongly-typed programming languages (like Fortran or C), since the data-type of the result is the same as the inputs, but the former is more inline with our expectations


In [ ]:
1/2

To get an integer result, we can use the // operator.


In [ ]:
1//2

Python is a dynamically-typed language—this means that we do not need to declare the datatype of a variable before initializing it.

Here we'll create a variable (think of it as a descriptive label that can refer to some piece of data). The = operator assigns a value to a variable.


In [ ]:
a = 1
b = 2

Functions operate on variables and return a result. Here, print() will output to the screen.


In [ ]:
print(a+b)

In [ ]:
print(a*b)

Note that variable names are case sensitive, so a and A are different


In [ ]:
A = 2048

In [ ]:
print(a, A)

Here we initialize 3 variable all to 0, but these are still distinct variables, so we can change one without affecting the others.


In [ ]:
x = y = z = 0

In [ ]:
print(x, y, z)

In [ ]:
z = 1

In [ ]:
print(x, y, z)

Python has some built in help (and Jupyter/ipython has even more)


In [ ]:
help(x)

In [ ]:
x?

Another function, type() returns the data type of a variable


In [ ]:
print(type(x))

Note in languages like Fortran and C, you specify the amount of memory an integer can take (usually 2 or 4 bytes). This puts a restriction on the largest size integer that can be represented. Python will adapt the size of the integer so you don't overflow


In [ ]:
a = 12345678901234567890123456789012345123456789012345678901234567890
print(a)
print(a.bit_length())
print(type(a))

floating point

when operating with both floating point and integers, the result is promoted to a float. This is true of both python 2.x and 3.x


In [ ]:
1./2

but note the special integer division operator


In [ ]:
1.//2

It is important to understand that since there are infinitely many real numbers between any two bounds, on a computer we have to approximate this by a finite number. There is an IEEE standard for floating point that pretty much all languages and processors follow.

The means two things

  • not every real number will have an exact representation in floating point
  • there is a finite precision to numbers -- below this we lose track of differences (this is usually called roundoff error)

On our course website, I posted a link to a paper, What every computer scientist should know about floating-point arithmetic -- this is a great reference on understanding how a computer stores numbers.

Consider the following expression, for example:


In [ ]:
0.3/0.1 - 3

Here's another example: The number 0.1 cannot be exactly represented on a computer. In our print, we use a format specifier (the stuff inside of the {}) to ask for more precision to be shown:


In [ ]:
a = 0.1
print("{:30.20}".format(a))

we can ask python to report the limits on floating point


In [ ]:
import sys
print(sys.float_info)

Note that this says that we can only store numbers between 2.2250738585072014e-308 and 1.7976931348623157e+308

We also see that the precision is 2.220446049250313e-16 (this is commonly called machine epsilon). To see this, consider adding a small number to 1.0. We'll use the equality operator (==) to test if two numbers are equal:

Quick Exercise:

Define two variables, $a = 1$, and $e = 10^{-16}$.

Now define a third variable, b = a + e

We can use the python == operator to test for equality. What do you expect b == a to return? run it an see if it agrees with your guess.



In [ ]:

modules

The core python language is extended by a standard library that provides additional functionality. These added pieces are in the form of modules that we can import into our python session (or program).

The math module provides functions that do the basic mathematical operations as well as provide constants (note there is a separate cmath module for complex numbers).

In python, you import a module. The functions are then defined in a separate namespace—this is a separate region that defines names and variables, etc. A variable in one namespace can have the same name as a variable in a different namespace, and they don't clash. You use the "." operator to access a member of a namespace.

By default, when you type stuff into the python interpreter or here in the Jupyter notebook, or in a script, it is in its own default namespace, and you don't need to prefix any of the variables with a namespace indicator.


In [ ]:
import math

math provides the value of pi


In [ ]:
print(math.pi)

This is distinct from any variable pi we might define here


In [ ]:
pi = 3

In [ ]:
print(pi, math.pi)

Note here that pi and math.pi are distinct from one another—they are in different namespaces.

floating point operations

The same operators, +, -, *, / work are usual for floating point numbers. To raise an number to a power, we use the ** operator (this is the same as Fortran)


In [ ]:
R = 2.0

In [ ]:
print(math.pi*R**2)

operator precedence follows that of most languages. See

https://docs.python.org/3/reference/expressions.html#operator-precedence

in order of precedence:

  • quantites in ()
  • slicing, calls, subscripts
  • exponentiation (**)
  • +x, -x, ~x
  • *, @, /, //, %
  • +, -

(after this are bitwise operations and comparisons)

Parantheses can be used to override the precedence.

Quick Exercise:

Consider the following expressions. Using the ideas of precedence, think about what value will result, then try it out in the cell below to see if you were right.

  • 1 + 3*2**2
  • 1 + (3*2)**2
  • 2**3**2


In [ ]:
2**(3**2)

The math module provides a lot of the standard math functions we might want to use.

For the trig functions, the expectation is that the argument to the function is in radians—you can use math.radians() to convert from degrees to radians, ex:


In [ ]:
print(math.cos(math.radians(45)))

Notice that in that statement we are feeding the output of one function (math.radians()) into a second function, math.cos()

When in doubt, as for help to discover all of the things a module provides:


In [ ]:
help(math.sin)

complex numbers

python uses 'j' to denote the imaginary unit


In [ ]:
print(1.0 + 2j)

In [ ]:
a = 1j
b = 3.0 + 2.0j
print(a + b)
print(a*b)

we can use abs() to get the magnitude and separately get the real or imaginary parts


In [ ]:
print(abs(b))
print(a.real)
print(a.imag)

strings

python doesn't care if you use single or double quotes for strings:


In [ ]:
a = "this is my string"
b = 'another string'

In [ ]:
print(a)
print(b)

Many of the usual mathematical operators are defined for strings as well. For example to concatenate or duplicate:


In [ ]:
print(a+b)

In [ ]:
print(a + ". " + b)

In [ ]:
print(a*2)

There are several escape codes that are interpreted in strings. These start with a backwards-slash, \. E.g., you can use \n for new line


In [ ]:
a = a + "\n"
print(a)

Quick Exercise:

The input() function can be used to ask the user for input.

  • Use help(input) to see how it works.
  • Write code to ask for input and store the result in a variable. input() will return a string.

  • Use the float() function to convert a number entered as input to a floating point variable.

  • Check to see if the conversion worked using the type() function.

In [ ]:
a = input("enter a string")
print(type(float(a)))

In [ ]:
a

""" can enclose multiline strings. This is useful for docstrings at the start of functions (more on that later...)


In [ ]:
c = """
Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do eiusmod tempor 
incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis 
nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. 
Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore 
eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt 
in culpa qui officia deserunt mollit anim id est laborum."""

In [ ]:
print(c)

a raw string does not replace escape sequences (like \n). Just put a r before the first quote:


In [ ]:
d = r"this is a raw string\n"
print(d)

slicing is used to access a portion of a string.

slicing a string can seem a bit counterintuitive if you are coming from Fortran. The trick is to think of the index as representing the left edge of a character in the string. When we do arrays later, the same will apply.

Also note that python (like C) uses 0-based indexing

Negative indices count from the right.


In [ ]:
a = "this is my string"
print(a)
print(a[5:7])
print(a[0])
print(d)
print(d[-2])

Quick Exercise:

Strings have a lot of methods (functions that know how to work with a particular datatype, in this case strings). A useful method is .find(). For a string a, a.find(s) will return the index of the first occurrence of s.

For our string c above, find the first . (identifying the first full sentence), and print out just the first sentence in c using this result



In [ ]:
c

there are also a number of methods and functions that work with strings. Here are some examples:


In [ ]:
print(a.replace("this", "that"))
print(len(a))
print(a.strip())    # Also notice that strip removes the \n
print(a.strip()[-1])

Note that our original string, a, has not changed. In python, strings are immutable. Operations on strings return a new string.


In [ ]:
print(a)

In [ ]:
print(type(a))

As usual, ask for help to learn more:


In [ ]:
help(str)

We can format strings when we are printing to insert quantities in particular places in the string. A {} serves as a placeholder for a quantity and is replaced using the .format() method:


In [ ]:
a = 1
b = 2.0
c = "test"
print("a = {}; b = {}; c = {}".format(a, b, c))

In [ ]: