A variable is way to store information.
In the print_sum
example z
is a variable to the result of x + y
Variables are names for values. In Python the = symbol assigns the value on the right to the name on the left. The variable is created when a value is assigned to it. Here, Python assigns an age to a variable age and a name in quotation marks to a variable first_name.
=
symbol assigns the value on the right to the name on the left.age
and a name in quotation marks to a variable first_name
.
In [1]:
age = 29
first_name = 'Christian'
In [2]:
first_name
Out[2]:
If we try to access a variable that we didn't define we get an error:
In [3]:
last_name
This is a Traceback, comes directly from the Python interpreter. It's the Python way to tell us where we made a mistake.
In this case we have an error called NameError and a message name 'last_name' is not defined
In [ ]:
# Write your code here
__alistairs_real_age
have a special meaning
so we won't do that until we understand the convention.
In [ ]:
age = 18
first_name = first_name + " " + 'Calogero'
first_name
In [ ]:
FIRST_NAME = FIRST_NAME + 'Calogero'
Do you remember our first lines of code?
In [ ]:
def print_sum(x, y):
z = x + y
print(z)
print is the function we need to use when we want to print something
In [ ]:
print(first_name)
In [ ]:
print(age)
In [ ]:
print("Hello there")
In [ ]:
print(1)
As you can see we can print variables but also values
How can I print: My name is *first_name* and I am *age* old
?
In [ ]:
print(f"My name is {first_name} and I am {age} years old")
let's change this a little bit
In [ ]:
first_name = "Christian"
age = 29
In [ ]:
print(f"My name is {first_name} and I am {age} years old")
We have 3 components here:
* print function
* f""
* variables inside {}
We already know the print function , what is new here is the f"" notation.
It's a new notation called f-strings (available from Python 3.6) to format strings in Python.
Basically we put our text inside f"" and the variables we want to be replaced by their values inside curly brackets, like {first_name}.
This is a real power notation and you can it also when you create variables like this:
In [ ]:
message = f"My name is {first_name} and I am {age} years old"
In [ ]:
print(message)
What is the final value of position
?
In [ ]:
initial = "left"
position = initial
initial = "right"
What is this code doing?
In [ ]:
s = 60
m = s * 60
m
print
to display values."In Python there are mainly 4 data types:
In [ ]:
string_1 = 'This is a string'
In [ ]:
string_2 = "This is also a string"
In [ ]:
string_3 = '''This is a multi lines
strings and it works fine'''
In [ ]:
string_4 = """This is a multi lines
strings and it works fine"""
We can sum
string together, we concatenate
them
In [ ]:
first_name = 'Christian'
last_name = 'Barra'
name = first_name + last_name
In [ ]:
name
In [ ]:
# type
You can also multiply a string for a number
In [ ]:
name * 5
But not I want to multiply my name for 1.5!
In [ ]:
name * 2
Let's take about this error!
So far we have seen 2 types of numbers:
* 5 -> integer
* 1.5 -> float
This are the main numberical types in Python
Integer is a number without decimal parts.
A float number is a rappresentation of a fraction, as a consequence is an aprozzimation!
In [ ]:
integer_1 = 1
integer_2 = 10
integer_1 + integer_2
In [ ]:
integer_1 = 1
integer_2 = "10"
integer_1 + integer_2
You would probably see this error very often, when you are trying to do operation between types that are not compatible
In [ ]:
## But we can do this
integer_1 = 1
integer_2 = "10"
integer_1 + int(integer_2)
This is operation is called casting
, we are forcing a conversion to another type, in this case from string to int.
We can also cast to str (string) and we saw the sum (+) operation before.
integer_1 = 1
integer_2 = "10"
str(integer_1) + integer_2
In [ ]:
integer_1 = 1
integer_2 = "10"
str(integer_1) + integer_2
What will be the result of this operation?
We can sum integers and floats together
In [ ]:
int_1 = 5
float_1 = 5.5
In [ ]:
int_1 + float_1
Internally Python does a casting conversion from int to float, basically
In [ ]:
float(5) + 5.5
In [ ]:
my_first_list = [1,2,3,4,5]
In [ ]:
my_first_list
In [ ]:
a_mixed_list = [1,2,"Ciao"]
In [ ]:
a_mixed_list
In [ ]:
# this is useful when you can to cast something to a list
my_first_list = list("christian")
In [ ]:
my_first_list
In [ ]:
my_letters = list("Christian")
In [ ]:
my_letters
Lists are very flexible and they allow many operations. For example you can sum 2 lists
In [ ]:
a = [1,2,3,4,5]
b = [6,7,8,9,10]
In [ ]:
a + b
As with the strings more than summing we are concatenating our lists.
Same goes with multiplication, a * 3 is equal to a + a + a
In [ ]:
a*3
In [ ]:
a + a + a
If you want to add a new element to a list you can use append
.
In [ ]:
l = [] # this is an empty list
In [ ]:
l.append(1)
In [ ]:
l
Back to adding elements you can also use the sum operator (+) but is not really Pythonic...
In [ ]:
l = []
l = l + [1]
l
So, generally, just avoid this practise.
What about deleting an element?
In [ ]:
l = [1,2,3,4,5]
In [ ]:
l
In [ ]:
l.pop()
In [ ]:
l
pop removes and returns the last element when you don't specify and index
In [ ]:
l = [1,2,3,4,5]
If you specificy and index betweenn () it will remove that element.
In [ ]:
l.pop(1)
In [ ]:
l
I deleted the first element, index 1, why 2 has been removed?
First, lists are an indexed collection of elements, so they have and index and an order.
The index starts from 0 not from 1
So index n. 1 is the second element, while index 0 is the first element.
With this concept we can introduce the brackets notation.
In [ ]:
l = [1,2,3,4,5]
We can access an element of a list using the brackets notation with an index inside:
In [ ]:
l[0]
In [ ]:
l[1]
The element should exist:
In [ ]:
l[100000]
Otherwise we would get an error.
We can also slice lists:
In [ ]:
l
In [ ]:
l[0:2]
This is what we called slicing.
Slicing is very powerful Python, is builty by, for now, 2 parts:
* the first index of the element, **this element will be included**
* the last index of element we want to consider, but **it won't be included**
Slicing returns a new list
2 important concepts:
* slicing is not like accessing an element
* slicing returns a list
* slicing don't raise a `list index out of range` but returns an empty list
In [ ]:
l[-50:-20]
In [ ]:
l[-50:20]
In [ ]:
l[0:10000000000]
Time for cool tricks:
In [ ]:
l[:]
from the beginning
till the end of the list
You can also mix the concepts together
In [ ]:
# From element with index 1 till the end
l[1:]
In [ ]:
# from the beginning of the list till element with index 4, not included
l[:4]
In [ ]:
# Element with index 4 is:
l[4]
In [ ]:
a_string = "Hello there, how is going?"
In [ ]:
a_string[0]
In [ ]:
a_string[4:10000]
In [ ]:
a_string[:]
Same slicing concepts can be applied to strings
Before moving to the next type
In [ ]:
l = [1,2,3,4,5]
In [ ]:
l[0] = "Ciao"
In [ ]:
l
This is how you change a value inside the list.
Nitty great details and best practices:
* avoid popping with an index
* avoid **adding** elements at the beggining/in the middle of your list.
Dictionaries are a key=value collection, they are not ordered (opposed to lists that are ordered) and you can put mixed types of variables inside (as for the lists.
This is how you create a dictionary:
In [ ]:
info_class = {
"number_of_students": 10,
"details": []
}
In [ ]:
info_class
In [ ]:
my_first_dictionary = dict(my_key="my_value")
In [ ]:
my_first_dictionary
How can we add a new element to a dictionary?
We already know the brackets notation, but with dicts we use the key instead of the index
In [ ]:
my_first_dictionary['new_key'] = "My new key"
In [ ]:
my_first_dictionary
If we want to access the element of a dict we use the same notation
In [ ]:
my_first_dictionary['new_key']
If the element does not exist we get an error
In [ ]:
my_first_dictionary['an_element']
One way to avoid this is using get
In [ ]:
my_first_dictionary.get("an_element")
If the element with key an_element
does not exist we the default value, None if something is not specified
In [ ]:
my_first_dictionary.get("an_element", "My default value")
How do you delete an element?
In [ ]:
my_first_dictionary
In [ ]:
del my_first_dictionary["my_key"]
In [ ]:
my_first_dictionary
In [ ]:
# Remeber to check if the key exists when deleting an element
del my_first_dictionary["my_key"]
In [ ]:
# Otherwise you can use pop with a default value
my_first_dictionary.pop("my_key", "Does not exist")
You can't add dicts but you can update one using values from another one, but pay attention to elements with the same keys.
In [ ]:
my_first_dictionary = {"my_key":"my_value", "1": []}
In [ ]:
my_second_dictionary = {"my_key":"my_2_value", "2":"my_second_value"}
In [ ]:
my_first_dictionary.update(my_second_dictionary)
In [ ]:
my_first_dictionary
In [ ]:
# Now I want to add the students that I miss from the list inside the dictionary
# student n. 4 is Bartosz and number 5 is Wioleta
# Use the way that you prefer, at the end you should obtain a dict with they keys that you don't have (4,5)
list_of_student_id = [1,2,3,4,5]
student_names = {1: "Christian", 2:"Monika", 3: "Lucasz"}
In [ ]:
# We have the dict of new students and we want to add the add this new elements to the old one
# How can we do this?
old_students = {1: "Christian", 2:"Monika", 3: "Lucasz"}
new_students = {6: "Anna", 7:"Jacek", 8: "Arnold"}
In [ ]:
# I'd like 2 new variables, first_name and last_name
# How can you extract first_name and last_name from my_name?
my_name = "christian Calogero Barra"
Loops and flow controls serve the need to control how your code is going to be executed by Python.
More or less they translate to computers this kind of actions:
* If the fridge is empty go to the supermarket
* For each ingredient inside the recipe cook them
* While the house is not clean, keep cleaning
Flow control means to change the behaviour of your code depending from some conditions (True, False, minor, major or equal of something). In Python we have:
In [ ]:
counter = 5
if counter > 1:
print("Greater than 1")
else:
print("Minor of 1")
Let's break this down:
1. we create a new variable: counter = 5
2. we check if counter is greater than 1
3. we print something
4. else is executed when counter > 1 is False
5. we print something else
Let's look at 2 exmaples with a subtle difference
elif stands for else if
In [ ]:
# Example 2
gender = "m"
if gender == "f":
print("oman")
elif gender == "v":
print("man")
elif gender == "a":
print("man")
elif gender == "f":
print("man")
In the first example the second if is something separated from the first one.
Let's explain this a little bit more.
for iterates over something
, we can call this something an object
In [ ]:
for element in [1,2,3,4,5]:
print(element)
In [ ]:
my_string = "Christian"
for letter in my_string:
print(letter)
In [ ]:
my_dict = {"first_name": "Christian", "last_name": "Barra"}
for key in my_dict:
print(key)
In [ ]:
my_dict = {"first_name": "Christian", "last_name": "Barra"}
In [ ]:
for item in my_dict:
print(item)
In [ ]:
for key in my_dict:
print(my_dict[key])
while is repeated till the expression results "True"
In [ ]:
i = 8
while i < 10:
i = i + 1
print(i)
In [ ]:
x = 0
while x:
print("Hello")
In the next challenges you will have to fill some empty spaces (__). Feel free to scroll back to each part of our lesson
# Concatenate all words: ["red", "green", "blue"] => "redgreenblue"
words = ["red", "green", "blue"]
result = ____
for ____ in ____:
____
print(result)
In [ ]:
# Put your code here
words = ["red", "green", "blue", "yellow"]
result = ""
for word in words:
result = result + word
print(result)
In [ ]:
words[0] + words[1] + words[2]
In [ ]:
empty = "-"
In [ ]:
empty.join(words)
# Create acronym: ["red", "green", "blue"] => "RGB"
acronym = ___
for word in ["red", "green", "blue"]:
if ___ == 'r':
acronym = ___
elif ___ == 'g':
acronym = ___
elif ___ == 'b':
acronym = ___
print(acronym)
In [ ]:
# Put your code here
acronym = ""
for word in ["red", "green", "blue"]:
if word[0] == 'r':
acronym = acronym + word[0]
elif word[0] == 'g':
acronym = acronym + word[0]
elif word[0] == 'b':
acronym = acronym + word[0]
print(acronym.upper())
# Count the number of `a` inside the sentence
sentence = """From a very early age, perhaps the age of five or six,
I knew that when I grew up I should be a writer. Between the ages of about seventeen
and twenty-four I tried to abandon this idea,
but I did so with the consciousness that I was outraging my true nature
and that sooner or later I should have to settle down and write books."""
counter = ___
for letter in sentence:
___ ___ == ___:
counter = ___
print(counter)
In [ ]:
# Put your code here
sentence = """From a very early age, perhaps the age of five or six,
I knew that when I grew up I should be a writer. Between the ages of about seventeen
and twenty-four I tried to abandon this idea,
but I did so with the consciousness that I was outraging my true nature
and that sooner or later I should have to settle down and write books."""
counter = 0
for letter in sentence:
if letter == "a":
counter += 1
print(counter)
# Separate odd and even numbers
numbers = [1,2,3,4,5,6,7,8,9,10]
odd_numbers = []
even_numbers = []
for number in numbers:
if number % 2 == 0:
___
else:
___
print(odd_numbers)
print(even_numbers)
In [ ]:
numbers = [1,2,3,4,5,6,7,8,9,10,12]
odd_numbers = []
even_numbers = []
for number in numbers:
if number % 2 == 0:
even_numbers = even_numbers + [number]
else:
odd_numbers = odd_numbers + [number]
print(even_numbers)
print(odd_numbers)
Functions are a way to write reusable code, you write function using the def
word followed my the name of the function.
In [ ]:
def my_Function():
print("This is my function")
In [ ]:
# This is how you calla function
my_Function()
A function generally returns something.
In [ ]:
def is_even(number):
'''
Return True if the number is even.
False is all the other cases.
This text is called docstring.
'''
if number % 2 == 0:
return True
return False
In [ ]:
is_even(1)
In [ ]:
is_even(10)
Docstrings help you documenting your code.
The docstring of is_even is used by help function to provide more information about you function.
In [ ]:
help(is_even)
In [ ]:
def who_is_bigger(x, y):
"""
Return the greater number between x and y
"""
if x >= y:
return x
return y
In [ ]:
who_is_bigger(1,10)
In [ ]:
who_is_bigger(1, 10)
You can specifiy the arguments in 2 ways:
* like arguments
* key=value notation
In [ ]:
# this is like arguments
who_is_bigger(1,10)
In [ ]:
# with key=value notation
who_is_bigger(x=1,y=10)
In [ ]:
# with key=value the order is no longer important
who_is_bigger(y=10,x=1)
Our function can accept many arguments, you can also specify default arguments:
In [ ]:
def say_something(message="Ciao"):
"""
Print a message, "Ciao" by default
"""
print(message)
In [ ]:
say_something()
If we don't put a default for our argument and we call the function we get an error:
In [ ]:
def say_something(message="Ciao"):
"""
Print a message, "Ciao" by default
"""
print(message)
In [ ]:
say_something()
Another important concept in namespace. You can think of a namespace as a personal shelves where Python put things. Functions have a reserved namespace that is called local.
In [ ]:
x = 10
def tell_me_the_value():
x = 1
print(x)
In [ ]:
tell_me_the_value()
In [ ]:
x = 10
def tell_me_the_value():
print(x)
In [ ]:
tell_me_the_value()
This is how Python will look for variables:
Local is what we have seen with our tell_me_the_value function. Python will first look inside the function and then outside.
In [ ]:
# Example 1
x = 10
def tell_me_the_value():
x = "Ciao"
def something_else(x):
print(x)
something_else(x)
In [ ]:
tell_me_the_value()
x
In [ ]:
# Example 2
x = 10
def tell_me_the_value():
def something_else(x):
x = 11
something_else(x)
In [ ]:
tell_me_the_value()
x
In the example 1 python is accessing the enclosed scope, wheras in the example 2 it follow this rule:
1. look inside the local scope -> empty
2. look inside the enclosed scope (body of tell_me_the_value) -> empty
3. look inside the global scope -> found (x=10)
In [ ]:
# Example 1
x = 10
def tell_me_the_value():
global x
x = "Ciao"
def something_else(x):
print(x)
something_else(x)
In [ ]:
tell_me_the_value()
x
global tells Python to consider a variable like a global and not as a local (as you can see x has a different value now)
# challenge n.1
input_list = [1,2,3, None, 5]
def api_call(input_list):
'''
Take a list of values and then return an integer
with the number of numbers in that list
'''
counter = 0
for element in input_list:
if type(element) is int:
counter = counter + 1
return counter
In [ ]:
# Put your code here
# challenge n.2
import random
input_list = [random.randint(-1,1) for i in range(100)]
def api_call(input_list):
'''
Take a list of values and then it return a list with the
same number of elements [True, False,...] where
True if element >= 0
False if element < 0
'''
list_true_false = []
for element in input_list:
if element ____ ____:
____
else:
____
return ____
In [ ]:
# Copy your function here
# challenge n.3
import random
input_list = [random.randint(-10,10) for i in range(100)]
def api_call(input_list):
'''
This function take a list of values and then return a dictionary.
The dictionary has 2 keys:
bigger_0: number of numbers inside the given list > 0
lower_0: number of numbers inside the given list < 0
'''
In [ ]:
# Copy your function here
A module is a way for organize Python code together.
Let's switch to visual studio studio and create 2 files inside the same directory.
# utils.py
first_name = "YourName"
last_name = "YourLastName"
def return_my_name():
return f"{first_name} {last_name}"
# app.py
from utils import return_my_name
return_my_name()
Now if we execute the code, with python app.py
you should see something.
Inside app.py
we imported the module utils
.
utils.py
is a Python script that we can import.