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.
a = 5
id(a)
4335036896
id(5)
4335036896
b = a # OK, b is also 5
id(b)
4335036896
As we see, letting b = a
assigns the same location of a
to b
. When we assign a different value to b
, what happens?
b = 10
print(id(b), id(a), a)
4335037056 4335036896 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:
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?
140666241667840 140666241667840 [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:
a = [1, 2]
b = a
print(id(a), id(b))
a[0] = 5 # Change the first element
print(b) # Do we get [1, 2]?
140666241695104 140666241695104 [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:
import copy
a = [1, 2]
b = copy.copy(a)
print(id(a), id(b))
a[0] = 5
b
140666241486784 140666241667840
[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:
# 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
What's your name? skibidi
'skibidi'
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:
num1 = int(input("One integer please: "))
num2 = float(input("And a one floating point number too: "))
print(num1 * num2)
One integer please: 1.5
--------------------------------------------------------------------------- ValueError Traceback (most recent call last) <ipython-input-3-d8fdde0512ef> in <module> ----> 1 num1 = int(input("One integer please: ")) 2 num2 = float(input("And a one floating point number too: ")) 3 print(num1 * num2) ValueError: invalid literal for int() with base 10: '1.5'
type(eval(input()
))
[]
list
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:
print(num1, 'times', num2, 'is', num1 * num2)
10 times 5.2 is 52.0
By default, print()
will separate the arguments with a space. We can separate them with newlines with:
print('Hello', 'world', sep='-')
Hello-world
Alternatively, we can output data while also doing some formatting. Here is one such way, by using the format()
method of strings:
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:
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:
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:
c = 100
f = 32 + c * 1.8
liquid = 'Water'
print(f'{liquid} boils at {f} Fahrenheit')
Water boils at 212.0 Fahrenheit
x = 5
print(x)
5
Importing and modules¶
Recall that we were able to use functions from the math library by importing them:
import math
math.cos(math.pi)
-1.0
8 % 2
0
We can also import a library while also giving it a new alias:
import random as r
# Returns a random number from 0 to 1
r.random()
0.08006952281314084
r.randint(0, 1)
1
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.
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
<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, I've added a simple life.py
file that assigns a value to a parameter. Here's how we can use it:
import life
print(f'The answer was {life.answer_to_life} all along.')
hello, world The answer was 42 all along.
import life
from importlib import reload
reload(life)
hello, world
<module 'life' from '/Users/cagri/CENG240/week4/life.py'>