Getting Started with Spyder 4

Video

Introduction

In this guide we will look at the basic concepts of the core python library and also the user interface of the Spyder 4 IDE. If you haven't already installed Python and Spyder 4 using the Anaconda Installation see.

Using the Console

We can use the Spyder Console like an ordinary calculator. The Console allows us to type in lines of code one by one.

In this case I am going to type in the value.

1

In order to execute the line of code press return .

The console will display what was input In [line number] and what was output Out [line number]. In both cases the value shown is the very basic 1.

To access the previously used command we can use the up arrow key . We can then scroll up and down through all the previously typed in commands.

Variable Assignment

Python is an object orientated programming language. In python the = sign is the assignment operator. The value to be assigned is on the right hand side of the assignment operator while the object name is on the left hand side of the assignment operator.

obj_name=1

Object names or variable names are usually always in lower case letters. Numbers can be used alongside the underscore (instead of a space) however the object name should not begin with these.

Note the difference between Line 2 and Line 1. Line 1 shows an output because no object or variable name was assigned. Line 2 on the other hand just assigns the value to the variable name obj_name.

Variable Explorer

Once an object or variable is assigned, it is created and details about it are shown on the variable explorer.

In this case we can see that the Name is obj_name and the Value is 1 as expected. The Type is an int which is an abbreviation for an integer (whole number) and the Size is 1 meaning it is a scalar.

Variable Reassignment

We can reassign the value of an object name. For example:

obj_name=obj_name+1.5

Where the . represents a decimal point.

The code above may be confusing on first glance however it is commonly used. The expression on the right hand side happens first before the assignment occurs. In this case the original value of obj_name is 1 and so the value 1+1.5 is calculated which gives 2.5. This new value of 2.5 is assigned to obj_name using the assignment operator =.

If you are observant, you will have noticed that when obj_name=1 that it was an int however now that it is updated to 2.5 and contains a decimal point it is now a float. float is an abbreviation for a floating point number which is essentially a decimal number. Under the hood the difference between these is more substantial, due to the way a computer stores values in its memory. However in practice a float and an int can be interacted with one another and the result in most cases will be a float.

Scripts

Opposed to individually executing each line one by one in the Console we can create a script of multiple commands.

By default it won't be saved which can be seen by the file name on the tab ending in a * and the fact that it is called untitled3.py

Select File→Save As:

Select your folder in this case I will select a Python folder within documents. Then name your script file, in this case I will label the file example. The .py extension will automatically be added when the file is saved.

You can see the folder and location of the script file.

Once saved, we can now run the script. Information will be given in the console stating the script file has been run.

The print Function

To the left hand side of the script file are line numbers. Note that on line 1, the code.

1

Displays nothing in the console when the script file is ran and because it is not assigned to a variable name, it does not show up on the variable explorer. Therefore as a user we have no idea whether this code was executed or not.

Code in the script file is not printed to the console by default. If we want it to display we can explicitly use the function print.

print

The value to be printed is enclosed in parenthesis. In this case the value to be printed is the solo positional input argument (we will cover input arguments in more detail in a moment).

Strings

So far we have only looked at numerical data (int and float) however in other cases we may want text. The datatype str is an abbreviation for a string of characters. As variable names are also strings of characters we need to differentiate text input from variable name. To do this we enclose the text in quotations. Note the Spyder IDE highlights strings in green so we can easily identify them.

obj_name1='obj_name1'

As we can see on the variable explorer. The Name is obj_name1, the Type is str and the value is obj_name1 (the variable explorer doesn't display the quotations).

It is possible to either use single or double quotes to enclose a string however double quotes should be used if the string contains a single quote within it:

"Philip's"

Or alternatively single quotes should be used if it contains a double quotation within it.

obj_name3='She said "this"'

In some cases the str may contain both single and double quotes, in which case we need to use the special character \ in front of the quotation to indicate that it is part of the string.

obj_name4='Philip\'s \"amazing\" guide'

Note this character also works with other characters \n will give a new line, \t will give a tab and \\ will give \.

The string can be placed as the positional input argument within the print function.

print('amazing')

Input Arguments

If you call a recognized function with open parenthesis, a pop-up balloon will display giving information about its input arguments.

Input arguments are contained within the parenthesis and typed one by one using a comma to separate one from the other.

function(value1,value2)

There are two types of input arguments positional and keyword input arguments.

Positional input arguments must be called at the start, in the order given for example:

function(value1,value2)

And keyword arguments can optionally be called once the positional input arguments are all listed.

function(value1,value2,kw3=value3)

To do this the name of the keyword input argument must be specified and then the assignment operator will be used to set it to a desired value. If the keyword argument is not specified the default value will be taken.

If we look at print. We can see we have a solo positional keyword input argument and we have the optional keyword input arguments sep, end, file and flush.

This image has an empty alt attribute; its file name is image-183.png

Let's demonstrate this. We will print the value 'amazing' three times however in the second time, we will change the end to '\n\t' which will give a new line and a tab opposed to just a new line.

print('amazing')
print('amazing',end='\n\t')
print('amazing')

This is seen in the Console.

Getting Help

We can use the ? to get help about an object such as the function print.

? print

This will display all the help in the console.

There is also a help tab however and it has a search box at the top.

We can also highlight the function and press [Ctrl] + [i] which will automatically inspect the currently highlighted text and look up it's help documentation. For instance if we change the code to:

print('amazing')
print('amazing',end='\n\t')
len('amazing')

Then we get information about the len function (an abbreviation for length).

The len Function

If we run this code we see nothing and this is once again because we haven't assigned the output to a variable name or enclosed it within a print statement.

Let's print the value:

print('amazing')
print('amazing',end='\n\t')
print(len('amazing'))

Note that we have a function as an input argument for a function. In order to show the coder what's going on Spyder automatically highlights the matching bracket. Here you can see the two outer parenthesis belong to the function print and the two inner parenthesis belong to the function print.

Many functions have a return statement. If len is assigned to a variable, it will return the value of the number of characters in the string (length) however if print is assigned an output it will return a different class NoneType (which essentially means the variable name is assigned to something that doesn't exist).

a=print('amazing')
b=len('amazing')

The input Function

We may in some cases need to gather input from a user and we can do this with the input function. This takes a string as a single positional input argument which prints as a prompt to the console.

We can demonstrate this using the following:

name=input('What is your name?')
print(f'Hi {name}, welcome')

Within the print statement we are using a formatted string. This strings begin with f (f for formatted) and the variable name is enclosed in {} as a place holder within the formatted string. When executed the value of the variable will be used in its place.

If we execute this code, it hangs on a prompt until the user inputs their string of text (no quotations are required because input always expects a string of text) and presses .

Once the user does this the formatted string will continue.

Note in the above there is no space after the question mark of the prompt. It is easier to read if there is, a full stop can also be added to the formatted string.

name=input('What is your name? ')
print(f'Hi {name}, welcome.')

We can now try and do some basic arthritic with a print statement.

number=input('Input a number. ')
number2=number+2
print('Your number plus 2 is {number2}')

However when we run this code we get a TypeError: can only concatenate str (not "int") to str.

What essentially happened here is:

line 1 returned number='5'

So line 2 tried to perform an addition of '5'+2. i.e. addition of a str and an int number because these are different data types the addition method does not make sense and the error above was given. We can use the classes int, float and str to convert an object to an int, float or str respectively (at this stage you can just think of these classes as conversion functions).

Note how the result is a float because it has a decimal point.

The del Function

The del function can be used to delete a variable for example let's delete the variable a

del(a)

As you can see it is now removed from the variable explorer.

Clearing the Console

To clear the console, you can either right click it and select clear console.

Or if your mouse is clicked into the console the keyboard shortcut [Ctrl] + [L] will clear the console. Note the line number will still be current (in this case 25 as it was 24 before and the keyboard shortcut was taken as an additional command).

[Ctrl], [Alt] and [R] can be used to remove all variables.

Restarting the Kernal

If we want to instead start completely fresh by removing any variables created, clearing the console and beginning at line 1 we can go to Console and Restart the Kernal.

Note the script file will be uninfluenced. It can be closed and a new untitled script file will display.

This has a shortcut key [Ctrl] + [.] which you can use if clicked into the Console.

Errors

In the following code we are trying to print the variable a, but we have not defined the variable a.

print(a)

As a result there is a red x on the script highlighting something is wrong.

We get the NameError: name 'a' is not defined when running the code:

Likewise if we forget to clsoe a bracket we get another warning.

a=1
print(a
print(a)

We get a SyntaxError: invalid syntax when we try and run the code.

Comments

Lines of code beginning with a # are comments and ignored by Python. A comment can also in some cases be placed at the end of a line.

If you want to comment out or uncomment out multiple lines of code you can highlight them and go to Edit and then select Comemnt/Uncomment. This also has the shortcut key [Ctrl]+[1].

Cell Breaks

To start a section break (or new cell) use.

# %%

For example:

# %% Section 1 - Create Variables
a=1
b=2
# %% Section 2 - Print Variables
print(a)
print(b)

Note that the currently selected cell is highlighted in yellow.

You then have the option of running the entire script, running the current cell, running the current cell and then proceeding to select the next cell.

Zero Order Indexing

We can use the function len to look at the number of characters in a str (length of a string).

a='amazing'
print(len(a))

The value returned is 7 as there are 7 characters.

When we count we use the decimal system, which is based on the fact that we have 10 fingers. However in normal every day use we don't proscribe the 10 digits to our 10 fingers, taking the first finger as 1 and going up with the tenth finger being proscribed double digits.

In computer science we often use zero order indexing opposed to 1st order indexing, meaning we count from 0 and as a result we count up to but don't include the value. For instance counting from 0 to 10 gives us 0,1,2,3,4,5,6,7,8,9.

Returning to the string. We can index an individual letter from it using square brackets.

a='amazing'

For instance if we want the 0th and 6th Index (6 is the last index, as we go up to the length in integer steps of 1 but don't include the last value of 7).

a='amazing'
print(a[0])
print(a[6])

We can also index using slices. For this we use the colon. The colon uses zero order indexing, so the index to the left hand side of the colon is inclusive of the value and the index to the right hand side of the colon is exclusive of the value.

a='amazing'
print(a[0:3])
print(a[:3])
print(a[:])
print(a[5:])

a[0:3] will look at the 0th, 1st and 2nd index.

a[:3] if the lower index is not selected, then slicing will occur from the lower limit 0.

a[5:] if the higher index is not selected, then slicing will occur up to but not including the last value of length of the string len(a).

a[:] if the colon is used without any value then a copy of the string will be made.

Methods

When we assign a str to a variable name.

a='amazing'

What we are doing in short is the following.

a=str('amazing')

We are creating the object a which is an instance of the class type str. A class can be thought of as a set of blueprints for an object. The object or instance of the class will acquire a number of functions that belong to the class type. A function belonging to an object is called an object. A list of methods available from the object can be scrolled through by typing in the object a followed by a dot . and then a tab .

We can use the method upper as an example to convert the string to upper case. We can type it with open parenthesis to view the input arguments (there are none in this case).

a.upper()

We can assign it to a new variable name.

b=a.upper()

We can then use the method lower which will give the original string.

b.lower()

amazing

Or we can opt to just capitilize the 0th index using the method capitalize.

a.capitalize()

Amazing

We can look at the method count which requires a positional input argument in the form of a sub string of the string being looked at.

a.count('a')

2

Because there are 2 occurrences of 'a' in the word 'amazing'.

We can also look at the method replace, here we see that there are two positional input arguments, the old value (in the form of a sub string) and the new value (in the form of a sub string). The keyword input argument is set to -1 meaning that all occurrence of the old value will be replaced.

We can for example replace all occurrences of 'a' with 'e'.

a.replace('a','e')

'emezing'

For the string we can see that most the methods for example capitalize, lower, upper, startswith, endswith, count, find, index, replace and join are all related to text which is expected for a string.

We can now make a different class using the code.

a=1

Which is a shorthand notation for:

a=int(1)

If we type in the object name and follow it by a dot . and tab we once again see a list of methods and attributes available to the object.

For example the method conjugate finds the complex conjugate value.

a.conjugate()

Because this number has no imaginary component, it returns the same value 1.


1

An attribute can be thought of as an object that belongs to another object or better put an object that is referenced with respect to another object. The real component and the imaginary component are each numbers i.e. objects in their own right. They can be accessed using the attributes real and imag.

a.real
a.imag

This returns the values 1 and 0 respectively.


1
0

Because attributes are themselves objects, in this case both belong to the int class with the values 1 and 0. Other attributes and methods can be called from them.

For example we can call the method conjugate, to get the complex conjugate of the real component a.real.

a.real.conjugate()

Special Methods

We have seen that the class int and the class str have different methods available. We also seen earlier when we used the function input that we could not add an int to a str. This is because the + key is a method and it is defined differently for each class.

If we create two instances of the class int, a and b respectively

a=1
b=2

We can add them together.

a+b

And get 3 as expected.

Behind the scenes we are using a special method of the int class __add__ (double underscore before and after). This is hidden from the list of methods which display when we use dot . and tab however we will type it explicitly in this case with open parenthesis to see the input arguments.

Here we see that the only input argument is value and the return is self+value. In this case the method is being called from the object a so self refers to a. We can find a+b by typing in.

a.__add__(b)

And once again get 3 as expected.

Now in contrast, let's look at instances of the class str instead.

a='apple'
b='banana'

When we use the + operator we instead perform the str method __add__ which performs a concatenation (adds the characters of b to the end of a).

We will now look at these special methods for numerical data types and then for strings.

Numerical Classes (int and float)

For numerical data we can use the following notation to carry out most basic operations in a calculator.

KeyDescription
=The assignment operator
0,1,2,3,4,5,6,7,8,9the numbers
.the decimal point
+additional operator
subtraction operator
*multiplication operator
**exponentiation operator
/division – returning a float
//division – returning only a complete integer
%division – returning the remainder as an integer
()parenthesis – do the operation in the brackets first
1+2

3
1-2

-1
2*3

6
2**2

4
2/2

1.0

This float division always returns a float.

5//2

2
5%2

1

Integer division can be used to find the total number of times a value divides into another value and an associated remainder can be found. In this case 2 remainder 1.

Parenthesis can be used to place precedence on a mathematical operation. By default multiplication and division operations are carried out before addition or subtraction. So this gives 3 times 5 plus 2 which is 17.

2+3*5

17
(2+3)*5

25

The parenthesis instead instruct in carrying out the addition first so this is 2 plus 3 which is 5 times 5 which is 25.

There are also a number of operations to perform reassignment. For example let's look at reassigning the value of a by incrementing it in integer steps of 1.

a=1
print(a)

1
a=a+1
print(a)

2

We can instead for shorthand use the += operator which in this case will increment the value of a by 1 and reassign this new value to a.

a=1
print(a)
a+=1
print(a)

1
2
+=in-place additional operator
-=in-place subtraction operator
*=in-place multiplication operator
**=in-place exponentiation operator
/=in-place division – returning a float
//=in-place division – returning only a complete integer
%=in-place division – returning the remainder as an integer

String Class (str)

It makes less sense for these numerical operators to work on strings however as we have seen earlier the + operator performs string concatenation.

A string can also be multiplied by an integer number n using the * operator and will undergo concatenation with itself n times. For example we can use the following to concatenate 'amazing' to itself 5 times.

a='amazing'
5*a

Boolean Values

Another important class is the bool class an abbreviation for Boolean. There are two values.

a=True
b=False

If we return to a basic string, this time a string containing a numeric value.

a='1'

We can use dot . and tab to access its available methods. We can see there are a number of methods beginning with is.


a.isnumeric()
a.isdecimal()
a.islower()
a.isupper()
a.isalnum()

Each of these questions return a bool, the statement is either True or False.

Now let's create two variables, one that is a string of a number and one that is a string of a letter.

a='1'
b='a'

These have a different outcome when the method isnumeric is used. This plays an important role when attempting to convert the object from an instance of the str class to an int class for instance. The value that is numeric can undergo the conversion and the value that isn't fails to undergo the conversion giving a ValueError: invalid literal for int() with base 10: 'a'

Comparison of Two Variables

There are a number of conditional logic statements that can be used to compare two variables, for instance two numbers. These statements yield a bool.

Logical OperatorDefinition
==equal to
!=not equal to
less than
<=less than or equal to
>=greater than or equal to
greater than
isis (the same object in memory)
is notis not (the same object in memory)

Supposing we create two variables.

a=1
b=2

We can use the equal to operator == to compare them.

a==b

This returns the bool value False.

Note the differences between equal to == and the assignment operator =. If we type in

a=b

Then the value of a is reassigned to b.

Now the statement.

a==b

Is True.

Now supposing we start with.

a=1
b=2.0

We can check whether a is equal to b.

a==b

False

a is not equal to b.

a!=b

True

a is greater than b.

a>b

False

a is greater than or equal to b.

a>=b

False

a is less than b.

a<b

True

a is less than or equal to b.

a<=b

True

Floats, Significant Figures, Scientific Notation and the round Function

Care should however be taken when comparing a float with another float especially when a calculation is used. The following code may seem to be very basic.

a=0.1+0.2
b=0.3

Intuitively a is equal to b should be True however the value returned is False.

a==b

False

The reason for this is rounding which can clearly be seen when we look at the variable explorer. The value of a is given to its 17th decimal place and its 17th decimal place is non-zero. This is due to the way computers store numbers. We have ten digits (decimal system) whereas the computer stores numbers using the binary system (what can be thought of as a series of on/off boolean switches).

An analogy with the decimal system is the concept of (1/3)+(2/3). The value (1/3) in decimal form is recursive 0.33333333333333333 and the value (2/3) in decimal form is also recursive 0.66666666666666666. Combining (1/3)+(2/3) together when represented in this way can give 0.99999999999999999 which is just short of 1.

For most practical purposes we do not measure values to their 17th significant figure and we can address this by using the round function, we can once again look at its input arguments by typing in the function with open parenthesis.

In this case we can reassign the value a to itself rounded to 3 decimal places.

a=0.1+0.2
a=round(a,3)
b=0.3

This makes the comparison.

a==b

True

In most cases we deal with numbers relative to each other in magnitude however sometimes we will have to deal with very large numbers and very small numbers. For example in physics the speed of light is approximately 30000000 m/s and the distance from the sun to the earth is 150000000000 m. When dealing with these numbers it is all to easy to mistype an additional 0 or mistype by missing a 0. Instead scientific notation is used which splits the number into a mantissa and exponent (power of 10).

So the speed of light would be taken to have a mantissa of 3.000 and an exponent of 8.


30000000
3000000×10
3000000×10^1
300000×100
300000×10^2

3.000×100000000
3.000×10^8

Likewise the distance from the sun to the earth would be taken to be.


150000000000
1.500×10^11

To type these in python we use e to split the mantissa and exponent.

v=3.000e8
d=1.500e11

We can use the formula time=distance/speed to calculate the time in seconds that it takes light from the sun to reach the earth.

v=3.000e8
d=1.500e11
t=d/v

We get about 500 seconds.

Now the concept of a (10 km = 10000 m = 1.0×10^4 m) run may seem a relatively large distance to us however if we compare it to the distance from the earth to the sun, it is insignificant and as we can see doesn't influence our calculation much.

v=3.000e8+1.0e4
d=1.500e11
t=d/v

In other words a larger number + a small number doesn't deviate the large number by much.

It is also possible to write a very small number in scientific notation, for example the universal gravitation constant is 0.0000000000667 m^3 kg^-1 s^-2 which once again has so many 0 that it easy to mistype. We can instead type it in scientific notation.


0.0000000000667
0.000000000667×0.1
0.000000000667×10^-1
0.00000000667×0.01
0.00000000667×10^-2

6.670×0.00000000001
6.670×10^-11

This number can be used with the mass of the earth 5.972×24 kg and radius of the earth 6371^3 m to calculate the gravity of the earth.

gcon=6.67e-11
me=5.972e24
re=6.371e6

We can use the basic formula to calculate the gravity of the earth by multiplying this gravitation constant by the mass of the earth and dividing by the radius of the earth squared.

ge=gcon*me/(re**2)

This gives 9.81 m s^-2 which is the correct gravity of the earth. This basic formula demonstrates briefly how a small number doesn't influence a large number much when added or subtracted but can be substantial when using division and or multiplication.

Conditional Code if, elif, else

We can take advantage of conditional logic and use if, elif (an abbreviation for else if) and else statements to execute code (or not) dependent on a condition. For conditional branching we use the colon : to begin a code block and each line of code belonging to the block is indented by four spaces. Since spacing is so important in the Python programming language it is helpful to enable the following settings within the Spyder IDE. Go to Tools and then Preferences.

Select Editor and then Show Indent Guides and Show Blank spaces.

We will start with an if statement. Let,s create a variable condition and set it to a boolean value. In this case True. We can now use the keyword if, to check a condition, in this case the value of the boolean of the object called condition and then a colon : to begin a new code block. Code belonging to the code block is indented by 4 spaces.

condition=True
if condition:
    print('if branch is True')

Because the condition is True we see the text within the print statement shown on the console.

Now conversely if we update the condition to False.

condition=False
if condition:
    print('if branch is True')

Then the code within the if branch is not executed and we do not see a print statement.

Now if we insert a line of code below this on line 4 and don't indent it we will see that it is outside the code block of the if statement and executed irrespective of the if statement.

condition=False
if condition:
    print('if branch is True')
print('I will run regardless of the condition')

We can follow the if branch with an else branch. The else branch begins with else and then a colon : to begin a code block. There is no condition following else (as by definition it is ran if the if condition is False and all other conditions are False).

condition=False
if condition:
    print('if branch is True')
else:
    print('else branch all conditions are False')
print('I will run regardless of the condition')

Now we can add an elif (else if) branch.

condition=False
if condition:
    print('if branch is True')
elif condition==False:
    print('elif branch is True')
else:
    print('else branch all conditions are False')
print('I will run regardless of the condition')

Here instead of specifying a variable that is a boolean we can use a conditional logic statement to compare the variable with a value. In this case the code in the elif branch is ran.

You can have multiple elif branches however one thing to note is that if an earlier condition is met, the code in its branch will be executed and the script won't bother looking at the conditions of later branches.

condition=False
if condition:
    print('if branch is True')
elif condition==False:
    print('elif branch is True')
elif condition==False:
    print('elif2 branch is True')
else:
    print('else branch all conditions are False')
print('I will run regardless of the condition')

In this case the second elif branch is ignored because the condition of the first elif branch was True and code belonging to this branch was accepted.

Now we can look at the following code.

value=4
if value>5:
    print('if branch is True')
elif value>4:
    print('elif branch is True')
elif value>=4:
    print('elif2 branch is True')
else:
    print('else branch all conditions are False')
print('I will run regardless of the condition')

Here we can see the if condition is False, the 1st elif condition is False and the 2nd elif condition is True (using the subtle differences between greater than or equal to versus greater than). As a result the code in the second elif branch is ran.

Now if we update the value to 6. Then if condition is True. Although all the conditions in the elif branches are also True because the if condition is True and is the top branch, only the code executed within it is ran.

value=6
if value>5:
    print('if branch is True')
elif value>4:
    print('elif branch is True')
elif value>=4:
    print('elif2 branch is True')
else:
    print('else branch all conditions are False')
print('I will run regardless of the condition')

It is also possible to created nested conditional branches.

value=6
if value>5:
    print('if branch is True')
    if value==5:
        print('value is 5')
    elif value==6:
        print('value is 6')
    else:
        print("I don't know the value")
elif value>4:
    print('elif branch is True')
elif value>=4:
    print('elif2 branch is True')
else:
    print('else branch all conditions are False')
print('I will run regardless of the condition')

Pay particular attention to the indent guides, line 2 begins a code block as it ends in a colon. The code belonging to it (line 3 and line 4) is indented by 4 spaces. Line 4 then begins a nested code block as it ends in a colon. The code in line 5 belongs to it and is indented an additional 4 spaces. As it is a nested within an additional if statement this means the total number of spaces before the line is 8.

The code highlighted in blue all belongs to a sub-branch of the outer if statement.

The arrows can be clicked to collapse each branch, this makes it more obvious that the other if, elif, elif and else branch are all together in the outer branch.

Looping for and while Loops

Sometimes we will want to do the same operation multiple times.

a='amazing'
print(a)
print(a)
print(a)
print(a)

We can automate the above using a for loop. To do this we need to create a loop variable, in this case we can create the loop variable i.

a='amazing'
for i

Now obviously in order to loop we need to increment this variable so want this variable to be within an object of multiple integers. We can get this by using the function range. If we call range with open parenthesis we can see that we can specify three positional input arguments a start integer, a stop integer and a step integer or if we only specify two input arguments the step is taken to have a value of 1 and if we only specify a single input argument the start is taken to have a value of 0.

When using a for loop, we once again begin a code block using a colon. The code belonging to the loop is indented by 4 spaces.

a='amazing'
for i in range(4):
    print(a)

In this case we see that we get the same output and that we have the loop variable i on the variable explorer.

The loop variable i has a value of 3 because once again we are using zero order indexing, which means we count up to 4 but don't include 4, with the integer value before 4 being 3. We can print the loop variable within the loop.

a='amazing'
for i in range(4):
    print(a)

Recall that we can use the function len to find out the length of a string and that we can index a letter into a string using square brackets. We can modify the loop to individually print the index number and the letter from the word.

a='amazing'
for idx in range(len(a)):
    print(idx)
    print(a[idx])

This is a bit cumbersome and for short hand we can loop using the string directly. In this case the loop variable l will automatically be assigned to letter as the loop steps through every letter in the string.

a='amazing'
for l in a:
    print(l)

The function enumerate can be used to get two loop variables the index and the letter respectively.

a='amazing'
for i,l in enumerate(a):
    print(i)
    print(l)

The for loop has a predefined number of iterations. The while loop on the other hand runs dependent on a condition. This makes it possible to setup infinite loops.

while True:
    print('Spam my Console')

To escape an infinite while loop use [Ctrl] + [c] while clicked into the Console. This will give a keyboard interrupt KeyboardInterrupt.

We can use a while loop to print every letter in a string however we will need to create a variable before beginning the loop (preallocate a variable) and then look at a condition involving the loop variable that is initially True so the loop begins. We then will need to alter the variable within the loop so it eventually changes making the condition False and the loop is broken out of. In this case we can create a variable i that is assigned to 0 and type in code so that it increments in steps of 1. We can use the function len to get the length of the string and have the condition so the value of i is less than this.

a='amazing'
i=0
while i<len(a):
    print(i)
    print(a[i])
    i+=1

How a Computer Stores Data

Computers store data in bits, which can be thought of as essentially binary switches. They have only two values 0 (False) and 1 (True).


0
1

We use the decimal system as mentioned earlier as we have 10 fingers (so arbitrarily created 10 characters to correspond to these).


0
1
2
3
4
5
6
7
8
9

When we run out of digits we add another digit.


10
11
12

In binary as there are only 2 digits this happens at the number we would consider 2.


0
1
10

We can use the function bin to convert a decimal integer to binary. For convenience we will use a for loop to see the first 12 numbers in binary. Here we convert both i and bin(i) to strings using the function str and use them as variables within a formatted string.

for i in range(12):
    print(f'{str(i)} : {str(bin(i))}')

The ob prefix indicates binary notation as you can see the decimal equivalent of 2 is 0b10.

This means we need 4 digits (which are referred to as bits in computer science) for the number 8. The more bits we use to represent a number means the more memory we use on a computer. It is common to group numbers over 8 bits (which is called a byte) because this gives us combinations on the order of 100 (actually 256 configurations, 0-255 in decimal).

This number set is called unsigned (because all are positive), 8 bit (because 8 bits are used) integers (because they are all integers).

The basic American Standard Code for Information Interchange (ASCII) maps these binary number configurations to what we recognize in the English as a character (number, letter, upper and lower cases and punctuation marks).

We can modify our for loop to use the function chr opposed to bin to see the character each binary configuration corresponds to.

for i in range(256):
    print(f'{str(i)} : {str(chr(i))}')

The first 32 commands are not necessary commands we would recognize but the hidden punctuation marks such as new line, new tab and carriage return when are used to communicate with devices.

The ord function can instead be used to calculate the numeric value which corresponds to a character.

These 256 levels are also used to control brightness of a Light Emitting Diode (LED) with 0 corresponding to a LED being fully turned off and 255 with the LED being a full brightness. This being the core component behind a screen.

Because the number 256 is 16 squared, it can also be presented as two digits with 16 characters. As our decimal system only uses 10, we need an additional 6 characters so we take the letters a to f. This is known as hexadecimal notation. We can use the function hex to view each of these numbers in hexadecimal form.

for i in range(256):
    print(f'{str(i)} : {str(hex(i))}')

To see color in electronics we utilize red, green and blue which correspond to the three primary colors our brain recognizes. The brain makes all other colors by mapping the relative intensity ratios of these three colors. Observe the three equally bright (to our brain) LEDs below. When all three overlap we see white (the light sensors in our eyes are saturated), when none are on we see nothing (black, the absence of light). A mixture of only red and blue creates magenta, a mixture of blue and green makes cyan and a mixture of red and green make yellow.

We use the hexadecimal notation with 2 characters corresponding to each primary color #ffffff to represent the three colors.

In some applications we want negative numbers also. If we use the same number of bits, we will half the magnitude of values as half the binary values will correspond to the positive numbers and half the binary values will correspond to the negative numbers. 8 bit unsigned therefore corresponds from -128 to +128 (up to but not including).

Floats are represented using a binary version of scientific notation. In 16 bit (half precision) float notation the first bit corresponds to the sign, the next 5 bits correspond to the exponent and the next 10 bits correspond to the mantissa. 11.5 (decimal) in 16 bit (half precision) float binary notation is.

Let's look at this in more detail. In +11.5 because the sign is positive, the first bit is 1.

Let's look at the integer part 11 next.

Let's look at powers of 2.


2**4=16
2**3=8
2**2=4
2**1=2
2**0=1
11//16=0
11%16=11

0b0xxxx
11//8=1
11%8=3

0b01xxx
3//4=0
3%4=3

0b010xx
3//2=1
3%2=1

0b0101x
1//1=1

0b01011

Let's look at the decimal part 0.5 next.

Let's look at powers of 2.


2**-1=0.5
0.5//0.5=1

0b0.1

Combining these together gives the following in binary notation:


0b01011.1

We now need to have the highest 1 in front of the decimal point. This gives the following in decimal scientific notation.


0b01011.1
0b0101.11×2^(0b1)
0b010.111×2^(0b10)
0b01.0111×2^(0b11)
0b1.0111×2^(0b11)

Because every binary value in float notation will begin with a 1 and the float notation wants to get the highest dynamic range from the number of bits available we don't want to waste a bit that will always have the same value. The 1 before the decimal point therefore is ignored so only the numbers after the decimal point are included in the mantissa. 0b1.0111

The float notation above is designed to get the most out the number of bits available and for this reason the sign takes up a separate bit by itself. The exponent on the other hand does not have such a large range of numbers and a result does not have a bit corresponding to the sign.


2**5=32

The 32 combinations are as a result split to give 14 corresponding to negative powers, a 0, 15 combination corresponding to positive powers and a combination corresponding to infinity. Moreover the exponents are adjusted to always be positive. As a result of this 0 is at position 15, giving a 15 offset. In this notation we must always specify the exponent as a positive number so an offset of 15 is applied. We had the power of 11 which is 3. If we add this offset it gives 18 which is.

bin(18)

0b10010

Giving.

This image has an empty alt attribute; its file name is binary.png

Float 32 gives a single bit for a sign, 8 bits for the exponent and 23 bits for the mantissa. The exponent has a zero order offset of 127.

This would make the exponent 127+3.

bin(130)

'0b10000010'

The mantissa would just have higher precision.

11.5 was selected as it was a nice number in binary notation. The numbers 0.2 and 0.1 are recurring which is why we got the rounding issue before.

0.2 in Binary
0.1 in Binary

In both cases the last digit is rounded up to 1 opposed to continuously recurring. This gives the discrepancy seen earlier where 0.1+0.2==0.3 is False.

It is not necessary to carry out conversions by hand routinely however it helps to do it once just to have an understanding and to avoid fairly common rounding errors.

Collections

So far we have only looked at scalar data types. Individual objects that are numbers (int, float), text (str) or boolean (bool). It is possible to group a series of these values into a collection. There are different types of collections.

The list

The most commonly used collection is called a list. This relates to a list used in every day life.

  • apples
  • bananas
  • grapes
  • oranges
  • pears

In python a list is enclosed in square brackets.

empty_list=[]

We can open double click the list in the variable explorer to open up in a separate window.

We can add a string to this list.

shop_list=['apples']

This list has a single item. We can see that this has an Index of 0, a Type of str, a Size of 1 (we will look at this in more detail later) and a Value of apples.

To add the next item to our list we need to separate it out from the first, to do this we need to use a separator (or delimiter) and we use the comma , to do this.

shop_list=['apples','bananas']

We can see that this has an entry at index 0 and an entry at index 1. We can add entries until we completely add all the items we want within the list.

shop_list=['apples','bananas','grapes','oranges','pears']

Within the script, the list can become hard to read if the list is very long. This would require quite a bit of scrolling to the right, so it is also possible to wrap the list up over multiple lines. In this case the script uses the opening [ and closing ] and knows that everything between these two belongs in the list. We can press after each comma , to begin a new line.

shop_list=['apples',
           'bananas',
           'grapes',
           'oranges',
           'pears']

Note that the Spyder IDE will automatically align each item in the list.

It is also possible to enter multiple lines in the console. To do this type in [Ctrl]+[↵].

Line wrapping should automatically continue when [↵] is pressed until the bracket is closed. Once the bracket is close [↵] will launch the code.

However [Ctrl]+[↵] can be used to always ensure that the line wrapping continues and pressing [Shift]+[↵] will stop the line wrapping and execute the code.

Indexing within a Collection

We can index by typing in the name of the list followed by the index enclosed in square brackets. With the list opened within the variable explorer, we can see the index. Index 2 is of type str with value grapes.

shop_list[2]

We can also index using a for loop. To get the index, we can use the function len with shop_list as an input argument to get the length of the list. We can then use the function range to get an index idx to loop over.

shop_list=['apples',
           'bananas',
           'grapes',
           'oranges',
           'pears']

for idx in range(len(shop_list)):
    print(idx)
    print(shop_list[idx])

However it is possible just to loop using the list to get the value val.

shop_list=['apples',
           'bananas',
           'grapes',
           'oranges',
           'pears']

for val in shop_list:
    print(val)

We can also use the enumerate function to get the index idx and value val.

shop_list=['apples',
           'bananas',
           'grapes',
           'oranges',
           'pears']

for idx,val in enumerate(shop_list):
    print(idx)
    print(val)

The last value of a list also has the index -1.

shop_list[-1]

And we can index in steps of -1 to the negative length of the list.

shop_list[-len(shop_list)]

We can use three input arguments for range: the start, stop and step so that we can look at indexing in reverse order.

shop_list=['apples',
           'bananas',
           'grapes',
           'oranges',
           'pears']

for idx in range(-1,-len(shop_list),-1):
    print(idx)
    print(shop_list[idx])

Like we saw earlier with a str, a colon : can be used to create a slice of a list. Recall that the lower bound is inclusive and the top bound is exclusive due to zero order indexing.

shop_list[0:2]

In the example below the lower bound 'apples' is included but the upper bound 'grapes' is not.

Like in the case of a str omitting a lower bound will assume slicing from the beginning 0 and omitting the upper bound will assume slicing to the end of the list. Indexing only using a colon : will therefore create a copy of the entire list.

shop_list[:]

We can also select every nth item in a list by indexing using a double colon followed by n. For example if we want every 2nd item.

shop_list[::2]

If we index every ::-1 item in the list, we get a reversed list.

shop_list[::-1]

Note because every index is a str. We can index into the list and then index into the str.

shop_list[2][::-1]

In the following example we select the second index from the list which gives the str 'grapes' and then index every negative 1 value of the str to reverse it giving 'separg'.

Nesting a collection

So far we have just looked at a list of the type str. however it is possible to create a list of multiple differing data types. For example.

random_list=['pie',3.14,3,True]

As you can see the Type differs for each item in the list and all are of size 1. It is also possible to nest a list within a list. Let's nest the list we created before as the last index.

shop_list=['apples',
           'bananas',
           'grapes',
           'oranges',
           'pears']

random_list=['pie',3.14,3,True,shop_list]

Note how the last index has a Type of list and Size of 5.

We can double click into this last index to get into the nested list.

We can index into the 4th index of shop_list, then we can index into the 3rd index of the nested list and finally we can index every negative 1 value to reverse the str.

random_list[4][3][::-1]

Concatenation of a list

The special methods defined in list class act in a similar way to those defined in the str class. The + operator performs concatenation and it is possible to duplicate the list n time using the * operator with a scalar n.

shop_list=['apples',
           'bananas',
           'grapes']

shop_list2=['oranges',
           'pears']

combined_list=shop_list+shop_list2

We can multiply shop_list2 three times using.

3*shop_list2

list methods

We can access a number of list methods by typing in the list name followed by a dot . and then tab .

Let's have a look at copy, this has no input arguments.

If we type the following into the console, the output value will be displayed.

shop_list2.copy()

We can also use the method index which takes a value as an input argument and returns the index if it is present in the list. Additional keyword input arguments start and stop are available to select a lower and upper index to define a subset of the list (although the documentation displays these as keyword input arguments, Python only works with these as positional input arguments).

For example we can use this to determine the index of the str 'pears'. This returns 1. If we instead search for the value 0, we get a ValueError: 0 is not in list.

shop_list2.index('pears')

We can use the method count to return the number of times a value occurs in a list.

For example if we duplicate each value in the list and then count the number of times 'pears' shows up we get the value 2 as expected.

shop_list4=2*shop_list2
shop_list4.count('pears')

Note if we return to using index, we only get the 1st occurrence in this case only index 1 shows (other indexes can be found using conditional logic, the append method and a for loop which we will look at).

shop_list4.index('pears')

These methods do not modify the original list. Some of the other methods will modify the original list instead of returning an output for example the reverse method which reverses the list.

combined_list.reverse()

Note this method does not have an output as it performs an in place update (mutates the instance self of the class list, where self is the name of the list the method is called from) which is why nothing is printed to the console.

The method sort, will sort the list (once again performing an in place update). It has a keyword input argument reverse=False meaning by default it will sort the values in order however can be easily changed to reverse the order by using the keyword input argument and assigning it to True.

combined_list.sort()
combined_list.sort(reverse=True)

Notice once again there is no output in the console. Instead the original list has been modified (or mutated).

The append method also mutates the list (performs an in place update).

Notice once again there is no output in the console. Instead the original list has been modified (or mutated).

shop_list4.append('berries')

If we assign this to an output. The output will be of the Type NoneType.

appended=shop_list4.append('bananas')

Note that the method append will append whatever value being appended to a single index at the end so it will create a nested list if the value being appended is a separate list.

shop_list4.append(shop_list2)

Concatenation can be used to concatenate multiple lists to create a new list. However the method extend may also be used which will extend the list.

shop_list4.extend(shop_list2)

We can remove a value from the list using the method remove which takes a value as a positional input argument.

For example we can remove the value 'bananas'.

shop_list.remove('bananas')

Notice that this is once again an in place upgrade and the list is mutated.

We can also use the method pop which by default removes the last item in a list. It has a default keyword input argument index=-1.

shop_list.pop()

Note the method pop has a return statement and the value popped in this case the str 'grapes' is returned.

It is also possible to clear a list using the method clear. This will make the list an empty list.

shop_list.clear()

The method insert works in a similar manner to append with the exception to requiring an additional positional input argument index.

For example we can insert shop_list2 at index 1 of shop_list.

shop_list.insert(1,shop_list2)

Note if we didn't want to nest the list, we could easily do this using indexing and concatenation. Since indexing a single element gives a str and not a list, it needs to be enclosed in square brackets to create a list with 1 str in it.

shop_list3=[shop_list[0]]+shop_list2[:]+shop_list[2:]

Let's create the following list which has duplicates itself three times.

shop_list=['apples',
           'bananas',
           'grapes']

shop_list2=['oranges',
           'pears']

shop_list2=3*shop_list2