1.2. Python Basic Syntax#

Hide code cell source

import sys
from pathlib import Path

current = Path.cwd()
for parent in [current, *current.parents]:
    if (parent / '_config.yml').exists():
        project_root = parent  # ← Add project root, not chapters
        break
    project_root = Path.cwd().parent.parent

sys.path.insert(0, str(project_root))

from shared import thinkpython, diagram, jupyturtle, download

# # Register as top-level modules so direct imports work in subsequent cells
sys.modules['thinkpython'] = thinkpython
sys.modules['diagram'] = diagram
sys.modules['jupyturtle'] = jupyturtle
sys.modules['download'] = download
../../_images/python-syntax.webp

Fig. 1.5 Python Syntax Overview[dataflair Team, 2017]#

1.2.1. Indentation and Block Structure#

In Python, indentation defines code blocks instead of braces ({}), which many other languages use. Any line ending with a colon (:), such as if, for, while, def, or class, starts a new block, and the indented lines that follow belong to that block. Python expects consistent indentation (commonly 4 spaces per level), and mixing tabs and spaces or misaligning lines can raise IndentationError. When indentation changes, Python treats that as entering or leaving a block, so correct spacing controls program structure and execution flow.

for i in range(5):
print(i)                ### This line is not indented, so it will cause an error

Output

Cell In[2], line 2
    print(i)                ### This line is not indented, so it will cause an error
    ^
IndentationError: expected an indented block after 'for' statement on line 1
for i in range(5):
    print(i)            ### This line is indented, so it will work correctly
0
1
2
3
4
### Exercise: Fix indentation
# The code below should print three lines: 0, 1, and 2.
# Replace the pass statement with print(i), keeping it indented inside the loop.
for i in range(3):
    ### Your code starts here.
    pass
    ### Your code ends here.

Hide code cell source

### Solution
for i in range(3):
    print(i)
0
1
2

1.2.2. Statement Formatting#

In Python, a statement usually ends at a newline, so each line is typically one statement. You can place multiple simple statements on one line using semicolons (;), but this is generally discouraged because it reduces readability. For long statements, Python allows implicit line continuation inside parentheses (), brackets [], or braces {}, which is the preferred way to split expressions across lines. Explicit continuation with a backslash (\) also works, but it is fragile because a trailing space after the backslash will cause an error. In practice, write one statement per line and use parentheses for multi-line expressions.

### 1) Newlines: one statement per line (preferred)
x = 10
y = 20
total = x + y
print(total)
30
### 2) Semicolons: multiple statements on one line (valid, but less readable)
x = 10; y = 20; print(x + y)
30
### 3) Multi-line with parentheses (preferred)
total = (
    100
    + 200
    + 300
)
print(total)
600
### 4) Multi-line with backslash (works, but less preferred)
total = 100 + 200 + \
        300
print(total)
600
### 5) Multi-line function call with parentheses
print(
    "Name:", "Alice",
    "Age:", 25
)
Name: Alice Age: 25
### Exercise: Split a long expression
# Rewrite the calculation of total inside parentheses across multiple lines.
# The answer should still print 46.5.
base_price = 32
shipping = 7.5
tax = 7
### Your code starts here.



### Your code ends here.

Hide code cell source

### Solution
base_price = 32
shipping = 7.5
tax = 7

total = (
    base_price
    + shipping
    + tax
)

print(total)
46.5

1.2.3. Input and Output#

Programs communicate through output and input. This section shows how print() displays values and how f-strings format them, then how input() reads text from the user and how to convert that text into numbers.

1.2.3.2. Keyboard Input#

Python provides a built-in function called input that stops the program and waits for the user to type something. When the user presses Return or Enter, the program resumes, and input returns what the user typed as a string. Before getting user input, you might want to display a prompt that explains what to type. The input syntax is:

variable = input("Prompt message: ")

To execute the following examples, enable Live Code and uncomment the input() line.

### uncomment the line below to make the code work

# name = input("Enter your name [{default_name}]: ") 
print("Hello, " + name + "!")
Hello, Alice!

1.2.3.3. Type Conversion with Input#

The user input will be a string type, so if you expect the user to type an integer, you can use the int function to convert the return value to int.

### uncomment the line below ot make the code work

# age = input("Enter your age: ")   

print(type(age))                      ### age is of string type
age = int(age)                        ### convert input string to integer

print(type(age))
print(f"You are {age} years old.")
<class 'int'>
<class 'int'>
You are 25 years old.

Or, just combine the intput function and the int() function together.

### uncomment the line below to make the code work

# age = int(input("Enter your age: "))      ### Directly convert input to integer

print(type(age))

print(f"You are {age} years old.")
<class 'int'>
You are 25 years old.

The int() function is not the only conversion function. Use float() when the text holds a decimal number, such as a price. Use str() to turn a number into text, for example when you want to join it to other strings with +. Recall that "I am " + 25 raised a TypeError earlier; converting the number with str() fixes that.

price_text = "19.99"                         ### text typed by a user
price = float(price_text)                    ### convert decimal text to a float
print(price * 2)

age = 25
message = "I am " + str(age) + " years old"  ### convert the number to text before joining
print(message)
39.98
I am 25 years old

You can also update a variable using its own current value. In total = total + 5, Python first evaluates the right side with the current value of total, then stores the result back in total. This pattern keeps running totals and counts up to date.

total = 10
total = total + 5     ### use the old value (10), then store the new value (15)
print(total)

total = total - 3     ### update again from the current value (15)
print(total)
15
12

Once text is converted to a number, you can do arithmetic with it. Two operators are handy for splitting a total into whole units and a leftover, such as minutes into hours, or items into full boxes: // (floor division) gives the whole number of times one number fits into another, and % (remainder, also called modulus) gives what is left over. Section 2.2 covers both operators in detail.

total_minutes = int("135")
print(total_minutes // 60)   # 2  (full hours)
print(total_minutes % 60)    # 15 (minutes left over)
2
15
### Exercise: Convert text input to numbers
# input() returns text, so numeric input usually needs conversion.
# The variable below simulates text a user typed into input().
# Convert minutes_text to an integer named total_minutes.
# Calculate full hours in a variable named hours_part.
# Calculate leftover minutes in a variable named minutes_part.
# Print hours_part and minutes_part.
minutes_text = "135"
### Your code starts here.



### Your code ends here.

Hide code cell source

### Solution
minutes_text = "135"

total_minutes = int(minutes_text)
hours_part = total_minutes // 60
minutes_part = total_minutes % 60

print("Hours:", hours_part)
print("Minutes:", minutes_part)
Hours: 2
Minutes: 15

1.2.4. Comments#

Comments are explanatory notes in your code that are ignored by the Python interpreter. Comments are used to:

  1. Explain complex logic: help others (and your future self) understand what the code does.

  2. Document assumptions: note why certain decisions were made.

  3. Mark TODO items: indicate areas that need improvement or completion.

  4. Disable code temporarily: comment out code for testing without deleting it.

Single-line comments start with the hash symbol #. Everything after # on that line is ignored by Python. For multi-line comments, we use multiple hashes.

### single-line comments
# This is a comment explaining the code below
price = 100
tax_rate = 0.08  # 8% sales tax

### multiple-line comments
# Calculate total price including tax
# total = price * (1 + tax_rate)
# print(f"Total price: ${total}")

Ideally, good variable names can reduce the need for comments, but long names can make complex expressions hard to read, so there is a tradeoff. For example, velocity_mph = 8 might be clear without a comment, but v = 8 # velocity in miles per hour might be more readable in a mathematical formula.

1.2.4.1. Docstring#

Unlike comments, which are ignored by the Python interpreter at runtime, docstrings are string literals used for documentation. They are processed by the interpreter and can be accessed with __doc__ or help() as part of the program.

Usually, a docstring is a string literal written as the first statement inside a module, function, class, or method to describe what it does, its inputs, outputs, or behavior. By convention it uses triple quotes ("""...""") so it can span multiple lines.

def area_circle(radius):
    """Return the area of a circle given its radius."""
    import math
    return math.pi * radius**2

print(area_circle(3))      # 28.274333882308138
print(area_circle.__doc__) # Return the area of a circle given its radius.
28.274333882308138
Return the area of a circle given its radius.
### Exercise: Add a useful docstring
# The function below is already written for you, so you do not write def or return yourself.
# 1. Replace the line "# docstring goes here" with a one-line docstring in triple quotes
#    that says what the function returns.
# 2. Add a short # comment at the end of the return line that explains the calculation.
# 3. Run the cell. The last two lines print the docstring with __doc__ and call the function.
def pages_per_day(total_pages, days):
    ### Your code starts here.
    # docstring goes here
    return total_pages / days
    ### Your code ends here.

print(pages_per_day.__doc__)
print(pages_per_day(93, 5))
None
18.6

Hide code cell source

### Solution
def pages_per_day(total_pages, days):
    """Return the average number of pages read per day."""
    return total_pages / days    # total pages divided by the number of days

print(pages_per_day.__doc__)
print(pages_per_day(93, 5))
Return the average number of pages read per day.
18.6

1.2.5. Objects#

Every value in Python, whether it is a number, a string, or a function, is an object. An object is a piece of data stored in memory, and every object has three properties:

  • a type, which says what kind of value it is and what you can do with it,

  • a value, which is the data itself, and

  • an identity, which tells it apart from every other object while the program runs.

A variable is a name that refers to an object.

Two built-in functions let you inspect an object. type() returns the object’s type, and id() returns its identity as an integer. You can think of the identity as the object’s address in memory. The number changes each time you run the program, so do not rely on a particular value. Section 2.1 looks at objects and identity in more detail.

score = 95
print(type(score))     ### the type: int
print(score)           ### the value: 95
print(id(score))       ### the identity: an integer that is unique while the object exists
<class 'int'>
95
140140617117320
### Exercise: Inspect object information
# Use the value below to inspect an object.
# Print its type, value, and identity with type(), the variable itself, and id().
value = "Python"
### Your code starts here.



### Your code ends here.

Hide code cell source

### Solution
value = "Python"

print(type(value))
print(value)
print(id(value))
<class 'str'>
Python
140140616346080

1.2.6. Python Keywords#

Python reserved words, or keywords, are words that you are not supposed to use for variable names.

Reserved words, or keywords, are special words reserved by the programming language to be used to specify the structure of a program. Keywords of the language cannot be used as ordinary identifiers (such as variable names).

For example, if you try to assign a string to a variable named class, you will receive a syntax error because class is a keyword.

%%expect SyntaxError
class = 'Assigning a string to a keyword to be a variable name...'
  Cell In[42], line 1
    class = 'Assigning a string to a keyword to be a variable name...'
          ^
SyntaxError: invalid syntax

Here’s a complete list of 35 Python keywords as shown in the Python Language Reference:

False      await      else       import     pass
None       break      except     in         raise
True       class      finally    is         return
and        continue   for        lambda     try
as         def        from       nonlocal   while
assert     del        global     not        with
async      elif       if         or         yield

Keywords are part of Python’s syntax. Instead of memorizing all 35 at once, it helps to group some common ones by the jobs they do:

Category

Examples

Flow control

if, elif, else, for, while, break, continue, pass

Definitions

def, class, lambda

Boolean logic and identity

and, or, not, is, in, True, False, None

Imports and scope

import, from, global, nonlocal

Exceptions and context

try, except, finally, raise, assert, with, as

Async and generators

async, await, yield

In most development environments, keywords are displayed in a different color; if you try to use one as a variable name, the editor or interpreter will warn you.

To show all the Python keywords, you can do:

from keyword import kwlist
print("Number of Python keywords:", len(kwlist), "\n")
print("All Python keywords: \n", kwlist)
Number of Python keywords: 35 

All Python keywords: 
 ['False', 'None', 'True', 'and', 'as', 'assert', 'async', 'await', 'break', 'class', 'continue', 'def', 'del', 'elif', 'else', 'except', 'finally', 'for', 'from', 'global', 'if', 'import', 'in', 'is', 'lambda', 'nonlocal', 'not', 'or', 'pass', 'raise', 'return', 'try', 'while', 'with', 'yield']

As stated earlier, keywords form the program structure. The examples below show how keywords act like the grammatical parts of a program.

For example, if is a keyword in Python and is highlighted in most code editors.

### example of the "if" keyword in Python
num = 5
if num > 0:
    print("Positive number")
Positive number

And for and in are also keywords.

### example of the "for" keyword in Python
for i in range(5):
    print(i, end=" ")
0 1 2 3 4 

1.2.6.1. Soft keywords#

Python’s soft keywords are special words that act as keywords only in specific contexts, but can be used as regular identifiers (such as variable or function names) in other contexts. As of Python 3.12, there are four soft keywords: match, case, _, and type.

### Exercise: Check keywords
# Import the keyword module.
# Store whether "class" is a Python keyword in class_is_keyword.
# Store whether "dataset" is a Python keyword in dataset_is_keyword.
# Print both values.
### Your code starts here.



### Your code ends here.

Hide code cell source

### Solution
import keyword

class_is_keyword = keyword.iskeyword("class")
dataset_is_keyword = keyword.iskeyword("dataset")

print(class_is_keyword)
print(dataset_is_keyword)
True
False

1.2.7. Built-In Functions#

In Python, built-in functions and built-in modules are both part of the standard tools the language gives you, but they serve different purposes. Built-in functions are ready to use without requiring any imports. They are automatically available in every Python program. Examples of built-in functions include print() and len().

For the functionalities that Python does not provide, you either:

  1. Find a third-party module or package (or library) with the functions, install the package, and use the contained functions, or

  2. Write a user-defined custom function or class method.

There are 71 built-in functions in recent Python 3 releases.

../../_images/python-builtin-functions.png

Fig. 1.6 Python Built-in Functions#

The built-in functions are available without import. The table below lists a compact working set for Python programs.

Function

Purpose

print()

display output

type()

inspect an object’s type

len()

count items in a sequence or collection

round()

round a number

abs()

absolute value

min(), max()

smallest or largest value

sum()

add numeric values

sorted()

return a sorted list

input()

read text from a user in interactive programs

In the Python Standard Library, you can find all the 71 Python built-in functions listed:

../../_images/python-builtin-functions.png

Fig. 1.7 Python Built-In Functions#

The 71 Python built-in functions can be grouped by purpose.

#

Group

Functions

Notes

1

Numbers & math

abs, divmod, max, min, pow, round, sum

pow(a, b, mod=None) supports modular exponentiation.

2

Type construction/conversion

bool, int, float, complex, str, bytes, bytearray, memoryview, list, tuple, set, frozenset, dict, range

Convert or construct core types.

3

Object/attribute introspection

type, isinstance, issubclass, id, hash, dir, vars, repr, ascii

vars(obj) → obj.__dict__ when available.

4

Attribute access

getattr, setattr, delattr, hasattr

Dynamic attribute management.

5

Iteration & functional tools

iter, next, enumerate, zip, map, filter, sorted, reversed

Prefer comprehensions when clearer.

6

Sequence/char helpers

len, ord, chr, slice

len() works on many containers.

7

I/O

print, input, open

open returns a context manager; prefer with open(...) as f:.

8

Formatting / representation

format, bin, oct, hex

Also see f-strings for formatting.

9

Object model (OOP helpers)

object, property, classmethod, staticmethod, super

Define descriptors and class behaviors.

10

Execution / metaprogramming

compile, eval, exec

Use with care; security concerns for untrusted input.

11

Environment / namespaces

globals, locals

Introspection of current namespaces.

12

Help/debugging

help, breakpoint

breakpoint() respects PYTHONBREAKPOINT.

13

Import

__import__

Low-level import; usually use import statement instead.

As an example, you can apply functions on a Python list called scores like:

scores = [88, 92, 75, 100, 83]
print(len(scores))
print(min(scores), max(scores))
print(sum(scores) / len(scores))
print(sorted(scores))
5
75 100
87.6
[75, 83, 88, 92, 100]

Some functions return values you can store and reuse, while print() displays output but returns None.

return_value = print("display only")
print(return_value)
display only
None

1.2.7.1. Arguments#

When you call a function, the expressions inside the parentheses are called arguments. A function may take no argument or more than one argument. For example:

num1 = int('101')           ### take one argument; string to integer
num2 = int('101', 2)        ### take two argument; convert base 2 to int
num3 = round(1.2345, 2)     ### 2 arg; round to two decimal places

print(num1)
print(num2)
print(num3, 'round to 2 places')     ### two arguments

print()                     ### no argument
101
5
1.23 round to 2 places

1.2.7.2. Return Values#

When you call built-in functions such as abs(), round(), sqrt(), and pow(), they return a value you can assign to a variable or use as part of an expression.

You use the print() function to display values, but it doesn’t return values that you can assign to variables or use in expressions. So, print()

import math

math.sqrt(42 / math.pi)            ### returns value
3.656366395715726
radius = math.sqrt(42 / math.pi)    ### assign return to variable
radius                              ### evaluate variable
3.656366395715726

If a function doesn’t have a return statement, it returns None, which is a special value like True and False.

For example, you have used the print function to display strings, but it does not use a return statement to return a value. If we assign the result of a print statement to a variable, the variable will have the value of None.

The example below still shows the output of the print () statement first, though, even when it’s been assigned.

unknown = print('a string')
print(unknown)
a string
None
### Exercise: Summarize prices with built-in functions
# The list below holds the prices of five fruits in dollars.
# Use built-in functions to store the number of prices in count,
# the lowest and highest prices in lowest and highest,
# and the average price rounded to 2 decimal places in average.
# Print count, then lowest and highest on one line, then average.
prices = [1.25, 0.5, 2.75, 3.1, 0.89]
### Your code starts here.



### Your code ends here.

Hide code cell source

### Solution
prices = [1.25, 0.5, 2.75, 3.1, 0.89]

count = len(prices)
lowest = min(prices)
highest = max(prices)
average = round(sum(prices) / len(prices), 2)

print(count)
print(lowest, highest)
print(average)
5
0.5 3.1
1.7

1.2.8. Modules and Packages#

In Python, functions, classes, modules, packages, and libraries are essential tools for organizing and reusing code. In contrast to built-in functions, you need to use the import keyword to import the module before you use it.

Term

What It Is

Typical Use

Function

A named, reusable block of code that performs one specific task and can be called multiple times.

Break problems into smaller steps and avoid repeating logic.

Class

A blueprint for creating objects that bundle data (attributes) and behavior (methods).

Model real-world entities and organize related state + behavior.

Module

A single Python file (.py) containing code such as functions, classes, and variables.

Split code into files and import what you need.

Package

A directory of related modules (and often subpackages) that forms a larger namespace.

Organize multi-file projects into clear components.

Library

A broader collection of packages/modules built to solve common domains or tasks.

Reuse mature tools (e.g., data analysis, web, scientific computing) instead of building from scratch.

Together, these components help make Python code more organized, efficient, and maintainable. The most important distinction for now is:

  • Module: a single Python file (e.g., mymodule.py).

  • Package: a directory of modules (optionally with __init__.py).

1.2.8.1. Importing Modules#

Python ships with about 300 built-in and standard-library modules. For those modules, you can import them directly and use them (for example, import math). There are different ways of importing:

Import Pattern

Example Code

Usage Example

Description

Standard import

import math

math.sqrt(25)

Clear, namespaced imports

Aliased import

import math as m

m.pi

Alias for brevity (common with large libs)

Selective import

from math import sqrt

sqrt(25)

Convenient, but use sparingly for readability

Import all (not recommended)

from math import *

sqrt(25)

Imports all names; can cause name conflicts and is discouraged

Style notes: Prefer absolute imports in top-level scripts; reserve relative imports for package internals. Follow PEP 8 import order: standard library → third-party → local.

To use methods or attributes from a module, use the dot operator (.) between the module name and the attribute name. For example, the Python math module provides a variable called pi that contains the mathematical constant denoted \(\pi\). We can access it as math.pi:

import math
math.sqrt(25)     ### dot operator
5.0
### Exercise: Import a module and use dot notation
# Import math.
# Store the square root of 144 in root_value.
# Store the area of a circle with radius 3 in circle_area.
# Print root_value and round(circle_area, 2).
### Your code starts here.



### Your code ends here.

Hide code cell source

### Solution
import math

root_value = math.sqrt(144)
circle_area = math.pi * 3 ** 2

print(root_value)
print(round(circle_area, 2))
12.0
28.27

1.2.8.2. Installing Packages#

External, third-party modules are created by the Python community. You must use pip to install them first (for example, notebook, NumPy, or pandas), then import them before using them. Most software on the Python Package Index (PyPI; https://pypi.org, where pip finds software packages) is referred to as “packages” in this context.

To install the packages in the CLI, you would go into your project directory, activate your virtual environment, and then use the pip installation syntax to install the package into your .venv folder (site-packages) for dependency integrity:

pip install [package_name]

If you are in Jupyter Notebook, use the Jupyter Notebook magic command %pip (instead of the older !pip) to install the package into the active kernel:

%pip install [package_name]

For example, NumPy (numeric Python) is a popular package for data science. From inside Jupyter Notebook, you can install it with:

%pip install numpy
Requirement already satisfied: numpy in /opt/hostedtoolcache/Python/3.11.17/x64/lib/python3.11/site-packages (2.4.6)
Note: you may need to restart the kernel to use updated packages.

You probably want to comment the %pip install line out after installation as you only need to install it once in the virtual environment.