Python basics#
The purpose of this tutorial is to introduce some basic Python syntax, which can help understand Ubermag simulations.
Variables#
Variables (such as the object with name a
in the examples below) are created through assignment of a value. We can check the type of the variable using the type()
function:
[1]:
a = 10
type(a)
[1]:
int
[2]:
a = 3.14 # decimal point present makes this a float type
type(a)
[2]:
float
[3]:
a = 'Python' # single or double quotes define a string
type(a)
[3]:
str
[4]:
a = (1, 2, 3) # tuple: round brackets
type(a)
[4]:
tuple
[5]:
a = ['a', 2, 3.14] # list: square brackets
type(a)
[5]:
list
Large/small values, e.g. \(a = 2.1 \times 10^{6}\)
[6]:
a = 2.1e-6
a
[6]:
2.1e-06
Basic arithmetic operations#
1. Addition \(c = a + b\)#
[7]:
a = 10
b = 3
c = a + b
To inspect an object, we can just type its name if it is in the last line of a notebook cell:
[8]:
c
[8]:
13
The print()
function can be used to send information to the standard output (typically the display):
[9]:
print('The value of c is:', c)
The value of c is: 13
2. Subtraction: \(a - b\)#
[10]:
a - b
[10]:
7
3. Multiplication: \(a \times b\)#
[11]:
a * b
[11]:
30
4. Division: \(a / b\)#
[12]:
a / b
[12]:
3.3333333333333335
In Python 3, if we divide two int
variables, we are going to get float
:
[13]:
5/2
[13]:
2.5
5. Power \(a^{b}\)#
[14]:
a = 5
b = 3
a ** b # Common mistake to write a^b
[14]:
125
6. More complicated operations#
Other “more complicated” operations generally live in math
or numpy
. Before math
can be used, it must be imported.
[15]:
import math
All functions living in math
or any other module, can be accessed using .
operator.
[16]:
theta = 3.14159 # theta is approximately pi
math.sin(theta)
[16]:
2.65358979335273e-06
[17]:
math.cos(theta)
[17]:
-0.9999999999964793
[18]:
math.sin(math.pi/2)
[18]:
1.0
[19]:
a = 10
math.log10(a)
[19]:
1.0
[20]:
math.log(math.e) # natural log
[20]:
1.0
Sequences: lists and tuples#
A collection of values can be represented using lists and tuples.
[21]:
a = [1, 2, 3, 5.1e4] # list -> square bracket
a
[21]:
[1, 2, 3, 51000.0]
[22]:
b = (1, 2, 3, 5.1e4) # tuple -> round bracket
b
[22]:
(1, 2, 3, 51000.0)
Indexing#
[23]:
a[0] # the first element
[23]:
1
[24]:
a[0] = 5
a
[24]:
[5, 2, 3, 51000.0]
[25]:
b[3] # the last element
[25]:
51000.0
Alternatively -1
can be used as an index to get the last element
[26]:
a[-1]
[26]:
51000.0
Length (the number of elements in a sequence)#
[27]:
len(a)
[27]:
4
[28]:
len(b)
[28]:
4
What is the difference between list and tuple? Tuples are not mutable.
[29]:
b[2] = 3
---------------------------------------------------------------------------
TypeError Traceback (most recent call last)
Cell In[29], line 1
----> 1 b[2] = 3
TypeError: 'tuple' object does not support item assignment
Unpacking#
If we have a point which is defined as a tuple and want to unpack the values into x, y, and z, we can write:
[30]:
point = (-1, 2, 0)
x = point[0]
y = point[1]
z = point[2]
print(f'x={x}, y={y}, z={z}')
x=-1, y=2, z=0
A more convenient way is:
[31]:
x, y, z = point
print(f'x={x}, y={y}, z={z}')
x=-1, y=2, z=0
Adding an element to the list:
[32]:
a.append('new_element')
a
[32]:
[5, 2, 3, 51000.0, 'new_element']
Sequences: numpy arrays#
Another sequence type that is used a lot in computational work is the ndarray
type (n-dimensional array) from the numpy
package. We can create a numpy array from other sequences, such as a list:
[33]:
import numpy as np # by convention `np` is used as the alias for numpy
[34]:
c = np.array([5, 2, 10, 100])
c
[34]:
array([ 5, 2, 10, 100])
[35]:
type(c)
[35]:
numpy.ndarray
The ndarray
data type has been designed for numeric work. It is fast in execution and allows to carry out the same operation on all elements of the array at the same time:
[36]:
3*c
[36]:
array([ 15, 6, 30, 300])
[37]:
np.sqrt(c) # element-wise square root
[37]:
array([ 2.23606798, 1.41421356, 3.16227766, 10. ])
[38]:
c.sum()
[38]:
np.int64(117)
The Ubermag modules, such as discretisedfield
, often return numpy arrays.
Dictionaries#
Dictionaries map keys (such as region1
) to values (such as 1e-12
):
[39]:
d = {'region1': 1e-12, 'region797': 5e-11}
d
[39]:
{'region1': 1e-12, 'region797': 5e-11}
Accessing an element in a dictionary
[40]:
d['region1'] # string in quotes
[40]:
1e-12
Conditional execution#
All lines belonging to one execution branch must be indented.
[41]:
a = 5
b = 4
if a == 5 and b < 10:
# indented lines
print("I'm in!") # single and double quotes
a += 1 # a = a + 1
a # output the value
I'm in!
[41]:
6
[42]:
if a == 10:
print('A')
elif a <= 4:
print('B')
else:
print('C')
C
Iteration#
[43]:
for i in [1, 2, 3, 5.1]:
print(i)
1
2
3
5.1
[44]:
a = [0, 5, 9, 4]
for i in range(len(a)):
print(f'{a[i]} + 1 = {a[i] + 1}') # f-string
0 + 1 = 1
5 + 1 = 6
9 + 1 = 10
4 + 1 = 5
[45]:
for i in a:
print(f'{i} + 1 = {i + 1}')
0 + 1 = 1
5 + 1 = 6
9 + 1 = 10
4 + 1 = 5
Functions#
[46]:
def area(a, b):
# indented
return a * b
area(5, 2)
[46]:
10
[47]:
def sum_of_elements(a):
s = 0
for i in a:
s = s + i
return s
sum_of_elements([1, 2, 3])
[47]:
6
Default values for arguments#
[48]:
def volume(a, b, c):
return a * b * c
volume(1, 2, 3)
[48]:
6
[49]:
def volume(a, b=2, c=3):
return a * b * c
volume(1)
[49]:
6
Accessing modules through import
#
[50]:
import numpy
numpy.pi
[50]:
3.141592653589793
Often we specify an alias
[51]:
import numpy as np
np.pi
[51]:
3.141592653589793
Common mistakes#
1. No colon#
[52]:
def speed(s, t)
return s/t
Cell In[52], line 1
def speed(s, t)
^
SyntaxError: expected ':'
2. Lack of indentation#
[53]:
def speed(s, t):
return s/t
Cell In[53], line 2
return s/t
^
IndentationError: expected an indented block after function definition on line 1
3. Mixing incompatible types#
[54]:
a = 10
b = 'a'
a + b
---------------------------------------------------------------------------
TypeError Traceback (most recent call last)
Cell In[54], line 3
1 a = 10
2 b = 'a'
----> 3 a + b
TypeError: unsupported operand type(s) for +: 'int' and 'str'
4. Using an undefined variable#
[55]:
my_var + 5
---------------------------------------------------------------------------
NameError Traceback (most recent call last)
Cell In[55], line 1
----> 1 my_var + 5
NameError: name 'my_var' is not defined
5. Module is not imported#
[56]:
import scipy
scpy.fft.fft() # typo: scpy instead of scipy
---------------------------------------------------------------------------
NameError Traceback (most recent call last)
Cell In[56], line 2
1 import scipy
----> 2 scpy.fft.fft() # typo: scpy instead of scipy
NameError: name 'scpy' is not defined
Object oriented programming basics#
In Python, everything is an object. Each object contains attributes and methods (functions). When we define an object, using .
we can access its different methods. For instance, if we define a string:
[57]:
my_object = "Ubermag"
Now we can access some of its methods:
[58]:
my_object.lower()
[58]:
'ubermag'
We can see all methods and attributes of an object using the dir()
function:
Getting help#
In Jupyter notebooks, it is often enough to append a question mark to the function name:
[59]:
import math
math.sqrt?
Signature: math.sqrt(x, /)
Docstring: Return the square root of x.
Type: builtin_function_or_method
Alternatively, use the help()
function to get more information about an object or method:
[60]:
help(math.sqrt)
Help on built-in function sqrt in module math:
sqrt(x, /)
Return the square root of x.
Further reading#
A more detailed Introduction to Python for scientists and engineers is available
More generic documentation and tutorials can be found on the Python home page at https://www.python.org/