Actions, continued...¶

Aliasing¶

The id() function displays the locations of variables in memory. Using this function, we can observe what happens when we assign values to variables.

In [1]:
a = 5
id(a)
Out[1]:
4352841784
In [2]:
id(5)
Out[2]:
4352841784
In [3]:
b = a # OK, b is also 5
id(b)
Out[3]:
4352841784

As we see, letting b = a assigns the same location of a to b. When we assign a different value to b, what happens?

In [4]:
b = 10
print(id(b), id(a), a)
4352841944 4352841784 5

The location of b changes, and a retains the value of 5.

Now, let's see when we use mutable containers, such as a list:

In [5]:
a = [1, 2]
b = a
print(id(a), id(b)) # Same location

a = [3, 4] # Assign a different list

print(b) # Will b also change?
4469397568 4469397568
[1, 2]

Assigning an entirely new list to a only changes a, and b keeps its original value.

However, let's look at when we change some part of the list:

In [6]:
a = [1, 2]
b = a
print(id(a), id(b))

a[0] = 5 # Change the first element

print(b) # Do we get [1, 2]?
4469401600 4469401600
[5, 2]

We didn't set b[0] = 5, what gives?

The reason for this is that a and b are aliases for the same location in memory, and changing an element using one variable will have the same effect as changing it with the other variable.

If we explicitly want a and b to hold distinct copies of data (such as lists), we can use the copy module, as follows:

In [7]:
import copy
In [8]:
a = [1, 2]
b = copy.copy(a)
print(id(a), id(b))

a[0] = 5
b
4469366912 4469397568
Out[8]:
[1, 2]

As we see, using the copy function will create a new list with a different location, and that's how b does not change when a is changed.

Note that copy.copy() will not recursively copy list contents, so if you have nested lists that you want to copy, you can use copy.deepcopy() instead.

Basic I/O¶

When writing a program, you will frequently need to receive input from some source, and also you will need ways to output your results as well.

For receiving input, Python provides the input() function which prompts the user to enter some input and stores it as a string:

In [9]:
# We can display a prompt to the user inside the input() function
# by providing a string
name = input("What's your name? ")
# We can display the provided name
name
Out[9]:
'cagri'

Occasionally, we would like to receive input which is of a number format. For this, we can use functions such as int() and float() to convert the input to a number, or eval() which will evaluate the provided input string as an expression:

In [10]:
num1 = int(input("One integer please: "))
num2 = float(input("And a one floating point number too: "))
print(num1 * num2)
82.5

For output, we have been using the print() function with simple strings and numbers. There are various ways to format our output.

First of all, we can give multiple arguments to print() as follows:

In [11]:
print(num1, 'times', num2, 'is', num1 * num2)
15 times 5.5 is 82.5

By default, print() will separate the arguments with a space. We can separate them with newlines with:

In [12]:
print('Hello', 'world', sep='\n')
Hello
world

Alternatively, we can output data while also doing some formatting. Here is one such way, by using the format() method of strings:

In [13]:
print('Hi, my name is {}!'.format(name))
Hi, my name is cagri!

When using str.format(), the variables replace the curly braces in the original string. Normally, the replacement is done left-to-right, but we can change the ordering by specifying the indices:

In [14]:
print('This is week {1} for section {2} of {0}'.format('CENG240', 4, 10))
This is week 4 for section 10 of CENG240

Alternatively, we can also format by using the % operator which is similar to .format(), but uses special tokens with leading % sign for formatting, as follows:

In [15]:
print('%d / %d = %f' % (22, 7, 22/7))
22 / 7 = 3.142857

You should consult the textbook for other formatting specifiers using %.

Lastly, starting from Python 3.6+, we can also use f-strings, which are also very useful for string formatting:

In [16]:
c = 100
f = 32 + c * 1.8

liquid = 'Water'
print(f'{liquid} boils at {f} Fahrenheit')
Water boils at 212.0 Fahrenheit

Importing and modules¶

Recall that we were able to use functions from the math library by importing them:

In [17]:
import math
math.cos(math.pi)
Out[17]:
-1.0

We can also import a library while also giving it a new alias:

In [18]:
import random as r
# Returns a random number from 0 to 1
r.random()
Out[18]:
0.5745958802551897

If we want, we can import everything inside a library with the following syntax. Don't forget that doing this will make all of the names inside the library available to you, which can cause problems if you have used those names before.

In [19]:
floor = '2nd'
print(f"I'm on the {floor} floor!")

from math import *
print(floor(2.5)) # Floor function in maths, rounds down

floor # Where's my variable?
I'm on the 2nd floor!
2
Out[19]:
<function math.floor(x, /)>

Finally, you can make your own modules inside your own .py files, and import them using the exact syntax, which will let you use functions and variables defined in one file in another file.

In this same directory, you can create a file called life.py that assigns a value to a parameter, with just one line of content:

answer_to_life = 42

Having done that, here's how we can use it:

In [20]:
import life
print(f'The answer was {life.answer_to_life} all along.')
The answer was 42 all along.