Subsections of CS50's Intro to Programming with Python

Week 0: Functions and Variables

Week 0: Practice Code GitHub

Functions

A small program that allows you to perform some action in the desired Computer Programming Language.

print("Hello, World!")

When run, the following text will be printed on the screen:

Hello, World!

The text printed on the screen is termed as a Side Effect. It’s the side effect of running the function with the pre-determined input.

The built-in function print allows you to output some text to the screen in Python.

  • You can change the underlying implementation of function as long as name and parameters remains same, nobody running the program will notice.

Bugs

An error/mistake in the code which stops the program from running normally.

  • You make mistake while writing the code.
  • You may forgot to close parenthesis, or forgot to close a quotation marks, or use some illegal (in-terms of programming of-course) input.

Comments

A note to yourself about the function of your code, so you don’t forget in the future, what’s really happening with your code.

# This code prints "Hello, World!" on the screen
print("Hello, World!")

If it’s a complex function, a comment will immediately remind you what’s this piece of code is actually doing inside the program.

Pseudocode

A plain statement in any human language which methodically and simply outlines what you want to build in the desired programming language. A comment starts with # symbol which tells the interpreter to ignore what comes after it.

# A program that asks for user name as an input
# It takes that input, and prints Hello to that user

Above lines are simply a pseudocode which explains the outcome we need from the actual code.

  • No coding involved on this stage.
  • Only plan english is used to list down the desired outcomes.
  • It can act as a TODO list inside your code.
  • It helps break down complex programs into simple workable chunks.

Parameters

Following are the types of parameters.

Positional Parameters

A parameter passed to the function will be printed at a position it’s on i.e., first, seconds etc.

print("Hello,", name)

First positional parameter is “Hello,”, will be printed first. The name variable is a second positional parameter, printed second.

Named Parameters

A named parameter is a built-in optional argument of the given function which is usually provided after the positional parameters.

print("Hello,", name, sep='?' )
#OR
print("Hello,", end="")
print(name)

Both sep=' ' and end="\n" (with defaults) are the Named parameters provided by the print function.

Strings str

An immutable sequences of Unicode characters used to store and manipulate textual data.

  • In Python, anything inside '' or "" is called a string.
  • The function input() only accepts string arguments.

Python f-string

A special of type string that provides a concise and efficient way to embed expressions inside string literals.

print(f'Hello, {name}')

Strings Methods

Python string methods are built-in functions attached to string objects that allow for text manipulation, such as changing case, searching, replacing, splitting, and formatting.

Because strings in Python are immutable, these methods don’t modify the original string; instead, they return a new string with the requested changes applied.

str.strip() method

Remove whitespace from around the strings:

str.strip()

str.capitalize() method

Change the first letter of the string to upper case.

str.capitalize()

str.title() method

Capitalize the first letter of all the words in the given string.

str.title()

str.join() method

The join() method takes all items in an iterable and joins them into one string. – W3School

Syntax = `string.join(iterable)

myDict = {"name": "John", "country": "Norway"}
mySeparator = "TEST"

x = mySeparator.join(myDict)
# OR separator string can be mentioned directly
x = ','.join(myDict)
print(x)
Note

When using a dictionary as an iterable, the returned values are the keys, not the values.

String Slicing

You can return a range of characters by using the slice syntax

Specify the start index and the end index, separated by a colon, to return a part of the string.

Example:

greet = "Hello, World!"

Slice From the Start

Get the characters from the start to position 5:

print(greet[0:5])
#OR
print(greet[:5])

When starting from index 0, we don’t need to mention it. The character at index [5] will not be included.

Slice From the End

By leaving out the end index, the range will go to the end:

print(greet[2:])

If we want to slice the last 6 characters:

print(greet[-6:])

Python Interactive Mode

The live Python interpreter, where you can write code and get the execution immediately, unlike you write your code in a file and then run that file with Python interpreter.

It removes the friction for some quick Python practice.

Integers int()

Integers are whole numbers without a decimal point i.e., -3, -2, -1, 0, 1, 2, 3 etc.

  • In programming, ideal for counting or indexing

Floats float()

Floats (floating-point numbers) represent real numbers with decimal components.

  • Ideal for a wider range of values and fractional precision, making them suitable for measurements and continuous data.

Function def()

A small reusable piece of program which can be defined once and used multiple times inside the code to reduce repeatability and increase the readability.

# All the code resides in the main
def main():
    return

# Define as many functions as you want
def func():
   return

# Call main() to start the execution
main()

Running without calling on the main() function, no execution will happen, as our main code resides in the main function and nobody calling it, when we call on the main() function, then the execution starts, and the rest of the user functions will also be executed one by one as they appear in the main().

Variable Scope

The variable only exists in the scope where you defined it.

  • Variable defined inside the function, cannot be called on globally.
  • Variable defined in the 2nd user function cannot be called in the first one.

Week 1: Conditionals

Week 1: Practice Code GitHub

Conditionals

Ability to ask questions and answer those questions, in order to decide which line of code will be executed.

  • To write conditional statements in Python, we can use if statement.
  • The elif statement, ask a question, taking into account whether or not a previous question had a true or false answer.
  • The else statement, used when the all the other options has bee exhausted and only logical answer is the last one. So, we don’t need to compare anything for it, because it’s the only answer left. It’s catch all statement, if everything else isn’t proved right, let’s assume this is the answer.

Some symbols used in Python, are:

Symbol Representation
> Greater than
>= Greater than or Equal to
< Less than
<= Less than or Equal to
== Equal to
!= Not equal to

Not better design (See compare.py)

A better designed program:

The final design:

  • In the first version, program goes through all the code despite finding the correction answer earlier.
  • In the second iteration, program will immediately terminate as it gets its answer.

Boolean Expression

A question that gives Yes or No answer, or simply True or False answer.

OR Returns True if at least one operand is True; it only returns False if both operands are false.

AND Returns True if both operands are true; otherwise, it returns False. It’s uses short-circuit evaluation, meaning if the first operand is false, the second is not evaluated.

Python Operators

Operator Meaning
+ Addition
- Substraction
* Multiplication
** Exponentiation
/ Division
% Modulo operator

matchcase Statement

Provides structural pattern matching as a more readable and powerful alternative to if-elif-else chains. It evaluates an expression against successive patterns and executes the corresponding code block for the first match.

match name:
    case 'Harry':
        print('Gryffindor')
    case 'Hermione':
        print('Gryffindor')
    case 'Ron':
        print('Gryffindor')
    case 'Draco':
        print('Slytherin')
    case _: # Handle edge cases
        print('Who?')

More legible or better version:

match name:
  case 'Harry' | 'Hermione' | 'Ron':
    print('Gryffindor')
  case 'Draco':
    print('Slytherin')
  case _:
    print('Who?')

Week2: Loops

Week 2: Practice Code GitHub

Loops

Ability in Python and other programming languages, to do something again and again.

while Loops

  • Runs for forever unless certain conditions are met.

An infinite loop (design flaw):

i = 3
while i != 0:
    print('meow')
  • This code will run for forever unless interrupted by the user (Ctrl + C).
  • i != 0 will always remain True, we change the value of i inside the loop on each run.

Well designed while loop:

i = 3
while i != 0:
    print('meow')
    i = i - 1
  • On each run, i = i - 1 reduces the value of i by one digit. Until i != 0 condition is no longer true, and the loop stops.

for loop

In Python, a for loop is a control flow statement used to iterate over a sequence (such as a list, tuple, string etc.) or any other iterable object.

for i in [0, 2, 3]:
    print("meow")
#OR using range() function
for i in range(3):
    print("meow")

We are not using the i variable in the code, though it has a use case for holding the range value.

The more Pythonic approach is to use _, when you don’t care about the variable used or not use it later in the code.

for _ in range(3):
    print("meow")

List []

A list in Python, is a ordered, and mutable data structure used to store a collection of items in a single variable.

  • Defined using square brackets []
  • Each list element is separated by commas
  • Lists are heterogeneous, meaning they can contain elements of different data types (such as integers, strings, booleans or else) within the same structure.
  • Each element has an index starting from [0].

len() function

It returns number of items (length or size) in an object, such as strings, lists, tuples, dictionaries, sets, etc.

students = ["Hermione", "Harry", "Ron"]
for i in range(len(students)):
  print(i + 1, students[i])

List Methods

To add an item to a list:

list.append("name")

The list.append() can also add/append another list to the particular list.

To add multiple items at once to the list

list.extend(["Silly", "Donkey King"])

The same list.append(["Silly", "Donkey King"]) would have added them as a list to the existing list.

Remove an item’s first instance from a list: For a single item:

list.remove("Silly")

You can remove a nested list:

list.remove(["Silly", "Donkey King"])

Add an item at the particular place in the list

list.insert([index], "Marty")

Reverse the order of a list

list.reverse()

Remove the last item from a list

list.pop()

To clear the whole list

list.clear()

List Comprehension

List comprehension offers a shorter syntax when you want to create a new list based on the values of an existing list. — W3School

newlist = [expression for item in iterable if condition == True]
Info

In Python, An expression is a combination of values, variables, operators, and function calls that evaluates to a single value.

list = []
for i in range(20):
    if i%2 == 0:
        list.append(i)
print(list)

Using list comprehension, the whole for loop can be condensed to a single line:

list = []
even_list = [i for i in range(20) if i%2==0]
print(even_list)

Dictionaries dict

A dictionary (or dict) is a built-in mutable mapping type that stores data as a collection of unique, hashable key-value pairs.

  • Since Python 3.7, dictionaries are ordered by insertion, meaning they preserve the sequence in which keys were added.

Key characteristics:

  • Keys must be immutable, while values can be any data type.
  • Dictionaries are created using curly braces {} with colon-separated key-value pairs.

Dictionary Methods

Python dictionary methods are built-in functions designed to manipulate, access, and manage the key-value pairs stored in Python dictionaries.

If you want to get the value of specific key and don’t know if that exists:

dict.get("name", "Unknown")

If “name” is not a valid key, it will return “Unknown” instead of throwing error if we have tried to get it via dict.["name"].

To update the dictionary.

dict["name"] = "David"

Another method to update a dictionary is via dict.update(). It takes another dictionary as an arguments adds to the current dictionary.

dic.update({"name": "Dawood", "name": "Amin"})

To return all keys in the dict:

dict.keys()

To return all values in the dict:

dict.values()

Delete a particular key from the dictionary:

dict.pop("key")

To clear the whole dictionary key-value pairs:

dict.clear()

Return the key-value pairs as tuples in the dictionary

dict.items()

It returns the both key and value pairs as tuples, which we can access as:

for key, value in dict.items()
    print(key, value)

Dictionary Comprehension

Dictionary comprehension is used to create a dictionary in a short and clear way. It allows keys and values to be generated from a loop in one line. This helps in building dictionaries directly without writing multiple statements. — GeeksforGeeks

dict_comp = {key: value for key, value in iterable if condition}

Normal dictionary creation with a starting empty dict.

dict = {}
for i in range(10):
    if i%2 == 0:
        dict[i] = i ** 2
print(dict)

The dict comprehension:

dict = {}
dict_comp = {i:i**2 for i in range(10) if i%2==0}
print(dict_comp)

None

None is special built-in constant that represents the absence of a value or a null value. It’s the sole instance of the NoneType class and acts as a singleton, meaning there is only one None object in any Python interpreter session.

  • None is distinct from 0, False, or an empty string "", though it evaluates to False in boolean contexts.
  • Functions that don’t explicitly return a value automatically return None.

Tuples

Tuples are used to store multiple items in a single variable.

A tuple is a collection which is ordered and unchangeable. —W3School

  • Tuples are written within parenthesis ().
  • A tuple can contain different data-types at once
  • A one item tuple can be created with ("item1", ), otherwise Python will not recognize it.
  • The () will create an empty tuple.
  • The len() can be used to find out the number of items in the tuple.

Tuples Items

Tuple items are ordered, unchangeable, and allow duplicate values.

Tuples items are indexed, the first item has index[0], the second item has index[1] etc.

Week 3: Exceptions

Week 3: Practice Code GitHub

Python Exceptions

Exceptions are the problems in the Python or any other programming language code, that the programmer has to solve.

SyntaxError

A SyntaxError in Python occurs when code violates the grammatical rules of the language, making it impossible for the interpreter to parse and understand the structure. —TutorialsPoint.com

Common causes include:

  • Missing punctuation
  • Incorrect indentation
  • Misspelled keywords
  • Invalid string delimiters, such as mixing single and double quotes improperly

Value Error

A ValueError in Python is a built-in exception raised when a function or operation receives an argument of the correct data type but an inappropriate or invalid value. —RealPython.com

Unlike a TypeError, which occurs when the wrong data type is provided, a ValueError indicates that while the input is the right kind of object, its specific content is unsuitable for the operating being performed.

Common scenarios that trigger a ValueError include:

  • Invalid Type Conversion: Attempting to convert a string that doesn’t represent a valid number into an integer or float
  • Mathematical Constraints: Performing an operation that requires a specific range of values, such as taking the square root of a negative number or calculating the factorial of a negative integer
  • List Operations: Trying to remove a value from a list that doesn’t exist within it.
  • Unpacking Mismatches: Attempting to unpack a list or iterable into a different number of variables than there are items

NameError

The NameError exception occurs if you use a variable that is not defined. —W3School

You can handle the NameError in a try..except statement.

try and except

The try block lets you test a block of code for error.

The except block lets you handle the error.

The else block lets you execute code when there is no error.

The finally block lets you execute code, regardless of the result of the try and except blocks.

The raise keyword is used to raise an exception.

Let’s see a code:

try:
    x = int(input("What's the value of x: "))
except ValueError:
    print("x is not an integer.")
else:
    print(f"The value of x is: {x}")

The else part will only be executed if there are no error.

If you don’t want to print anything in the except block write pass, it will not raise/print anything to the user.

MemoryError

In Python, a MemoryError is a built-in exception that occurs when the interpreter cannot allocate enough memory for an operation, such as creating a large list, loading a huge file, or executing a recursive function without a base case.

This error signals that the program has attempted to use more memory (RAM or virtual address space) that the system can provide.

Common causes include:

  • Unbounded Data Growth: Accumulating data in lists or dictionaries within infinite or large loops.
  • Large Object Allocation: Attempting to create data structures (like NumPy arrays or Pandas DataFrames) that exceed available system memory.
  • Excessive Recursion: Recursive functions that consume excessive stack space before hitting the recursion limit.
  • Memory Leaks: Although less common as a direct cause, failing to release references to large objects can lead to exhaustion.

KeyError

A KeyError is an exception raised when you try to access a value in a dictionary (or other mapping) using a key that doesn’t exist within that collection.

It is a subclass of LookupError and serves as Python’s signal that the requested key cannot be found among existing keys.

Raise an Exception

As a programmer, you can choose to throw an exception if a condition occurs.

To throw an exception, use the raise keyword.

x = -1
if x < 0:
    raise Exception("Sorry, no numbers below zero")

You can define what kind of error to raise, and the text to print to the user.

x = "hello"
if not type(x) is int:
    raise TypeError("Only integers are allowed")

Debugging

Debugging is the process of identifying, analyzing, and resolving errors or bugs in code to ensure it runs correctly and produces expected results.

It involves detecting syntax errors (structural issues like typos) and semantic errors (logical issues where code runs but yields incorrect outputs).

The primary goal is to locate the root cause of unexpected behavior by examining the program’s state during execution. This is achieved through various techniques, including:

  • Using Tracebacks: Analyzing error messages to pinpoint where exceptions occur.
  • Print Statements: Inserting print() calls to track variable values and code flow.
  • BreakPoints: Pausing execution at specific lines to inspect variables and state.
  • Debuggers: Using tools like built-in pdb module or IDE debuggers (e.g., in Zed or VS Code) to step through code line-by-line and interactively inspect the execution environment.

Week 4: Libraries

Week 4: Practice Code GitHub

Libraries

In programming, libraries are like ready-made tools that save time, reduce effort, and make development much easier.

A Python library is simply a collection of pre-written code that developers can use to perform specific tasks without having to write everything from scratch. —Antara Das via Medium

Basically, you can import the library into your code, and it comes with predefined functions and methods that help you get the gob done.

Module

A module in Python is just a library that typically or more functions or other features built into it.

Purpose of a Module or Library is a re-usability of a code when you find yourself using same lines of code over and over again from across your project’s code.

import

The keyword import allows to import some functions from modules in Python.

To import random library with all its functions:

import random
# Coin flip
coin = random.choice(["Heads", "Tails"])
print(coin)

from

The from keyword is primarily used to import specific functions, classes, or variables from a module into the current namespace, allowing direct access without prefixing the module name.

To import choice() function from random module:

from random import choice
# Coin flip
coin = choice(["Heads", "Tails"])
print(coin)

Command-Line Arguments

Command-Line arguments are parameters passed to a Python script upon execution from the terminal, allowing users to customize program behavior without modifying source code.

The one we use is using sys module feature sys.argv, where sys.argv[0] is the script name and subsequent indices are the provided arguments.

import sys
print("Hello, my name is", sys.argv[1])

Running with name as an argument:

python name.py Alex

If we replace with sys.argv[0], it will print the name of program as Hello, my name is name.py, and no CLI argument will be printed.

random Module

It’s a built-in Python module, that provides functions for generating pseudo-random numbers, selecting random elements, and shuffling sequences.

It’s not a separate package and doesn’t require installation; it’s used by simply adding import random to your code.

statistics Module

Python has a built-in module that you can use to calculate mathematical statistics of numeric data.

Statistics Methods (REF: W3School) W3School W3School

requests Module

The requests module allows you to send HTTP requests using Python. —W3School

The HTTP request returns a Response Object with all the response data (content, encoding, status, etc.).

Syntax:

requests.methodname(params)

Methods

Python json

Python has a built-in package called json, which can be used to work with JSON data. —W3School

There are many different methods json has, but for now we only see the json.dumps() method.

Format the Result

The json.dumps() prints a JSON string, but it is not very easy to read, with no indentations and line breaks.

The method has parameters to make it easier to read the results.

json.dumps(x, indent=4)

You can also define the separators, default value is `(", “, “: “), which means using a comma and a space to separate each object, and a colon and a space to separate keys from values:

json.dumps(x, indent=4, separators=(". ", "= "))

Order the Result

Use sort_keys parameter to specify if the result should be sorted or not:

json.dumps(x, ident=4, sort_keys=True)

PACKAGE

A package is a third-party library that we can install on our computer.

An external package maintainer is PyPI searchable via https://pypi.org or CLI.

You can install packages from PyPI via Python package manager called PIP.

Create a virtual environment inside your project:

python -m venv myenv

Activate your virtual env:

source myenv/bin/activate

Now install the module:

pip install cowsay

Now you can import it inside .py file.

After finishing your work, you can deactivate the env:

deactivate

Depending on your prompt, it will show you’re using a virtual environment with Python version number.

Note

Add your virtual env directory and __pycache__ dir to .gitignore, so they don’t mess your git history or project repo.

Create Your Own Package

Create a folder, put all your modules inside it. Create an empty file inside that folder:

touch __init__.py

Now you can import your package:

from foldername.module import function

APIs

API stands for Application Programming Interface.

An API is a set of rules, protocols, and specifications that allows different software applications to communicate with each other.

In context Python, APIs generally refer to two distinct concepts:

  • Consuming External APIs: Using libraries like Requests to send HTTP requests to external services (e.g., weather data, social media) and parse the responses.
  • Building Web APIs: Creating server-side endpoints using frameworks like Flask, Django or FastAPI that expose functionality and data to other applications via specific URLs and HTTP methods (GET, POST, etc.).

JSON

JSON stands for JavaScript Object Notation.

  • JSON is a lightweight format for storing and transporting data.
  • JSON is often used when data is sent from a server to a web page.
  • JSON is “self-describing” and easy to understand

Though it’s related to JavaScript, but it’s itself typically used as a language agnostic format for exchanging data between computers.

__name__ REF: freeCodeCamp

When a Python interpreter reads a Python file, it first sets a few special variables. Then it executes the codes from the file.

One of those variables is called __name__.

So when the interpreter runs a module, the __name__ variable will be set as __main__ if the module is being run is the main program.

But if the code is importing the module from another module, then the __name__ variable will be set to that module name.

There is a really nice use case for the __name__ variable, whether you want a file that can be run as the main program or imported by other modules. We can use an if __name__ == "__main__" block to allow or prevent parts of code from being run when the modules are imported.

When the Python interpreter reads a file, the __name__ variable is set as __main__ if the module being run, or as the module’s name if it is imported. Reading the file executes all top level code, but not functions and classes (since they will only get imported).

Style

The standardized way of writing Python source code to ensure readability and maintainability.

The primary standard is PEP 8, which dictates rules for indentation (4 spaces), line length, naming conventions (snake_case for variables/functions, CamelCase for classes), and import ordering.

Adhering to these rules allows teams to collaborate efficiently and reduces bugs caused by inconsistent formatting.

REF: Python Style Basics - PEP8

  • Indentation: Indent code by 4 spaces and be consistent.
  • Tab/Spaces: Early on python, some people use 2 spaces or tabs. Those practices have died out, and now 4 spaces is the standard.
  • Max Lines Length: In the old days of relatively small computer displays, projects would frequently have a rules that no line in the code could be wider than 80 or 100 characters, so that the code would fit on the display. This sort of rule has becomes less common. Often in Python, the simplest thing to do is just let a long line be long.
    • However if a line so long that it’s hard to read or work with, break it into shorter, separate lines.
  • Blank Lines: PEP8 requires 2 blank lines before each def in a file. This is one of the weaker PEP8 rules.