This is a small README placeholder so the repository doesn't look empty.
Functions
: a block of code that performs a specific task.Bugs and Debugging
: mistakes in the code and how to fix them.Data Types
: the type of data that a variable can store.Named Parameters
: parameters that are passed by name.String Methods
: methods that can be used with strings.Integers and Operators
: operators that can be used with integers.Type Conversion
: converting one data type to another.
Week 0 notes
Note: I have alreaady some experience with Python, but I will worte down as much as possible either way.
print("Hello Bison!")
-
We just used the
print
function. -
Argument: value passed to a function.
-
Side effects: printing to the console.
-
Bugs: mistakes in the code.
-
Debugging: finding and fixing bugs.
-
Return values: values returned by functions.
-
Variables: store values for later use.
-
Comments: notes to self and others.
-
Pseudocode: planning code before writing it.
-
String: a sequence of characters.
-
Concateation: combining strings.
Data Type | Description |
---|---|
int |
Integer numbers (e.g., 5, -3, 100) |
float |
Floating point numbers (e.g., 3.14, -0.001, 2.0) |
complex |
Complex numbers (e.g., 3 + 4j, -2.5 + 0.1j) |
str |
String (e.g., "hello", 'world', "123") |
list |
List (e.g., [1, 2, 3], ['a', 'b', 'c']) |
tuple |
Tuple (e.g., (1, 2, 3), ('a', 'b', 'c')) |
set |
Set (e.g., {1, 2, 3}, {'a', 'b', 'c'}) |
dict |
Dictionary (e.g., {'key': 'value'}, {1: 'one', 2: 'two'}) |
bool |
Boolean (True or False) |
bytes |
Immutable bytes sequence (e.g., b'hello', b'\x00\x01\x02') |
print(*objects, sep=' ', end='\n', file=sys.stdout, flush=False)
- *objects: zero or more objects to print.
- sep: how to separate objects.
- end: what to print at the end.
- file: where to print.
- flush: whether to forcibly flush the stream.
- Parameters: variables in the function definition.
- Arguments: values passed to the function.
print("Hello ", end="")
print("Bison!")
The expected output is Hello Bison!
.
- Named parameters: they are optional and can be passed in any order.
- Positional parameters: they are required and must be passed in the correct order.
- Escape character: backslash ().
- f-string: formatted string literal.
name = "Bison"
print(f"Hello, {name}!")
The expected output is Hello, Bison!
.
-
.strip(): remove whitespace.
-
.capitalize(): capitalize the first letter.
-
.title(): capitalize the first letter of each word.
-
.lstrip(): remove whitespace from the left.
-
.rstrip(): remove whitespace from the right.
name = input("What's your name? ").strip().title()
print(f"Hello, {name}!")
-
Style: how code looks and is organized.
-
PEP 8: Python Enhancement Proposal 8.
- .split(): split a string into a list.
names = input("Enter names separated by commas: ").split(", ")
print(names)
Expected output: ['Alice', 'Bob', 'Charlie']
.
-
Integer: whole numbers.
-
Operators: symbols that perform operations.
Operator | Name | Description |
---|---|---|
+ | Addition | Adds two numbers. |
- | Subtraction | Subtracts two numbers. |
* | Multiplication | Multiplies two numbers. |
/ | Division | Divides two numbers. |
// | Floor division | Divides two numbers and rounds down. |
% | Modulus | Returns the remainder of the division. |
** | Exponentiation | Raises a number to the power of another. |
The interactive mode is a way to run Python code line by line.
x = input("What's x? ")
y = input("What's y? ")
z = x + y
print(z)
-
Type conversion: converting one data type to another.
-
int(): convert to integer.
x = int(input("What's x? "))
y = int(input("What's y? "))
print(x + y)
-
Floating point values: decimal numbers.
-
float(): convert to floating point.
x = float(input("What's x? "))
y = float(input("What's y? "))
print(x + y)
- round(): round a number to a specified number of decimal places.
round(number[, ndigits])
x = float(input("What's x? "))
y = float(input("What's y? "))
z = round(x + y)
print(z)
- f-string: formatted string literal.
x = float(input("What's x? "))
y = float(input("What's y? "))
z = round(x + y)
print(f"{z:,}")
- /: division.
x = float(input("What's x? "))
y = float(input("What's y? "))
z = x / y
print(f"{z:.2f}")
- def: define a function.
def main():
name = input("What's your name? ")
hello(name)
def hello(to="Bison"):
print("Hello ", to)
main()
- Scope: where a variable is accessible.
- return: return a value from a function.
def main():
x = int(input("What's x? "))
print("x squared is", square(x))
def square(n):
return pow(n, 2)
main()
Comparison Operators
: operators that compare two values.if
: a way to run code conditionally.elif
: a way to run code conditionally if the first condition is false.else
: a way to run code if all other conditions are false.or
: a way to combine conditions.not equal
: a way to check if two values are not equal.and
: a way to combine conditions.chains
: a way to combine multiple conditions.modulo
: a way to get the remainder of a division.boolean
: a way to represent true or false values.pythonic expressions
: a way to write code in a more concise way.match
: a way to match a value to a pattern.
Week 1 notes
- Hability to ask and answer questions
Symbol | Meaning |
---|---|
> |
Greater than |
< |
Less than |
== |
Equal to |
!= |
Not equal to |
>= |
Greater than or equal to |
<= |
Less than or equal to |
x = int(input("What's x? "))
y = int(input("What's y? "))
if x < y:
print("x is less than y")
if x > y:
print("x is greater than y")
if x == y:
print("x is equal to y")
- In this example "x < y" is a boolean expression
x = int(input("What's x? "))
y = int(input("What's y? "))
if x < y:
print("x is less than y")
elif x > y:
print("x is greater than y")
elif x == y:
print("x is equal to y")
x = int(input("What's x? "))
y = int(input("What's y? "))
if x < y:
print("x is less than y")
elif x > y:
print("x is greater than y")
else:
print("x is equal to y")
x = int(input("What's x? "))
y = int(input("What's y? "))
if x < y or x > y:
print("x is not equal to y")
else:
print("x is equal to y")
x = int(input("What's x? "))
y = int(input("What's y? "))
if x != y:
print("x is not equal to y")
else:
print("x is equal to y")
x = int(input("What's x? "))
y = int(input("What's y? "))
if x == y:
print("x is equal to y")
else:
print("x is not equal to y")
-
Identation is important in Python, if you don't use it, you will get an error
-
The colon is also important, it tells Python that the next line is going to be a block of code
score = int(input("Score: "))
if score >= 90 and score <= 100:
print("Grade: A")
elif score >= 80 and score < 90:
print("Grade: B")
elif score >= 70 and score < 80:
print("Grade: C")
elif score >= 60 and score < 70:
print("Grade: D")
else:
print("Grade: F")
score = int(input("Score: "))
if score >= 90:
print("Grade: A")
elif score >= 80:
print("Grade: B")
elif score >= 70:
print("Grade: C")
elif score >= 60:
print("Grade: D")
else:
print("Grade: F")
score = int(input("Score: "))
if score >= 90:
print("Grade: A")
if score >= 80:
print("Grade: B")
if score >= 70:
print("Grade: C")
if score >= 60:
print("Grade: D")
Symbol | Meaning |
---|---|
+ |
Addition |
- |
Subtraction |
* |
Multiplication |
/ |
Division |
% |
Modulo (Remainder) |
x = int(input("What's x? "))
if x % 2 == 0:
print("x is Even")
else:
print("x is Odd")
def main():
x = int(input("What's x? "))
y = int(input("What's y? "))
if is_even(x):
print("x is Even")
else:
print("x is Odd")
def is_even(n):
if n % 2 == 0:
return True
else:
return False
main()
def main():
x = int(input("What's x? "))
y = int(input("What's y? "))
if is_even(x):
print("x is Even")
else:
print("x is Odd")
def is_even(n):
return n % 2 == 0
main()
name = input("What's your name? ")
match name:
case "Harry" | "Herminone" | "Ron":
print("Gryffindor")
case "Draco:":
print("Slytherin")
case _:
print("Who?")
Loops
: the hability of doing something multiple times.while
: a way to repeat a block of code while a condition is true.for
: a way to repeat a block of code a number of times.Validating input
: a way to ensure that the input is correct.Iteration with Lists
: a way to iterate over a list.len
: a way to get the length of a list.Dictionaries
: a way to store key-value pairs.List of Dictionaries
: a way to store a list of dictionaries.Nested Loops
: a way to have a loop inside another loop.
Week 2 notes
- The hability of doing something multiple times
print("meow")
print("meow")
print("meow")
- Is a way to repeat a block of code while a condition is true
i = 3
while i != 0:
print("meow")
i = i - 1
i = 0
while i < 3:
print("meow")
i = i + 1
- Is a way to repeat a block of code a number of times
for i in [0, 1, 2]:
print("meow")
for i in range(3):
print("meow")
- The _ is a convention to indicate that the variable is not going to be used or is not important
for _ in range(3):
print("meow")
print("meow\n" * 3, end="")
- Is a way to ensure that the input is correct
while True:
n = int(input("What's n? "))
if n > 0:
break
for _ in range(n):
print("meow")
def main():
number = get_number()
meow(number)
def get_number():
while True:
n = int(input("What's n? "))
if n > 0:
return n
def meow(n):
for _ in range(n):
print("meow")
main()
- Is a way to iterate over a list
students = "[Hermione, Harry, Ron]"
for student in students:
print(student)
- Is a way to get the length of a list
students = ["Hermione", "Harry", "Ron"]
for i in range(len(students)):
print(i + 1, students[i])
- Is a way to store key-value pairs
students = {
"Hermione": "Gryffindor",
"Harry": "Gryffindor",
"Ron": "Gryffindor",
"Draco": "Slytherin",
}
print(students["Hermione"])
print(students["Harry"])
print(students["Ron"])
print(students["Draco"])
students = {
"Hermione": "Gryffindor",
"Harry": "Gryffindor",
"Ron": "Gryffindor",
"Draco": "Slytherin",
}
for student in students:
print(student, students[student], sep=", ")
students = [
{"name": "Hermione", "house": "Gryffindor", "patronus": "Otter"},
{"name": "Harry", "house": "Gryffindor", "patronus": "Stag"},
{"name": "Ron", "house": "Gryffindor", "patronus": "Jack Russell terrier"},
{"name": "Draco", "house": "Slytherin", "patronus": None},
]
for student in students:
print(student["name"], student["house"], student["patronus"], sep=", ")
- Is a way to have a loop inside another loop
def main():
print_square(3)
def print_square(size):
# For each row in square
for _ in range(size):
# For each brick in row
for _ in range(size):
# Print brick
print("#", end="")
print()
main()
def main():
print_square(3)
def print_square(size):
for i in range(size):
for j in range(size):
print("#", end="")
print()
main()
def main():
print_square(3)
def print_square(size):
for _ in range(size):
print("#" * size)
main()
def main():
print_square(3)
def print_square(size):
for _ in range(size):
print_row(size)
def print_row(width):
print("#" * width)
main()
###
###
###
Exceptions
: a way to handle errors in the code.Syntax Error
: a mistake in the code that the python interpreter can not execute.ValueError
: an error that happens when you try to convert a string to a number, but the string is not a number.try, except
: a way to handle exceptions.NameError
: an error that happens when you try to use a variable that is not defined.else
: a way to run a code if the try block does not raise an exception.Reprompting, break
: a way to reprompt the user if the input is not valid.get_int
: a way to create a function to get an integer from the user.pass
: a way to do nothing.function arguments
: a way to pass arguments to a function.raise
: a way to raise an exception.
Week 3 notes
That means that something went wrong in the program. It is an error that happens during the execution of the program.
This is just and introductory example. The error is in the syntax of the code. It is a mistake in the code that the python interpreter can not execute.
print("Hello World)
This error is a error that you need to fix, you can't create a code to handle this error.
This error happens when you try to convert a string to a number, but the string is not a number.
x = int(input("What's x? "))
print(f"x is {x}")
This programs works fine if you input a number, but if you input a string, it will raise a ValueError.
You can handle exceptions with the try and except blocks.
try:
x = int(input("What's x? "))
print(f"x is {x}")
except ValueError:
print("x is not an integer")
This error happens when you try to use a variable that is not defined.
try:
x = int(input("What's x? "))
except ValueError:
print("x is not an integer")
print(f"x is {x}")
You can use the else block to run a code if the try block does not raise an exception.
try:
x = int(input("What's x? "))
except ValueError:
print("x is not an integer")
else:
print(f"x is {x}")
You can use a while loop to reprompt the user if the input is not valid.
while True:
try:
x = int(input("What's x? "))
except ValueError:
print("x is not an integer")
else:
break
print(f"x is {x}")
You can create a function to get an integer from the user.
def main():
x = get_int()
print(f"x is {x}")
def get_int():
while True:
try:
return x = int(input("What's x? "))
except ValueError:
print("x is not an integer")
main()
You can use the pass statement to do nothing.
def main():
x = get_int()
print(f"x is {x}")
def get_int():
while True:
try:
return x = int(input("What's x? "))
except ValueError:
pass
main()
Note: In this case I wouln't use the pass argument, I think as a user would be weird to just be asked again and again for the same input.
This is a refined version of the get_int function.
def main():
x = get_int("What's x? ")
print(f"x is {x}")
def get_int(prompt):
while True:
try:
return x = int(input(prompt))
except ValueError:
pass
main()
This was just a quick mention of the raise statement.
Libraries
: a piece of code that someone else wrote that you can use in your code.Modules
: is a library that has one or more functions built into it.import
: to use a module, you have to import it.from
: is the keyword to import a specific function from a module.statistics
: is a built-in module in Python. It has a set of functions to calculate statistics.Command-line Arguments, sys
: sys module, provides access to some variables used or maintained by the interpreter and to functions that interact strongly with the interpreter.Packages, PyPI, pip
: Package: is a collection of modules. PyPI: Python Package Index, is a repository of software for the Python programming language. pip: is a package installer for Python.Custom Libraries
: create your own library.
Week 4 notes
A piece of code that someone else wrote that you can use in your code.
Is a library that has one or more functions built into it.
Re-usability code.
random is a built-in module in Python. It has a set of functions to generate random numbers.
To use a module, you have to import it.
import random
coin = random.choice(["head", "tails"])
print(coin)
Is the keyword to import a specific function from a module.
from random import choice
coin = choice(["head", "tails"])
print(coin)
import random
number = random.randint(1, 10)
print(number)
import random
cards = ["jack", "queen", "king"]
random.shuffle(cards)
for card in cards:
print(card)
statistics is a built-in module in Python. It has a set of functions to calculate statistics.
import statistics
print(statistics.mean([100, 90]))
sys module, provides access to some variables used or maintained by the interpreter and to functions that interact strongly with the interpreter.
- sys.argv: argument vector, a list of strings representing the arguments.
import sys
if len(sys.argv) < 2:
print("Too few arguments")
elif len(sys.argv) > 2:
print("Too many arguments")
else:
print("hello, my name is", sys.argv[1])
Exit from Python.
import sys
if len(sys.argv) < 2:
sys.exit("Too few arguments")
elif len(sys.argv) > 2:
print("Too many arguments")
print("hello, my name is", sys.argv[1])
A slice is a portion of a list.
import sys
if len(sys.argv) < 2:
sys.exit("Too few arguments")
for arg in sys.argv[1:]:
print("hello, my name is", arg)
- Package: is a collection of modules.
- PyPI: Python Package Index, is a repository of software for the Python programming language.
- pip: is a package installer for Python.
pip install cowsay
import cowsay
import sys
if len(sys.arg) == 2:
cowsay.cow("hello, " + sys.argv[1])
- API: Application Programming Interface.
- requests: is a simple HTTP library for Python.
- JSON: JavaScript Object Notation, a language-independent data format.
import json
import requests
import sys
if len(sys.argv) != 2:
sys.exit()
response = requests.get("https://itunes.apple.com/search?entity=song&limit=1&term=" + sys.argv[1])
print(json.dumps(response.json(), indent=2))
import json
import requests
import sys
if len(sys.argv) != 2:
sys.exit()
response = requests.get("https://itunes.apple.com/search?entity=song&limit=50&term=" + sys.argv[1])
o = response.json()
for result in o["results"]:
print(result["trackName"])
Create your own library.
def main():
hello("world")
goodbye("world")
def hello(name):
print(f"hello, {name}")
def goodbye(name):
print(f"goodbye, {name}")
if __name__ == "__main__":
main()
import sys
from sayings import hello
if len(sys.argv) == 2:
hello(sys.argv[1])
Unit Tests
: is a way to test the code you wrote.assert
: is a way to test the code you wrote.AssertionError
: is a way to handle an AssertionError.pytest
: is a testing framework that makes it easy to write small tests.Side Effects and Testing
: is a way to test the code you wrote.Collection of Tests
: is a way to group tests together.
Week 5 notes
Is a way to test the code you wrote.
def main():
x = int(input("What's x? "))
print("x squared is", square(x))
def square(n):
return n * n
if __name__ == "__main__":
main()
from calculator import square
def main():
test_square()
def test_square():
if square(2) != 4:
print("2 squared is not 4")
if square(3) != 9:
print("3 squared is not 9")
if __name__ == "__main__":
main()
testing this code
def main():
test_square()
def test_square():
assert square(2) == 4
assert square(3) == 9
if __name__ == "__main__":
main()
How to handle an AssertionError
testing this code
def main():
test_square()
def test_square():
try:
assert square(2) == 4
except AssertionError:
print("2 squared is not 4")
try:
assert square(3) == 9
except AssertionError:
print("3 squared is not 9")
if __name__ == "__main__":
main()
testing this code
def main():
test_square()
def test_square():
try:
assert square(2) == 4
except AssertionError:
print("2 squared is not 4")
try:
assert square(3) == 9
except AssertionError:
print("3 squared is not 9")
try:
assert square(-3) == 9
except AssertionError:
print("-3 squared is not 9")
try:
assert square(0) == 0
except AssertionError:
print("0 squared is not 0")
if __name__ == "__main__":
main()
$ pip install pytest
testing this code
from calculator import square
def test_square():
assert square(2) == 4
assert square(3) == 9
assert square(-2) == 4
assert square(-3) == 9
assert square(0) == 0
And then run the test with pytest test_calculator.py
$ pytest test_calculator.py
testing this code
from calculator import square
def test_positive():
assert square(2) == 4
assert square(3) == 9
def test_negative():
assert square(-2) == 4
assert square(-3) == 9
def test_zero():
assert square(0) == 0
testing this code
import pytest
from calculator import square
def test_positive():
assert square(2) == 4
assert square(3) == 9
def test_negative():
assert square(-2) == 4
assert square(-3) == 9
def test_zero():
assert square(0) == 0
def test_str():
with pytest.raises(TypeError):
square("cat")
def main():
name = input("What's your name? ")
hello(name)
def hello(to="world"):
print(f"hello, ", to)
if __name__ == "__main__":
main()
testing this code
from hello import hello
def test_arguement():
assert hello("David") == "hello, David"
-
The test will fail because the function
hello
prints the message instead of returning it. -
Printing the message is a side effect.
-
Is a good practice to avoid side effects in functions as much as possible so the function can be tested.
def main():
name = input("What's your name? ")
print(hello(name))
def hello(to="world"):
return f"hello, {to}"
if __name__ == "__main__":
main()
- This is a better version because the assert tests are meant to check the return value of the function and not the side effects.
testing this code
from hello import hello
def test_default():
assert hello() == "hello, world"
def test_arguement():
for name in ["Hermione", "Harry", "Ron"]:
assert hello(name) == f"hello, {name}"
With a structure like this:
project/
hello.py
test/
__init__.py
test_hello.py
from hello import hello
def test_default():
assert hello() == "hello, world"
def test_arguement():
assert hello("David") == "hello, David"
Even if it's empty, __init__.py
have the effect of telling Python that the directory is not just a module, but a package.
$ pytest test
File I/O
: is a way to read and write data to and from files.lists
: the data is stored in a list but the moment the program is closed, the data is lost.open
: is used to open a file.with
: is used to open a file and automatically close it when the block is done.Reading from a file
: to read from a file, you can use theread
method to read the entire file, or thereadline
method to read one line at a time.sorted
: is used to sort a list.Comma-Separated Values (CSV)
: is a file format used to store tabular data.sort keys
: is used to sort a list of dictionaries by a key.Lambda Functions
: is a way to create small anonymous functions.CSV Library
: is a library that can be used to read and write CSV files.Images, PIL Library
: is a library that can be used to read and write images.
Week 6 notes
File I/O (input/output) is a way to read and write data to and from files. This is useful for saving data to a file, or reading data from a file.
Usually the data is stored in a list but the moment the program is closed, the data is lost. This is where file I/O comes in. It allows you to save the data to a file, and read it back in when the program is run again.
names = []
for _ in range(3):
names.append(input("What's your name? "))
for name in sorted(names):
print(f"hello, {name}")
The open
function is used to open a file. It takes two arguments, the name of the file, and the mode to open the file in. The mode can be r
for read, w
for write, or a
for append.
name = input("What's your name? ")
file = open("names.txt", "w")
file.write(name)
file.close()
name = input("What's your name? ")
file = open("names.txt", "a")
file.write(f"{name}\n")
file.close()
The with
statement is used to open a file and automatically close it when the block is done. This is useful because it ensures that the file is closed, even if an error occurs.
name = input("What's your name? ")
with open("names.txt", "a") as file:
file.write(f"{name}\n")
To read from a file, you can use the read
method to read the entire file, or the readline
method to read one line at a time.
The value returned by the read
method is a string, while the value returned by the readline
method is a list of strings.
with open("names.txt", "r") as file:
for line in file:
print("hello,", line.rstrip())
The sorted
function is used to sort a list. It takes a list as an argument, and returns a new list with the elements sorted.
names - []
'''
When you want to open a file in read mode
you don't need to specify the mode because
it is the default mode.
'''
with open ("names.txt") as file:
for line in file:
names.append(line.rstrip())
for name in sorted(names):
print(f"hello, {name}")
with open("names.txt", "r") as file:
for line in sorted(file):
print("hello,", line.rstrip())
Comma-Separated Values (CSV) is a file format used to store tabular data. Each line in the file represents a row in the table, and the values are separated by commas.
The csv
module in Python can be used to read and write CSV files.
Hermione,Gryffindor
Harry,Gryffindor
Ron,Gryffindor
Draco,Slytherin
with open(sudents.csv) as file:
for line in file:
row = line.rstrip().split(",")
print(f"{row[0]} is in {row[1]}")
students = []
with open(sudents.csv) as file:
for line in file:
name, house = line.rstrip().split(",")
student = {"name": name, "house": house}
students.append(student)
for student in students:
print(f"{student['name']} is in {student['house']}")
The sorted
function can take a key
argument to specify a function to use to extract a comparison key from each element in the list.
students = []
with open("students.csv") as file:
for line in file:
name, house = line.rstrip().split(",")
student = {"name": name, "house": house}
students.append(student)
def get_name(student):
return student["name"]
for student in sorted(students, key=get_name):
print(f"{student['name']} is in {student['house']}")
Lambda functions are small anonymous functions that can have any number of arguments, but only one expression.
students = []
with open("students.csv") as file:
for line in file:
name, house = line.rstrip().split(",")
student = {"name": name, "house": house}
students.append(student)
for student in sorted(students, key=lambda student: student["name"]):
print(f"{student['name']} is in {student['house']}")
Harry,Number Four, Privet Drive
Ron,The Burrow
Draco,Malfoy Manor
The csv
module in Python can be used to read and write CSV files. It provides a DictReader
class to read CSV files as dictionaries, and a DictWriter
class to write dictionaries to CSV files.
import csv
students = []
with open("students.csv") as file:
reader = csv.reader(file)
for name, home in reader:
students.append({"name": name, "home": home})
for student in sorted(students, key=lambda student: student["name"]):
print(f"{student['name']} is from {student['home']}")
name,home
Harry,Number Four, Privet Drive
Ron,The Burrow
Draco,Malfoy Manor
import csv
students = []
with open("students.csv") as file:
reader = csv.DictReader(file)
for row in reader:
sudents.append({"name": row["name"], "home": row["home"]})
for student in sorted(students, key=lambda student: student["name"]):
print(f"{student['name']} is from {student['home']}")
name,home
import csv
name = input("What's your name? ")
home = input("Where's your home? ")
with open("students.csv", "a") as file:
writer = csv.writer(file)
writer.writerow([name, home])
import csv
name = input("What's your name? ")
home = input("Where's your home? ")
with open("students.csv", "a") as file:
writer = csv.DictWriter(file, fieldnames=["name", "home"])
writer.writeheader() # This didn't appear in the video but is necessary to write the header
writer.writerow({"name": name, "home": home})
The Python Imaging Library (PIL) adds image processing capabilities to your Python interpreter. This library supports many file formats, and provides powerful image processing and graphics capabilities.
import sys
from PIL import Image
images = []
for arg in sys.argv[1:]:
image = Image.open(arg)
images.append(image)
images[0].save(
"costumes.gif", save_all=True, append_images=[images[1]], duration=200, loop=0
)
Regular Expressions
: a pattern that we can use to match strings.Validation with Regular Expressions
: a way to validate strings using regular expressions.re Library
: a library that allows us to define a pattern that we can use to match strings.Regular Expressions Patterns
: a set of symbols that can be used to match strings.
Week 7 notes
Know as a regex is just a pattern that we can use to match strings. It is a powerful tool that allows us to search for strings that match a certain pattern.
email = input("Whats your email? ").strip()
username, domain = email.split("@")
if username and domain.ends with(".edu"):
print("Valid")
else:
print("Invalid")
This library let us define a pattern that we can use to match strings.
- re.search(pattern, string, flags=0)
import re
email = input("Whats your email? ").strip()
if re.search("@", email):
print("Valid")
else:
print("Invalid")
Symbol | Description |
---|---|
. |
Matches any character except a newline. |
^ |
Matches the start of the string. |
$ |
Matches the end of the string. |
* |
Matches 0 or more repetitions of the preceding pattern. |
+ |
Matches 1 or more repetitions of the preceding pattern. |
? |
Matches 0 or 1 repetition of the preceding pattern. |
{n} |
Matches exactly n repetitions of the preceding pattern. |
{n,} |
Matches n or more repetitions of the preceding pattern. |
{n,m} |
Matches between n and m repetitions of the preceding pattern. |
[] |
Matches any one of the characters inside the brackets. |
[^] |
Matches any character not inside the brackets. |
` | ` |
() |
Groups patterns and captures the matched text. |
(?:...) |
Groups patterns without capturing the matched text. |
\ |
Escapes special characters or denotes a special sequence. |
\d |
Matches any digit; equivalent to [0-9] . |
\D |
Matches any non-digit; equivalent to [^0-9] . |
\w |
Matches any word character (alphanumeric plus underscore); equivalent to [a-zA-Z0-9_] . |
\W |
Matches any non-word character; equivalent to [^a-zA-Z0-9_] . |
\s |
Matches any whitespace character (spaces, tabs, newlines). |
\S |
Matches any non-whitespace character. |
\b |
Matches a word boundary. |
\B |
Matches a non-word boundary. |
import re
email = input("Whats your email? ").strip()
if re.search(r".+@.+\.edu", email):
print("Valid")
else:
print("Invalid")
^
Matches the start of the string.$
Matches the end of the string.
import re
email = input("Whats your email? ").strip()
if re.search(r"^.+@.+\.edu$", email):
print("Valid")
else:
print("Invalid")
[]
Matches any one of the characters inside the brackets.[^]
Matches any character not inside the brackets.
import re
email = input("Whats your email? ").strip()
if re.search(r"^[^@]+@[^@]+\.edu$", email):
print("Valid")
else:
print("Invalid")
import re
email = input("Whats your email? ").strip()
if re.search(r"^[a-zA-Z0-9_]+@[a-zA-Z0-9_]+\.edu$", email):
print("Valid")
else:
print("Invalid")
\d
Matches any digit; equivalent to[0-9]
.\D
Matches any non-digit; equivalent to[^0-9]
./s
Matches any whitespace character (spaces, tabs, newlines).\S
Matches any non-whitespace character.\w
Matches any word character (alphanumeric plus underscore); equivalent to[a-zA-Z0-9_]
.\W
Matches any non-word character; equivalent to[^a-zA-Z0-9_]
.
import re
email = input("Whats your email? ").strip()
if re.search(r"^\w+@\w+\.edu$", email):
print("Valid")
else:
print("Invalid")
re.IGNORECASE
Makes the pattern case-insensitive.re.MULTILINE
Makes the pattern match the start and end of each line.re.DOTALL
Makes the.
character match any character, including newlines.
import re
email = input("Whats your email? ").strip()
if re.search(r"^\w+@\w+\.edu$", email, re.IGNORECASE):
print("Valid")
else:
print("Invalid")
A|B
Matches either the pattern before or the pattern after the pipe.()
Groups patterns and captures the matched text.(?:...)
Groups patterns without capturing the matched text.
import re
email = input("Whats your email? ").strip()
if re.search(r"^(\w|\.)+@(\w+\.)?\w+\.edu$", email, re.IGNORECASE):
print("Valid")
else:
print("Invalid")
This is the regular expression pattern that the browsers uses to validate email addresses.
^[a-zA-Z0-9.!#$%&'*+\/=?^_`{|}~-]+@[a-zA-Z0-9](?:[a-zA-Z0-9-]{0,61}[a-zA-Z0-9])?(?:\.[a-zA-Z0-9](?:[a-zA-Z0-9-]{0,61}[a-zA-Z0-9])?)*$
re.match(pattern, string, flags=0)
Matches the pattern at the start of the string.re.fullmatch(pattern, string, flags=0)
Matches the pattern against the whole string.
- When we use
()
we are creating a capturing group that allows us to extract the matched text. :=
is the walrus operator that allows us to assign a value to a variable and use it in the same line.
import re
name = input("Whats your name? ").strip()
if matches := re.search(r"^(.+), *(.+)$", name):
name = matches.group(2) + " " + matches.group(1)
print(f"Hello, {name}")
.re.sub(pattern, repl, string, count=0, flags=0)
Replaces the matched text with the replacement text.
import re
url = input("URL: ").strip()
username = re.sub(r"^(https?://)?(www\.)?twitter\.com/", "", url)
print(f"Username: {username}")
re.search(pattern, string, flags=0)
Searches for the pattern in the string.
import re
url = input("URL: ").strip()
if matches := re.search(r"^https?://?(?:www\.)?twitter\.com/([a-z0-9_]+)", url, re.IGNORECASE)
print(f"Username: {matches.group(1)}")
There are other other functions
re.split(pattern, string, maxsplit=0, flags=0)
Splits the string at the matches of the pattern.re.findall(pattern, string, flags=0)
Finds all the matches of the pattern in the string.
Object-Oriented Programming (OOP)
: is a programming paradigm that uses "objects" to design applications and computer programs.Tuples
: is a collection of objects that are ordered and immutable.Dictionaries
: is a little bit more powerful than a list. It is a collection of key-value pairs.Classes and Objects
: a class is a blueprint or a mold for creating objects.Instance Methods
: are functions that are defined inside a class and are used to perform operations on objects of that class.Types and Classes
: a type is a category of values, and a class is a blueprint for creating objects.Class methods
: are methods that are bound to the class and not the object of the class.Static methods
: are methods that are bound to the class and not the object of the class.Inheritance
: is a way to create a new class that is based on an existing class.Operator Overloading
: is a way to define how an operator behaves when used with a class.
Week 8 notes
Object-Oriented Programming (OOP) is a programming paradigm that uses "objects" to design applications and computer programs. It utilizes several techniques from previously established paradigms, including modularity, polymorphism, and encapsulation. OOP is based on the concept of "objects," which can contain data in the form of fields, often known as attributes, and code in the form of procedures, often known as methods.
A tuple is a collection of objects that are ordered and immutable. Tuples are sequences, just like lists. The differences between tuples and lists are, the tuples cannot be changed unlike lists and tuples use parentheses, whereas lists use square brackets.
def main():
student = get_student()
print(f"{name} from {house}")
def get_student():
name = input("Name: ")
house = input("House: ")
return name, house
if __name__ == "__main__":
main()
A dictionary is a little bit more powerful than a list. It is a collection of key-value pairs. Dictionaries are used to store data values in key:value pairs.
def main():
student = get_student()
if student["name"] == "Padma":
student["house"] = "Ravenclaw"
print(f"{student['name']} from {student['house']}")
def get_student():
return student = {
"name": input("Name: "),
"house": input("House: ")
}
if __name__ == "__main__":
main()
-
A
class
is a blueprint or a mold for creating objects. -
A
class
is a user-defined blueprint or prototype from which objects are created. -
An
object
is an instance of a class that has attributes and methods.
class Student:
...
def main():
student = get_student()
print(f"{student.name} from {student.house}")
def get_student():
student = Student()
student.name = input("Name: ")
student.house = input("House: ")
return student
if __name__ == "__main__":
main()
Method | Description |
---|---|
__init__() |
Constructor method, called when a new instance is created. |
__del__() |
Destructor method, called when an instance is about to be destroyed. |
__repr__() |
Returns an official string representation of the instance, useful for debugging. |
__str__() |
Returns a readable string representation, used by print() . |
__call__() |
Allows an instance to be called as a function. |
__eq__() |
Defines behavior for the equality operator == . |
__ne__() |
Defines behavior for the inequality operator != . |
__lt__() |
Defines behavior for the less-than operator < . |
__le__() |
Defines behavior for the less-than-or-equal-to operator <= . |
__gt__() |
Defines behavior for the greater-than operator > . |
__ge__() |
Defines behavior for the greater-than-or-equal-to operator >= . |
__len__() |
Defines behavior for the built-in len() function. |
__getitem__() |
Defines behavior for indexing obj[key] . |
__setitem__() |
Defines behavior for assigning to indexed elements obj[key] = value . |
__delitem__() |
Defines behavior for deleting indexed elements del obj[key] . |
__iter__() |
Defines behavior for the iterator protocol. |
__next__() |
Defines behavior for retrieving the next item from an iterator. |
__contains__() |
Defines behavior for the membership test operators in and not in . |
__enter__() |
Defines behavior for entering a context (used with with statement). |
__exit__() |
Defines behavior for exiting a context (used with with statement), handles exceptions. |
__getattr__() |
Defines behavior for accessing an attribute that does not exist. |
__setattr__() |
Defines behavior for setting an attribute's value. |
__delattr__() |
Defines behavior for deleting an attribute. |
__hash__() |
Defines behavior for the built-in hash() function. |
__eq__() |
Defines behavior for the equality operator == . |
__ne__() |
Defines behavior for the inequality operator != . |
- The
__init__
method is a special method in Python classes that is called when a new instance of the class is created.
class Student:
def __init__(self, name, house):
if not name:
raise ValueError("Missing name")
if house not in ["Gryffindor", "Hufflepuff", "Ravenclaw", "Slytherin"]:
raise ValueError("Invalid house")
self.name = name
self.house = house
def main():
student = get_student()
print(f"{student.name} from {student.house}")
def get_student():
name = input("Name: ")
house = input("House: ")
return Student(name, house) #Constructor call
if __name__ == "__main__":
main()
- The
__str__
method is called by thestr()
built-in function and by theprint()
function to compute the "informal" or nicely printable string representation of an object.
class Student:
def __init__(self, name, house):
if not name:
raise ValueError("Missing name")
if house not in ["Gryffindor", "Hufflepuff", "Ravenclaw", "Slytherin"]:
raise ValueError("Invalid house")
self.name = name
self.house = house
def __str__(self):
return f"{self.name} from {self.house}"
def main():
student = get_student()
print(f"{student.name} from {student.house}")
def get_student():
name = input("Name: ")
house = input("House: ")
return Student(name, house) #Constructor call
if __name__ == "__main__":
main()
class Student:
def __init__(self, name, house, patronus):
if not name:
raise ValueError("Missing name")
if house not in ["Gryffindor", "Hufflepuff", "Ravenclaw", "Slytherin"]:
raise ValueError("Invalid house")
self.name = name
self.house = house
self.patronus = patronus
def __str__(self):
return f"{self.name} from {self.house}"
def charm(self):
match self.patronus:
case "Stag":
return "π΄"
case "Otter":
return "π¦¦"
case "Jack Russell Terrier":
return "πΆ"
case _:
return "π¦"
def main():
student = get_student()
print("Expecto Patronum!")
print(sudent.charm())
def get_student():
name = input("Name: ")
house = input("House: ")
patronus = input("Patronus: ")
return Student(name, house, patronus) #Constructor call
if __name__ == "__main__":
main()
Properties
are a special kind of attribute that can be accessed like an attribute, but are computed like a method.@property
is a decorator that allows you to define a method that can be accessed like an attribute.Getters
are methods that allow you to access the value of a property.Setters
are methods that allow you to change the value of a property.
class Student:
def __init__(self, name, house):
if not name:
raise ValueError("Missing name")
self.name = name
self.house = house
def __str__(self):
return f"{self.name} from {self.house}"
# Getter
'''
This is the getter method. This is the "function"
that will be called when you try to get the value of the property.
like this: print(student.house)
'''
@property
def house(self):
return self._house
# Setter
'''
This is the setter method. This is the "function"
that will be called when you try to set the value of the property.
like this: student.house = "Gryffindor"
'''
@house.setter
def house(self, house):
if house not in ["Gryffindor", "Hufflepuff", "Ravenclaw", "Slytherin"]:
raise ValueError("Invalid house")
self._house = house
def main():
student = get_student()
student.house = "Number Four, Privet Drive"
print(student)
def get_student():
name = input("Name: ")
house = input("House: ")
return Student(name, house) #Constructor call
if __name__ == "__main__":
main()
- All this time
int
was a class:class int(x, base=10)
. - The str has been a class all along:
class str(object='')
.- All the time we used
str.lower()
we have been taking an object of class str and calling the method lower on it. str.strip([chars])
is the same thing, a method of the str class.
- All the time we used
list
is a class, everytime we created a list we were creating an object of the class list:class list([iterable])
.list.append(x)
is a method of the list class.
dict
is a class, everytime we created a dictionary we were creating an object of the class dict:class dict(**kwarg)
.
print(type(50))
print(type("Hello, World!"))
print(type([]))
print(type({}))
Output:
<class 'int'>
<class 'str'>
<class 'list'>
<class 'dict'>
Sometimes is not really necessary to associate a function with object of a class but rather with the class itself.
Sometimes you want that the class have certain behaivor or certain functionality that is not related to the object itself but rather to the class. That means that no matter how is the object created, the class will have that functionality.
For that he got another decorator called @classmethod
, we use this decorator to define a method that is associated with the class rather than the object.
In this example we're gonna implement a sorting hat that will assign a house to a student.
import random
class Hat:
def __init__(self):
self.houses = ["Gryffindor", "Hufflepuff", "Ravenclaw", "Slytherin"]
def sort(self, name):
print(name, "is in", random.choice(self.houses))
hat = Hat()
hat.sort("Harry")
The code of above works but it's not really necessary to create an object of the class Hat to sort a student.
And it has a feature/bug, you can create more than one hat, and that's not a functionality that we want. We could do something like this:
hat1 = Hat()
hat2 = Hat()
hat3 = Hat()
In the world of harry potter the sorting hat is not an object that you can create, it's just one and only one hat that you put on the head of the student and it will sort the student.
We have been using instance methods
: writing functions inside of classes that are automatically passed a reference to self, the current object, but sometimes you don't need that, sometimes it suffices to just know what the class is and assume that might not even be an object of that class.
So in this case we can use a class really as a container for data and/or functions that is somehow conceptually related to the class but not necessarily to the object. Here is where @classmethod
comes in.
import random
class Hat:
# This is a list of houses that is shared by all the instances of the class
houses = ["Gryffindor", "Hufflepuff", "Ravenclaw", "Slytherin"]
@classmethod
def sort(cls, name): # We dont reference self, we reference cls now which is the class itself
print(name, "is in", random.choice(cls.houses))
Hat.sort("Harry")
class Student:
def __init__(self, name, house):
if not name:
raise ValueError("Missing name")
self.name = name
self.house = house
def __str__(self):
return f"{self.name} from {self.house}"
# Getter
'''
This is the getter method. This is the "function"
that will be called when you try to get the value of the property.
like this: print(student.house)
'''
@property
def house(self):
return self._house
# Setter
'''
This is the setter method. This is the "function"
that will be called when you try to set the value of the property.
like this: student.house = "Gryffindor"
'''
@house.setter
def house(self, house):
if house not in ["Gryffindor", "Hufflepuff", "Ravenclaw", "Slytherin"]:
raise ValueError("Invalid house")
self._house = house
def main():
student = get_student()
student.house = "Number Four, Privet Drive"
print(student)
def get_student():
name = input("Name: ")
house = input("House: ")
return Student(name, house) #Constructor call
if __name__ == "__main__":
main()
class Student:
def __init__(self, name, house):
self.name = name
self.house = house
def __str__(self):
return f"{self.name} from {self.house}"
def main():
student = get_student()
student.house = "Number Four, Privet Drive"
def get_student():
name = input("Name: ")
house = input("House: ")
return Student(name, house) #Constructor call
if __name__ == "__main__":
main()
The original code wasn't bad, but might be a little weird in the long run to have a function related to the class as a separate function outside of the class such as get_student()
.
So what we can do is to move that function inside of the class and make it a class method.
class Student:
def __init__(self, name, house):
self.name = name
self.house = house
def __str__(self):
return f"{self.name} from {self.house}"
@classmethod
def get(cls):
name = input("Name: ")
house = input("House: ")
return cls(name, hosue)
def main():
student = Student.get()
print(student)
if __name__ == "__main__":
main()
@staticmethod
is a decorator that allows you to define a method that does not operate on the instance of the class or the class itself.
Anyway this is a rabbit hole that the course will not go down, but it's good to know that it exists.
Turns out, via OOP (Object Oriented Programming), there's an opportunity to design your classes in a heirarchical way , whereby you can have one class "inherit" from or borrow attributes that is metods or variables from another class if they are all have those in common.
class Wizard:
def __init__(self, name):
if not name:
raise ValueError("Missing name")
self.name = name
...
class Student(Wizard):
def __init__(self, name, house):
super().__init__(name) # Super have the habilitiy to call the constructor of the parent class
self.house = house
...
class Professor(Wizard):
def __init__(self, name, subject):
super().__init__(name)
self.subject = subject
...
wizard = Wizard("Albus")
student = Student("Harry", "Gryffindor")
professor = Professor("Severus", "Defense Against the Dark Arts")
BaseException
+-- KeyboardInterrupt
+-- Exception
+-- ArithmeticError
| +-- ZeroDivisionError
+-- AssertionError
+-- AttributeError
+-- EOFError
+-- ImportError
| +-- ModuleNotFoundError
+-- LookupError
| +-- KeyError
+-- NameError
+-- SyntaxError
| +-- IndentationError
+-- ValueError
Operator overloading is a specific case of polymorphism, where different operators have different implementations depending on their arguments.
In other words, a +
doesn't always mean addition, it can mean concatenation if the operands are strings for example.
__add__
is a special method that is called when the+
operator is used.
class Vault:
def __init__(self, galleons=0, sickles=0, knuts=0):
self.galleons = galleons
self.sickles = sickles
self.knuts = knuts
def __str__(self):
return f"{self.galleons} galleons, {self.sickles} sickles, {self.knuts} knuts"
def __add__(self, other):
galleons = self.galleons + other.galleons
sickles = self.sickles + other.sickles
knuts = self.knuts + other.knuts
return Vault(galleons, sickles, knuts)
potter = Vault(100, 50, 25)
print(potter)
weasley = Vault(25, 50, 100)
print(weasley)
total = Vault(galleons, sickles, knuts)
print(total)
set
: is a collection of unique elements. It is an unordered collection of items.global
: is used to declare that a variable inside the function is global (outside the function).constants
: is a type of variable whose value cannot be changed.Type Hints, mypy
: is a static type checker for Python.Docstrings
: is a string that appears right after the definition of a function, class, or module.argparse
: is a module in the Python standard library that allows you to parse command-line arguments.unpacking
: is a way to assign the elements of an iterable to multiple variables.map
: is a function that applies a given function to each item of an iterable (list, tuple, etc.).List Comprehensions
: is a way to create lists in Python.filter
: is a function that filters elements of an iterable based on a given function.Dictionary Comprehensions
: is a way to create dictionaries in Python.enumerate
: is a function that adds a counter to an iterable.Generators, Iterators, yield
: is a way to create iterators in Python.
Week 9 notes
A set is a collection of unique elements. It is an unordered collection of items. Sets are used to store multiple items in a single variable. It is a collection data type in Python.
students = [
{"name": "Hermione", "house": "Gryffindor"},
{"name": "Harry", "house": "Gryffindor"},
{"name": "Ron", "house": "Gryffindor"},
{"name": "Draco", "house": "Slytherin"},
{"name": "Padma", "house": "Ravenclaw"},
]
houses = []
for student in students:
if student["house"] not in houses:
houses.append(student["house"])
for house in sorted(houses):
print(house)
students = [
{"name": "Hermione", "house": "Gryffindor"},
{"name": "Harry", "house": "Gryffindor"},
{"name": "Ron", "house": "Gryffindor"},
{"name": "Draco", "house": "Slytherin"},
{"name": "Padma", "house": "Ravenclaw"},
]
houses = set()
for student in students:
houses.add(student["house"])
for house in sorted(houses):
print(house)
The global keyword is used to declare that a variable inside the function is global (outside the function). If you want to change a global variable inside a function, you can use the global keyword to declare which variables are global.
balance = 0
def main():
print("Balance:", balance)
if __name__ == "__main__":
main()
balance = 0
def main():
print("Balance:", balance)
deposit(100)
withdraw(50)
print("Balance:", balance)
def deposit(n):
balance += n
def withdraw(n):
balance -= n
if __name__ == "__main__":
main()
The result is a UnboundLocalError
because the variable balance
is not defined inside the function deposit
.
Apparently is okey to read a global variable inside a function, but if you want to change it, you need to use the global
keyword.
balance = 0
def main():
print("Balance:", balance)
deposit(100)
withdraw(50)
print("Balance:", balance)
def deposit(n):
global balance
balance += n
def withdraw(n):
global balance
balance -= n
if __name__ == "__main__":
main()
class Account:
def __init__(self):
self._balance = 0
@property
def balance(self):
return self._balance
def deposit(self, n):
self._balance += n
def withdraw(self, n):
self._balance -= n
def main():
account = Account()
print("Balance:", account.balance)
account.deposit(100)
account.withdraw(50)
print("Balance:", account.balance)
if __name__ == "__main__":
main()
Usually using global variables is not a good practice. It is better to use classes and objects.
Note for myself: the instance variable balance
and all the other instance variables by definition can be accessed by all the methods of the class.
A constant is a type of variable whose value cannot be changed.
MEOWS = 3
for _ in range(MEOWS):
print("meow")
There's actually no way to define a constant in Python. The convention is to use uppercase letters for the variable name to indicate that it should be treated as a constant.
class Cat:
MEOWS = 3
def meow(self):
for _ in range(Cat.MEOWS): # or self.MEOWS
print("meow")
cat = Cat()
cat.meow()
mypy
is a static type checker for Python. It is a tool that can be used to check the types of variables and functions in your code.
def meow(n):
for _ in range(n):
print("meow")
number = input("Number: ")
meow(number)
MyPy will detect the error because the function input
returns a string, and the function meow
expects an integer.
def meow(n: int):
for _ in range(n):
print("meow")
number: int = input("Number: ")
meow(number)
The code is still wrong, but now mypy
can detect the error much quicker by adding type hints.
def meow(n: int):
for _ in range(n):
print("meow")
number: int = int(input("Number: "))
meow(number)
def meow(n: int) -> None:
for _ in range(n):
print("meow")
number: int = int(input("Number: "))
meows: str = meow(number)
print(meows)
def meow(n: int) -> str:
return "meow\n" * n
number: int = int(input("Number: "))
meows: str = meow(number)
print(meows, end="")
A docstring is a string that is used to document a function, method, or class. It is used to describe what the function does, what parameters it takes, and what it returns.
There's a standarized way on how to document your functions, methods, and classes in Python, the PEP (Python Enhancement Proposal) is the PEP 257
def meow(n: int) -> str:
"""
Meow n times.
:param n: Number of times to meow.
:type n: int
:raise TypeError: If n is not an int.
:return: A string of n meows, one per line.
:rtype: str
"""
return "meow\n" * n
number: int = int(input("Number: "))
meows: str = meow(number)
print(meows, end="")
The argparse
module is used to parse command-line arguments in Python. It is a standard module that comes with Python.
import argparse
parser = argparse.ArgumentParser(description="Meow like a cat.")
parser.add_argument("-n", default=1, help="number of times to meow", type=int)
args = parser.parse_args()
for _ in range(args.n):
print("meow")
Note: if you specify that the argument is a type int
, you don't need to convert it to an integer.
Unpacking is the process of extracting values from a collection, such as a list or a tuple, and assigning them to variables.
first, _ = input("What's your name? ").split(" ") # Here is the unpacking
print(f"hello, {first}")
We used the foo, bar = "foo bar".split(" ")
to unpack the values of the list returned by the split method.
def total(galleons, sickles, knuts):
return (galleons * 17 + sickles) * 29 + knuts
coins = [100, 50, 25]
print(total(*coins), "Knuts") # Here is the unpacking
We used the *
operator to unpack the values of the list coins
and pass them as arguments to the function total
.
def total(galleons, sickles, knuts):
return (galleons * 17 + sickles) * 29 + knuts
coins = {"galleons": 100, "sickles": 50, "knuts": 25}
print(total(**coins), "Knuts") # Here is the unpacking
We used the **
operator to unpack the values of the dictionary coins
and pass them as arguments to the function total
.
*args
is used to pass a variable number of arguments to a function.**kwargs
is used to pass a variable number of keyword arguments to a function.
def f_positional(*args, **kwargs):
print("Positional:", args)
# The output of this function is: Positional: (100, 50, 25)
f_positional(100, 50, 25)
def f_keyword(*args, **kwargs):
print("Keyword:", kwargs)
# The output of this function is: Keyword: {'galleons': 100, 'sickles': 50, 'knuts': 25}
f_keyword(galleons=100, sickles=50, knuts=25)
The map
function is used to apply a function to each item in a list or other iterable.
map(function, iterable)
def main():
yell("This", "is", "CS50")
def yell(*words):
uppercased = []
for word in words:
uppercased.append(word.upper())
print(*uppercased)
if __name__ == "__main__":
main()
def main():
yell("This", "is", "CS50")
def yell(*words):
"""
We use str.upper instead of something.upper()
because instead of actually applying the method
to the string, we are just passing the method
itself to the map function.
"""
uppercased = map(str.upper, words)
print(*uppercased)
if __name__ == "__main__":
main()
List comprehensions are a concise way to create lists in Python. They are used to create a new list by applying an expression to each item in an existing list.
def main():
yell("This", "is", "CS50")
def yell(*words):
uppercased = [word.upper() for word in words]
print(*uppercased)
if __name__ == "__main__":
main()
The filter
function is used to filter items from a list or other iterable.
filter(function, iterable)
students = [
{"name": "Hermione", "house": "Gryffindor"},
{"name": "Harry", "house": "Gryffindor"},
{"name": "Ron", "house": "Gryffindor"},
{"name": "Draco", "house": "Slytherin"},
{"name": "Padma", "house": "Ravenclaw"},
]
gryffindors = [
student["name"] for student in students if student["house"] == "Gryffindor"
]
for gryffindor in sorted(gryffindors):
print(gryffindor)
In the code above, we used a list comprehension with a conditional to filter the students that belong to the house Gryffindor.
students = [
{"name": "Hermione", "house": "Gryffindor"},
{"name": "Harry", "house": "Gryffindor"},
{"name": "Ron", "house": "Gryffindor"},
{"name": "Draco", "house": "Slytherin"},
{"name": "Padma", "house": "Ravenclaw"},
]
def is_gryffindor(s):
return s["house"] == "Gryffindor"
gryffindors = filter(is_gryffindor, students)
for gryffindor in sorted(gryffindors, key=lambda s: s["name"]):
print(gryffindor["name"])
Dictionary comprehensions are a concise way to create dictionaries in Python. They are used to create a new dictionary by applying an expression to each item in an existing dictionary.
students = ["Hermonie", "Harry", "Ron"]
gryffindors = []
for student in students:
gryffindors.append({"name": student, "house": "Gryffindor"})
print(gryffindors)
students = ["Hermonie", "Harry", "Ron"]
gryffindors = {student: "Gryffindor" for student in students}
print(gryffindors)
The enumerate
function is used to add a counter to an iterable.
enumerate(iterable, start=0)
students = ["Hermonie", "Harry", "Ron"]
for i in range(len(students)):
print(i + 1, students[i])
students = ["Hermonie", "Harry", "Ron"]
for i, student in enumerate(students, start=1):
print(i + 1, student)
A generator is a function that returns an iterator. It generates values using the yield
keyword.
def main():
n = int(input("What's n? "))
for i in range(n):
print(sheep(i))
def sheep(n):
flock = []
for i in range(n):
flock.append("π" * i)
return flock
if __name__ == "__main__":
main()
The problem in the code above is that the function sheep
first generates all the values and then returns them all at once. This is not efficient because it uses a lot of memory.
def main():
n = int(input("What's n? "))
for i in range(n):
print(sheep(i))
def sheep(n):
for i in range(n):
yield "π" * i
if __name__ == "__main__":
main()
The word yield
is used to return a value at the time, that way we don't need to store all the values in memory but just one at a time.
yeild
is returning an iterator, so we can use it in a for
loop.