Zero to Hundred in Python
9 min Shivan Sivakumaran Suggest ChangesPython is a general-purpose programming language, excellent if you want to begin learning how to program or build a company to take over the world.
Programming is a powerful skill and Python is the easy entry point.
Without wasting much time, this post provides a zero to hundred with Python… with some tips at the end.
Here is a video demonstration below:
Printing and Variables
Lesson number 1 (or 0…), printing to the console. We open up the Python interpreter:
$ python
# or
$ pipenv run ipython
Next, we write our notorious first line of code.
print("Hello World")
>>> Hello World
Next, we learn about commenting code.
# this is a comment and will not be
# evaluated like code
Variables store values, or they point to places in memory in which these values are stored.
number = 10
string = "This is a string"
print(number)
>>> 10
print(string)
>>> "This is a string"
We can use f-strings
to print our code within a string, using curly braces to enclose our variable names or Python code.
print(f"number: {number}, and string: {string}")
>>> "number: 10, and string: This is a string"
Variables aren’t all the same. They have types. For example, number is an integer
and string is a collection of characters, called a string
.
print(f"{type(number} and {type(string)}")
>>> "<class 'int'> and <class 'str'>"
Functions
If we have code we have to write, again and again, it’s good practice to not repeat ourselves. Instead of repeating ourselves, we put this code in a function and call this function. Let’s create a function for the Fibonacci sequence, where we pass a number, n
and this will give the nth
sequence.
# the fibonacci sequence
# 0, 1, 1, 2, 3, 5, 8...
def f(n: int) -> int:
"""
Fibonacci sequence
---
params
n (int) number requested in sequence
returns
integer in fibonacci sequence
"""
if n == 0:
return 0
elif n == 1 or n == 2:
return 1
else:
return f(n-1) + f(n-2)
There is a number of notions going on in this above code. Firstly, we introduce function using def. We use typing, where we accept n as an integer (n: int) and the function will return an integer (-> n
).
We include a doc-string enclosed in triple quotes ("""). The primary purpose of the doc-string is to explain what the function does.
Also, this function is a good example of conditionals (also known as flow control, because we are controlling the flow of the execution). The if
statements checks if the following is True
or False
. If True
, then the code block is executed.
NOTE: Indentation in Python is important.
If the if
statement is False
, then elif
(short for else-if) is checked. We can see the use of or
meaning only n == 1
or n == 2
need to be true for the code block to fire, in this case returning 1
. Finally, else
is fired when the previous if
and elif
statements are False
.
This line, f(n-1) + f(n-2)
is an example of recursion. The function f
calls itself but with one less n
and two less n
.
Lists
We can do a lot with functions and variables. Let’s have a look at different data types in Python starting with lists.
We set up a range.
r = range(10)
r
>>> range(0, 10) # number from 0 up to and not including 10
We create a list.
lst = list(r)
lst
[0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
If we want to access elements in the list, we do this:
lst[0] # the first element of the list is "0th" index
>>> 0
lst[1:3] # the second and up to and not including the 3rd element
>>> [1, 2]
lst[-1] # the last element
>>> 9
lst[::-1] # reverses the list
>>> [9, 8, 7, 6, 5, 4, 3, 2, 1, 0]
Lists don’t need to be integers, they can be functions.
def f1(x: int) -> str:
return f"f1: {x + 1}"
def f2(x: int) -> str:
return f"f2: {x ** 2}"
def f3(x: int) -> str:
return f"f3: {1 / x}"
funcs = [f1, f2, f3]
for func in funcs:
print(func(2))
>>> "f1: 3"
>>> "f2: 4"
>>> "f3: 0.5"
This is a good way to introduce the notorious for loop. A list is an example of an iterator. We can use a for loop to iterate for a list for example.
for i in lst:
output = f(i)
print(output, end=", ")
>>> "0, 1, 1, 2, 3, 5, 8, 13, 21, 34"
Instead of printing a list of numbers, we can return a list.
output = []
for i in lst:
output.append(f(i))
output
>>> [0, 1, 1, 2, 3, 5, 8, 13, 21, 34]
Instead of creating an empty list and utilising multiple lines, we can turn this into a ‘one-liner’. We use the infamous list comprehension.
output = [f(i) for i in lst]
output
>>>[0, 1, 1, 2, 3, 5, 8, 13, 21, 34]
Importing Libraries
A lot of the time, we will be using other people’s well-written code. This saves time, but also there are some very intelligent people on this planet. They are providing solutions to problems that I could not solve in my lifetime for FREE. One example is a library called pandas
, which is popular in data analytics.
Back to our Fibonacci function, you will notice the problem with recursions is that the more numbers we request in the sequence, the longer the program takes.
We can show this using IPython’s magic function, %timeit
.
times = []
for i in range(33): # we will do the first 32 numbers in sequence
t = %timeit -n1 -o f(i)
times.append(t.best)
The -n<N>
tag means how many times a function will loop, in our case, only once; -o
outputs the timeit
result to a variable, t
in our case, for later inspection.
Now we will use pandas
to tabulate our data.
import pandas as pd
df = pd.DataFrame(data=times, columns=["f"]
df.head() # prints first five results by default
>>>
f
0 5.829997e-07
1 6.790001e-07
2 7.760000e-07
3 1.695000e-06
4 2.799000e-06
We have given pandas
the alias pd
. This saves us having to write “pandas” all the time.
By importing pandas
we have access to the DataFrame
class. We can plot this data and save it. We use another library, matplotlib
, which is a graphing library to save our graph as an image.
import matplotlib.pyplot as plt
g = df.plot(title="Time on Fib")
g.set_xlabel("n")
g.set_ylabel("times (s)")
plt.savefig("f.png")
We can see the exponential rise in the time the program takes as n
rises.
We can calculate the total time:
df.sum()
>>> f 3.693825 # total time
>>> dtype: float64
More data types — Dictionaries
We will introduce dictionaries. Dictionaries have a key and a value pair.
dct = {
"key1": "value1",
"key2": ["value2", "value3"],
"random name for a key": 1,
"key4": (2000, 3000)
}
And to get a value from a dictionary:
dct["key4"]
>>> (2000, 3000) # this is a tuple by the way
If we refer to a key that doesn’t exist (e.g. dct["key5"]
, we get an error. Alternatively we can use the get()
method, so our code doesn’t error and get None
returned instead.
dct.get("key5") is None
>>> True
We can even apply a for loop to dictionaries.
for k, v in dct.items():
print(f"key: {k}, value: {v}")
>>> key: key1, value: value1
>>> key: key2, value: ['value2', 'value3']
>>> key: key3, value: 1
>>> key: key4, value: (2000, 3000)
Coming back to our Fibonacci function example, we can use dictionaries to store some previously calculated values instead of calculating then again and again. This is called caching and what we are doing is trading memory for less compute time.
cache = {}
def f_better(n: int) -> int:
"""
Fibonacci sequence - !!with caching!!
---
params
n (int) number requested in sequence
returns
(int) in fibonacci sequence
"""
if all([cache.get(n-1), cache.get(n-2)]):
return cache[n-1] + cache[n-2]
elif n == 0:
return 0
elif n == 1 or n == 2:
return 1
else:
cache[n] = f_better(n-1) + f_better(n-2)
return cache[n]
We have added a conditional in the beginning that simple checks if the previous result is in the cache and uses this stored value instead of calculating from scratch all the time.
Another addition is that the newly calculated value is stored in the cache.
We can measure how long this new function runs.
times = []
for i in range(35):
t = %timeit -n1 -o -q f_better(i)
times.append(t.best)
df['f_better'] = pd.DataFrame(data=times)
df.tail(10)
>>>
f f_better
23 0.005453 5.040001e-07
24 0.008786 1.269000e-05
25 0.014167 1.357200e-05
26 0.022997 4.909998e-07
27 0.037203 1.430600e-05
28 0.060064 1.520500e-05
29 0.097307 5.379998e-07
30 0.157304 1.653000e-05
31 0.254740 1.687500e-05
32 0.412715 5.299999e-07
We can plot this.
g = df.plot(title="Comparing")
g.set_xlabel("n")
g.set_ylabel("time (s)")
plt.savefig("f_better.png")
We can see that the time taken is dramatically less with this caching implementation.
Finally, we can print this cache
.
cache
>>> {
3: 2,
4: 3,
6: 8,
7: 13,
9: 34,
10: 55,
12: 144,
13: 233,
15: 610,
16: 987,
18: 2584,
19: 4181,
21: 10946,
22: 17711,
24: 46368,
25: 75025,
27: 196418,
28: 317811,
30: 832040,
31: 1346269,
33: 3524578,
34: 5702887
}
Classes
In Python, everything are objects. To create our own objects, we create classes.
class Car:
"""
Class for cars that people drive
---
params
make (str): make of the car
model (str): model of the car
year (int): year car was made
"""
def __init__(self, make: str, model: str, year: int=2021):
self.make = make
self.model = model
self.year = year
self.__is_driving = False
def __repr__(self):
return f'{self.make} - {self.model}'
def drive(self) -> str:
if self.__is_driving == False:
print(f'{self.make} - {self.model} is now driving')
self.__is_driving = True
else:
print(f'{self.make} - {self.model} is already driving!')
def stop(self) -> str:
if self.__is_driving == True:
print(f'{self.make} - {self.model} has stopped')
self.__is_driving = False
else:
print(f'{self.make} - {self.model} is already stopped!')
Let’s test drive our new class.
shivan = Car('Toyota', 'Corolla', '2008')
bruno = Car('Tesla', 'Model3')
We have two objects, shivan
and bruno
derived from the Car
class.
bruno
>>> "Tesla - Model3"
shivan.drive()
>>> "Toyota - Corolla is now driving"
shivan.drive()
>>> "Toyota - Corolla is ALREADY driving"
shivan.stop()
>>> "Toyota - Corolla has stopped"
bruno.stop()
>>> "Tesla - Model3 is ALREADY stopped!
Ending notes
So now you have seen the basics of Python. This is only the beginning. Go building something. Go be somebody!
Whatever interests you big or small, go make it. Don’t be afraid of making mistakes, but do ask for help when needed. Build a habit, small steps every day, leads to a large distance over time.
Finally, if you have any feedback, suggestions, or questions please let me know. Remember to respect yourself, stay Pythonic.
Ka kite anō!