Python3 Documentation

  • number: Replace with a number.
  • *object: Any number of objects.
  • [, ndigits]: Anything between [] is optional.

Run Python Code

Python is an interpreted language. It is not compiled like C.

python hello.py

Skipping the compiling step makes the language easier to use but at the cost of performance.

Print

Print information on the console.

print("hello, world")
answer = "Some Text"
print(f"Your answer is: {answer}") # formatted string (any type)
print("Your answer is: " + answer) # answer must be string
print("Your answer is:", answer) # answer can be any type
print("Same line", end="")
print("!" * 4) # "!!!!
# '' and "" do the same.

Input

Prompt the user for information.

# String Input
name = input("Prompt: ")
number = int(input("Prompt: "))
name = name.strip() # Remove whitespaces from a string

Variables

Python infers the type of variable automatically.

answer = "Some Text"
counter = 0
counter = counter + 1
counter += 1
# Counter++ does not exist in Python

Global Variables

Use the global keyword to modify a global variable while outside its scope.

balance = 0
def deposit(n):
	global balance
	balance += n

Comments

# Comment
"""
Multi
Line
Comment
"""

Docstring

The documentation for a function.
This can later be used to generate manuals and documentation Some IDEs do so automatically.

def code():
	"""
	Does something.
	"""
	...

Type Hints

Type hints are not enforced. These can be read by debuggers, but Python itself allows these types to be violated.

def meow(n: int) -> None: # -> None indicates that the function does not return a value
	...
number: int = int(input("Number: "))

Data Types

Data Type CategoryData Types
Text Typestr
Numeric Typesint, float, complex
Sequence Typeslist, tuple, range
Mapping Typedict
Set Typesset, frozenset
Boolean Typebool
Binary Typesbytes, bytearray, memoryview
None TypeNoneType

float types can overflow, much like C, but int never does, and instead grows as large as needed to fit a given value.

Type Conversion

int("1") # convert to int
float(2) # convert to float

Data Type Functions

round(5.12) # round a number
round(3.43, 2) # round to the 2nd decimal place
len(list) # length of a list (nº of elements)

Unpack Values

Values can be split using *. This method works with lists, dictionaries, and tuples.
For dictionaries, *dict returns key names and **dict returns the value names.
Unpack can also be used when returning values from functions.

def total(gold, silver, copper):
	return gold * 100 + silver * 10 + copper
coins = [100, 50, 25]
print(total(*coins), "Copper coins")

F Strings

A string that executes formatting.

f'Some text: {1+1}' # Executes 1+1 and prints "Some text: 2"
f'{z:.2f}' # Variable z is rounded to the 2nd decimal place.

Ranges

Produce a range of numbers.

range(10) # 1, 2, 3... 10
if 80 <= score < 90:
	code...

Lists

Arrays are flexible in Python, unlike C. Elements can be added and removed at runtime.

scores = [72, 73, 33]
scores.append(32)
# scores += [43, 45] # Adds a list to another list
average = sum(scores) / len(scores)
print(f"Average: {average}")
scores[1:] \#Slice the list -> From 1 to the end. (Skips element 0)
scores[1:-1] \#Slice the list -> From 1 to the end. (Skips element 0 and last)

List Comprehension

Create a new list with a function applied to every element of the list.

words = ["This", "is", "CS50"]
uppercased = [word.upper() for word in words]
print(*uppercased)

In this example, every element in words is uppercased.
This feature is useful to filter items as well:

students = [
	{"name": "Trude", "class": "code"},
	{"name": "Maxs", "class": "art"},
	{"name:" "JCionx", "class": "code"},
]
codeStudents = [
	student["name"] for student in students if student["class"] == "code"
]

Filter Function

Similar code, but for a more functional oriented programming approach.

def is_class(s):
	return s["class"] == "code"
coders = filter(is_class, students)

Sets

Similar to a list, but can only contain unique items.

values = set(1, 1, 1, 2)
print(values) # 1, 2

Dictionaries

A list, but instead of using an index, uses keywords.

students = {
	"Key1": "Value",
	"Key2": "Value",
	"Key3": "Value",
}
print(students["Key1"])

Loop over Dictionary

for student in students:
	print(student)           # Iterates over keys.
	print(students[student]) # Iterates over values.

Dictionary Comprehension

Filter dictionaries.

students = ["Trude", "Maxs", "JCionx"]
# Classic for
coders = []
for student in students:
	coders.append({"name": student, "class": "code"})
# List comprehension (3 dicts in a list)
coders = [{"name": student, "class": "code"} for student in students]
# Dict comprehension (1 dict with name: class for all 3)
coders = {student: "code" for student in students}

Hashtable

table = [
	{"Key1": "Value", "Key2": "Value", "Key3": "Value"},
	{"Key1": "Value", "Key2": "Value", "Key3": "Value"},
	{"Key1": "Value", "Key2": "Value", "Key3": "Value"},
	{"Key1": "Value", "Key2": "Value"},
]
print(table[0]["Key2"])
for tab in table:
	print(tab["Key1"]) # Print all Key1 values.

Conditions

IF

if x < y:
	print("x is less than y")
elif x > y:
	print("x is greater than y")
else:
	print("x is not less than y")
if test in numbers:
	code...

() are optional in the condition.

:= Operator

Syntax sugar to integrate an assignment operator with an IF statement.
Without it

matches = 1 == 1
if matches:
	print(matches) # True

With it

if matches := 1 == 1:
	print(matches) # True

Loops

While

while True:
	print("meow")
i = 0
while i < 3:
	print("meow")
	i += 1

For

Loop through an Array

for _ in [0, 1, 2]:
	print("Hello World.")
# _ throws away the value (no variable).

Loop through a Range

for i in range(3):
	print("Loops from 0 - 3")

Loop through an Array, Using Its Value as the Index

students = ["one", "two", "three"]
for student in students:
	print(student)

Enumerate

Iterate over a sequence, and get the current value and index.

students = ["Trude", "Maxs", "JCionx"]
for i, student in enumerate(students):
	print(i + 1, student)

Map

Execute a function on every element of a list.
Takes two arguments:

  • Name of a function
  • Data structure to apply the function on
words = ["This", "is", "CS50"]
uppercased = map(str.upper, words)
print(*uppercased)

Keywords

  • break: leave the loop
  • continue: skip to next iteration

Libraries

import lib
lib.component(...)
from lib import component
component(...)

Packages

A package is a collection of libraries in the same directory, often built by other developers.
You can download user packages on pypi.org.

Install Packages with Pip

pip is a package manager for Python.

pip install package

Useful Libraries

  • random Generate random numbers
    • shuffle
    • choice
    • randint
  • statistics
    • mean
  • requests Web requests (similar to curl) useful for interfacing with APIs

APIs

APIs usually communicate in JSON.

import requests, sys
import json
response = requests.get("<https://itunes.apple.com/search?entity=song(...)>")
print(response.json()) # Print respose formatted for json (as a py dictionary))
print(json.dumps(response.json(), indent=2)) # Print response indented to read easily.
o = response.json()
for result in o["results"]:
	print(result["trackName"]) # Print all songs in the response

Create Custom Library

# LIBRARY: lib.py file
def main():
	hello("world")
def hello(name):
	print(f"hello, {name}")
# Only call main if ran from the command line. Not as a library.
if __name__ == "__main__":
	main()
# MAIN CODE: main.py file
from lib import hello
hello("trude")

Functions

def hello(name):
	print(f'Hello {name}')
hello("trude")

Default Values

def hello(to='world'):
	print("hello,", to)
hello() # hello, world
hello('trude') # hello, trude

Main Function

def main():
	# main code ...
# other fucntions ...
main()

Generator Function (Yield)

Return one value at a time, without ending the function, and then loop again.
Useful when working with a large amount of logic that takes a long time to execute.

def sheep(n):
	for i in range(n):
		yield "SHEEP" * i

yield returns an iterator that allows the program to handle one element at a time and not lose track.

Command Line Arguments

from sys import argv
if len(argv) == 2:
	print(f"Hello, {argv[1]}")
else:
	print("Hello World.")
# argv[0] is the program name
# List all arguments
for arg in argv:
	print(arg)

Flags

To support commands like: python file.py -n 2

import argparse
parser = argparse.ArgumentParser(description="Meow like a cat")
parser.add_argument("-n", default=1, type=int, help="number of times to meow")
args = parser.parse_args()
for _ in range(args.n): # .n is the -n flag
	print("meow")

Allow Function to Take Any Number of Arguments (Unpack)

def f(*args, **kwargs):
	print("Positional: ", args)
	print("Named: ", kwargs)
f(100, 50, 25) # Positional: (100, 50, 25)
f(100, 50, 25, 5) # Positional: (100, 50, 25, 5)
f(gold=10, silver=20, copper=1) # Named: {'gold': 10, 'silver'20 ...}

Pass Functions as Arguments

def get_name(student):
	return student["name"] # Sort by student name
for student in sorted(students, key=get_name):
	...

Lambda Function

Pass a function without having to give it a name.

for student in sorted(students, key=lambda student: student["name"]):
	...

Exceptions

# Int
try: # Try to execute
	x = int(input("What's your number?"))
except ValueError: # If error is ValueError
	print("Not a number.")
except: # If error
	print("Unknown Error.")
else: # If success
	print(f"Your number+1 is {x + 1}")
try:
	...
except:
	pass # Catch the error, but then ignore it.

Operators

Comparison Operators

  • <
  • >
  • ==

Logic Operators

  • and
  • or
s = input("Do you agree?")
if s == 'y' or s == 'Y' or s == "Yes" or s == "yes":
	(...)
# This can be improved:
s = input("Do you agree?").lower()
if s in ['y', 'yes']:
	(...)

Scope

  1. Program
  2. Function (Variables in loops and IFs are global to the function)

Exit with Error Codes

from sys import exit
exit(1) # Exit with error code 1
exit("Failed for some reason")

Unit Tests

Helper code to test the main code and (hopefully) catch bugs before they can reach production.

Manual Tests

Main code

def main():
	x = int(input("X value: "))
	print(f"X^2 is {square(x)}")
def square(n):
	return(n * n)
if __name__ = "__main__":
	main()

Test code (Using IF)

from file import square
def main():
	test_square()
def test_square():
	if square(2) != 4:
		print("2 squared was not 4.")
	if square(3) != 9:
		print("3 squared was not 9.")
if __name__ == "__main__":
	main()

Improved test code (Using assert)

from file import square
def main():
	test_square()
def test_square():
	try:
		assert square(2) == 4
	except AssertionError:
		print("2 squared was not 4.")
	try:
		assert square(3) == 9
	except AssertionError:
		print("3 squared was not 9.")
if __name__ == "__main__":
	main()

Pytest

pytest is an external program that simplifies python’s unit testing.

import pytest
from file 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
def test_str(): # Verify that an error will happen for a str
	with pytest.raises(TypeError):
		square("cat")

Test with pytest:

pytest test_file.py

A function with side effects (doesn’t return a value) can’t be tested.

File I/O

Write to a File (Replace)

name = "trude"
file = open("name.txt", "w")
file.write(name)
file.close()

Append to a File (Add)

name = "trude"
file = open("name.txt", "a")
file.write(name)
file.close()

Open a File and close Automatically

name = "trude"
with open("name.txt", "a") as file:
	file.write(name) \\#File is only open in this scope

Read a File

"r" is optional when reading.

with open("name.txt", "r") as file:
	lines = file.readlines() # Return all lines as a list
for line in lines:
	print(line.rstrip()) # Print all lines and remove \\n separating each line

Better Code for Reading Every line

with open("name.txt") as file:
	for line in file:
		print(line.rstrip())

Sort File Lines

names = []
with open("name.txt") as file:
	for line in file:
		names.append(line.rstrip())
for name in sorted(names):
	print(f"Hello, {name}")

CSV Files

Read

Python

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 students:
	print(f"{student['name]"} is from {student['home']}")

CSV File

trude, myHome
maxs, hisHome
jcionx, anotherHome

Dict Reader

Ignores column order and doesn’t break the program if new columns are added. It is recommended to always use **DictReader** instead of **Reader**.
Python

import csv
students = []
with open("students.csv") as file:
	reader = csv.DictReader(file)
	for row in reader:
		students.append({"name": row["name"], "home": row["home"]})
for student in students:
	print(f"{student['name]"} is from {student['home']}")

CSV File

name, home
trude, myHome
maxs, hisHome
jcionx, anotherHome

Write

import csv
name = 'trude'
home = 'myHome'
with open("students.csv", "a") as file:
	writer = csv.writer(file)
	writer.writerow([name, home])

Dict Writer

import csv
name = 'trude'
home = 'myHome'
with open("students.csv", "a") as file:
	writer = csv.DictWriter(file, fieldnames=["name", "home"])
	writer.writerow({"name": name, "home": home})

Other Formats

  • TXT
  • CSV
  • JSON
  • BINARY
    • Images (PIL library)

Animated GIF Program

from PIL import Image
items = ["image1.png", "image2.png"]
images = []
for item in items:
	image = Image.open(item)
	images.append(image)
images[0].save(
	"images.gif", save_all=True, append_images=[images[1]], duration=200, loop=0
)

Regular Expressions (regex)

Define patterns for comparisons.

import re
email = input("Email Address: ").strip()
if re.search(r"^.+@.+\\.edu$", email, re.IGNORECASE):
	print("Email is valid.")
else:
	print("Email is not valid.")

Regular Expression Symbols

  • . any character except a newline
  • 0 or more repetitions
  • + 1 or more repetitions
  • ? 0 or 1 repetition
  • \\ escape character
  • {m} m repetitions
  • {m,n} m-n repetitions (range)
  • ^ match the start of the string
  • $ match the end of the string
  • [] set of characters
  • [^] complementing the set
  • \\d decimal digit
  • \\D not a decimal digit
  • \\s whitespace characters
  • \\S not a whitespace character
  • \\w word character, numbers and underscore
  • \\W not a word character
  • A|B either A or B
  • (…) a group
  • (?:…) non-capturing version
    Always use r"string" (raw string) so python won’t interpret \\ as a special character.

RE Lib Flags

  • re.IGNORECASE
  • re.MULTILINE
  • re.DOTALL

Examples

Regular Expression for Email Validation

import re
regex = re.compile(r"^[a-z0-9!#$%&'*+/=?^_`{|}~-]+(?:\\.[a-z0-9!#$%&'*+/=?^_`{|}~-]+)*@(?:[a-z0-9](?:[a-z0-9-]*[a-z0-9])?\\.)+[a-z0-9](?:[a-z0-9-]*[a-z0-9])?$")
def isValid(email):
    if re.fullmatch(regex, email):
      print("Valid email")
    else:
      print("Invalid email")

More regular expressions
compile caches the query to make new lookups faster.
When a regular expression becomes too complex, the rule of thumb is to use a library instead.

Object-Oriented Programming: OOP

Tuple

An immutable (constant) list.

tuple = (val1, val2)

Return More than One Value from a Function

def get_student():
	n = input("Name: ")
	h = input("House: ")
	return n, h
name, house = get_student()
print(name)
student = get_student()
print(student[0])

To make the values mutable, return a list instead.

return [item1, item2]

Class

Classes are ‘blueprints’ for data; They define custom data types.
For example, you can use a class to represent a real-world entity.

Basic Class

class Student:
	... # '...' is a valid placeholder
student = Student  # Create an object from the Student class
student.name = "trude" # Add a new instance variable
student.house = "myHome"
print(student.name) # 'trude'

Attributes

class Student:
	def __init__(self, name, house): # Initialization function
		self.name = name
		self.house = house
name = 'trude'
house = 'myHome'
student = Student(name, house)
  • self is the object created from the class definition.
  • When an object is created, the __init__ function is executed.

Attributes Error Handling

class Student:
	def __init__(self, name="Unknown", house): # Initialization function
		if not name:
			raise ValueError("Missing name") # Create and output a custom error
		if house not in ["myHome", "yourHome", "hisHome"]:
			raise ValueError("Invalid house")
		self.name = name
		self.house = house

Change what Happens when an Object is Printed

class Student:
	def __init__(self, name, house): # Initialization function
		self.name = name
		self.house = house
	def __str__(self):
		return f"A Student - {self.name}"
student = Student("trude", "myHome")
print(student) # "A Student - trude"

Methods

class Mage:
	def __init__(self, name, clan, element): # Initialization function
		self.name = name
		self.clan = clan
		self.element = element
	def castElement(self):
		match self.element:
			case "Fire":
				return "FIRE SPELL"
			case "Water":
				return "Water SPELL"
			case "Earth":
				return "Earth SPELL"
			case "Air":
				return "AIR SPELL"
			case _:
				return "NO SPELL"
mage = Mage("trude", "magicINC", "Fire")
print(mage.castElement())

Properties

Dot notation (student.name = "") can bypass any data validation. To avoid this, make attributes constant.
A constant attribute is referred to as a property.

class Mage:
	def __init__(self, name, clan, element): # Initialization function
		self.name = name
		self.clan = clan
		self.element = element
	# Getter (function to get house attribute)
	@property # Indicates that this is a getter
	def clan(self):
		return self._clan # Add _ to avoid name collision.
	# Setter (function to set a value)
	@clan.setter # Indicates that this is a setter
	def clan(self, clan):
		if clan not in ["magicINC", "spellInator", "glyphGens"]:
			raise ValueError("Invalid Clan")
		self._clan = clan
mage = Mage("trude", "spellInator", "Fire")
mage.clan = "not-a-clan"

Example

class Mage:
	...
def main():
	mage = get_mage()
	print(f"{mage.name} from {mage.clan}")
def get_mage():
	mage = Mage()
	mage.name = "trude"
	mage.clan = "spellInator"
	return mage
if __name__ == "__main__":
	main()

Class Methods

A method that relates to the class itself, and doesn’t depend on the objects individually.

import random
class Hat:
	def __init__(self):
		self.clans = ["magicINC", "spellInator", "glyphGen"]
	def sort(self, name):
		print(name "is in", random.choice(self.clans))

hat = Hat()
hat.sort("Trude")

Class Variables

  • self.var A variable unique for each object
  • var A variable shared with all objects
class Hat:
	clans = ["magicINC", "spellInator", "glyphGen"]
	@classmethod
	def sort(cls, name):
		print(name "is in", random.choice(cls.clans))
Hat.sort("Trude")

A class method is a method global to the class, instead of created for each object. It relates to the data type (class) itself.

Example

class Mage:
	...
	@classmethod
	def get(cls):
		name = input("Name: ")
		clan = input("Clan: ")
		return cls(name, clan) # name and clans are returned to the class as self.name and self.clan.
	...
def main():
	mage = Mage.get()
	print(mage)
if __name__ == "__main__":
	main()

Inheritance

Inherit code from a parent class to avoid code duplication.

class Person:
	def __init__(self, name)
		if not name:
			raise ValueError("Missing name")
		self.name = name
class Student(Person): # Pass Person to Student
	def __init__(self, name, house):
		super().__init__(name) # Execute Person's __init__ method.
		self.house = house
	...
class Professor(Person):
	def __init__(self, name, subject):
		super().__init__(name)
		self.subject = subject
	...
person = Person("Random Guy")
student = Student("Trude", "myHome")
professor = Professor("Someone", "Math")

Operator Overloading

Customize what an operator does with a Class.

  • __add__ Behavior of ‘+’ operator.
class Vault:
	def __init__(self, gold=0, silver=0, copper=0):
		self.gold = gold
		self.silver = silver
		self.copper = copper
	def __str__(self):
		return f"{self.gold} Gold, {self.silver} Silver, {self.copper} Copper."
	def __add__(self, other):
		gold = self.gold + other.gold
		silver = self.silver + other.silver
		copper = self.copper + other.copper
		return Vault(gold, silver, copper)
trude = Vault(100, 50, 25)
jcionx = Vault(25, 50, 100)
print(trude) # 100 Gold, 50 Silver, 25 Copper
print(jcionx) # 25b Gold, 50 Silver, 100 Copper
total = trude + jcionx
print(total) # 125b Gold, 100 Silver, 125 Copper