23. On Programming#

23.1. Programming as Language#

Learning to program means learning a new way to think – think like a computer scientist. This way of thinking combines some of the best features of mathematics, engineering, and natural science.

Like mathematicians, computer scientists use formal languages to denote ideas – specifically computations. Like engineers, they design things, assembling components into systems and evaluating trade-offs among alternatives. Like scientists, they observe the behavior of complex systems, form hypotheses, and test predictions.

We will start with the most basic elements of programming and work our way up. In this chapter, we’ll see how Python represents numbers, letters, and words. And you’ll learn to perform arithmetic operations.

You will also start to learn the vocabulary of programming, including terms like operator, expression, value, and type. This vocabulary is important – you will need it to understand the rest of the book, to communicate with other programmers, and to use and understand virtual assistants.

Natural languages are the languages people speak, like English, Spanish, and French. They were not designed by people; they evolved naturally.

Formal languages are languages that are designed by people for specific applications. For example, the notation that mathematicians use is a formal language that is particularly good at denoting relationships among numbers and symbols. Similarly, programming languages are formal languages that have been designed to express computations.

Although formal and natural languages have some features in common there are important differences:

  • Ambiguity: Natural languages are full of ambiguity, which people deal with by using contextual clues and other information. Formal languages are designed to be nearly or completely unambiguous, which means that any program has exactly one meaning, regardless of context.

  • Redundancy: In order to make up for ambiguity and reduce misunderstandings, natural languages use redundancy. As a result, they are often verbose. Formal languages are less redundant and more concise.

  • Literalness: Natural languages are full of idiom and metaphor. Formal languages mean exactly what they say.

Because we all grow up speaking natural languages, it is sometimes hard to adjust to formal languages. Formal languages are more dense than natural languages, so it takes longer to read them. Also, the structure is important, so it is not always best to read from top to bottom, left to right. Finally, the details matter. Small errors in spelling and punctuation, which you can get away with in natural languages, can make a big difference in a formal language.

23.2. Expressions and statements#

By definition, an expression is a combination of values, variables, operators, and function calls that the Python interpreter can evaluate to produce a single value. Note that a single value, like an integer, floating-point number, or string, can be an expression.

A statement is a unit of code that executes an action or command, or controls the flow of the program. They do not evaluate to a value that can be used elsewhere, like an expression. For example, an assignment statement creates a variable and gives it a value, but the statement itself has no value.

Computing the value of an expression is called evaluation; whereas running a statement is called execution.

This is an expression that includes several elements.:

19 + n + round(math.pi) * 2

This is a statement:

n = 17

So, a statement performs an action. An expression computes a value. For example:

Type

Example

Description

Statement

x = 5

Assignment statement: Assigns 5 to x (changes program state). Produces no value.

print(x)

Print statement: Prints something to the screen (has an effect); no value.

if x > 0:

if statement: Begins a conditional block — a control flow structure; no value.

import math

Import the functionalities from the math module; no value.

Expression

2 + 3

Produces the value 5.

x * y

Computes a value based on x and y.

len("data")

Evaluates to 4.

Overall, the key differences between expressions and statements can be summarized as:

Feature

Expression

Statement

Produces value

Yes

No

Can be assigned

var = expr

No (e.g., if (x = 5):)

Can be printed

print(expr)

No

Used as argument

func(expr)

No

Can be nested?

Yes

Limited

6 + 6 ** 2
42

Notice that exponentiation happens before addition. Python follows the order of operations you might have learned in a math class: exponentiation happens before multiplication and division, which happen before addition and subtraction.

In the following example, multiplication happens before addition.

12 + 5 * 6
42

If you want the addition to happen first, you can use parentheses.

(12 + 5) * 6
102

Every expression has a value. For example, the expression 6 * 7 has the value 42.

23.3. Python Operators#

In Python, operators are special symbols that perform computations or logical comparisons between values. They form the backbone of most expressions — whether you’re performing arithmetic, comparing data, assigning values, or testing relationships between objects.

23.3.1. Operators#

Type

Operator

Meaning

Example

Result

1. Arithmetic

+

Addition

5 + 3

8

-

Subtraction

5 - 3

2

*

Multiplication

5 * 3

15

/

Division

5 / 2

2.5

//

Floor Division

5 // 2

2

%

Modulus

5 % 2

1

**

Exponentiation

5 ** 2

25

2. Comparison

==

Equal to

5 == 5

True

!=

Not equal to

5 != 3

True

>

Greater than

5 > 3

True

<

Less than

5 < 3

False

>=

Greater than or equal

5 >= 5

True

<=

Less than or equal

5 <= 3

False

3. Logical

and

Logical AND

True and False

False

or

Logical OR

True or False

True

not

Logical NOT

not True

False

4. Assignment

=

Assign

x = 5

x is 5

+=

Add and assign

x += 3

x = x + 3

-=

Subtract and assign

x -= 3

x = x - 3

*=

Multiply and assign

x *= 3

x = x * 3

/=

Divide and assign

x /= 3

x = x / 3

//=

Floor divide and assign

x //= 3

x = x // 3

%=

Modulus and assign

x %= 3

x = x % 3

**=

Exponent and assign

x **= 3

x = x ** 3

:=

Assignment expression (walrus operator)

total := sum(data)

assign sum(data) to total

5. Bitwise

&

Bitwise AND

5 & 3

1

|

Bitwise OR

5 | 3

7

^

Bitwise XOR

5 ^ 3

6

~

Bitwise NOT

~5

-6

<<

Left shift

5 << 1

10

>>

Right shift

5 >> 1

2

6. Membership

in

Member of

'a' in 'cat'

True

not in

Not member of

'x' not in 'cat'

True

7. Identity

is

Same object

x is y

Varies

is not

Different object

x is not y

Varies

23.3.2. Operator Precedence#

Operator precedence determines the order in which operations are evaluated in an expression. Operations with higher precedence are performed before those with lower precedence. Use parentheses () to override the default precedence.

Precedence Level

Operator(s)

Description / Example

1 (Highest)

()

Parentheses — control order of evaluation

2

**

Exponentiation

3

+x, -x, ~x

Unary plus, unary minus, bitwise NOT

4

*, /, //, %

Multiplication, division, floor division, modulus

5

+, -

Addition, subtraction

6

<<, >>

Bitwise left and right shift

7

&

Bitwise AND

8

^, |

Bitwise XOR, bitwise OR

9

Comparison: <, <=, >, >=, !=, ==

Relational and equality checks

10

is, is not, in, not in

Identity and membership operators

11

not

Logical NOT

12

and

Logical AND

13

or

Logical OR

14 (Lowest)

Assignment: =, +=, -=, *=, /=, //=, %= , **=

Assignment and augmented assignment

Note:

  • Always use parentheses when precedence is unclear to improve code readability

  • Exponentiation (**) is evaluated right-to-left: 2 ** 3 ** 2 equals 2 ** 9 = 512

  • Comparison operators all have the same precedence and are evaluated left-to-right

  • Logical operators follow the order: notandor

  • When operators have the same precedence, they are typically evaluated left-to-right (left-associative), except for exponentiation which is right-associative.

### example of operator precedence

x = 1
y = 2
z = 3

result = x + y * z ** 2
print(result)               ### output: 19
19

23.3.3. Arithmetic operators#

An arithmetic operator is a symbol that represents an arithmetic computation. For example, the plus sign, +, performs addition.

30 + 12
42

The minus sign, -, is the operator that performs subtraction.

43 - 1
42

The asterisk, *, performs multiplication.

6 * 7
42

And the forward slash, /, performs division:

84 / 2
42.0

Notice that the result of the division is 42.0 rather than 42. That’s because they are two types of numbers:

  • integers, which represent numbers with no fractional or decimal part, and

  • floating-point numbers, which represent integers and numbers with a decimal point.

If you add, subtract, or multiply two integers, the result is an integer. But if you divide two integers, the result is a floating-point number. Python provides another operator, //, that performs integer/floor division. The result of integer division is always an integer.

84 // 2
42

Integer division is also called “floor division” because it always rounds down (toward the “floor”).

85 // 2
42

Finally, the operator ** performs exponentiation; that is, it raises a number to a power:

7 ** 2
49

In some other languages, the caret, ^, is used for exponentiation, but in Python it is a bitwise operator called XOR. If you are not familiar with bitwise operators, the result might be unexpected:

7 ^ 2
5

23.3.4. Bitwise Operations#

Bitwise operations are used for low-level programming tasks that require efficient manipulation of individual bits, such as optimizing arithmetic operations, flagging file permissions, and hashing.

For an example of Bitwise operations, let’s take a look at Bitwise AND(&). The bitwise AND operation returns 1 only if both bits are 1, as seen in the Truth Table below.

Truth Table:

A

B

A & B

0

0

0

0

1

0

1

0

0

1

1

1

a = 5           ### binary: 0101
b = 3           ### binary: 0011

result = a & b  ### 0001 = 1

print(f"{a} & {b} = {result}")   ### 5 & 3 = 1
print(f"binary: {bin(result)}")  ### binary: 0b1 (0b/0B means Binary or Base 2)
5 & 3 = 1
binary: 0b1

As another example, we can use the bitwise operation to check if a number is even:

def is_even(n):
    return (n & 1) == 0

23.3.5. Arithmetic functions#

In addition to the arithmetic operators, Python provides a few functions that work with numbers. For example, the round function takes a floating-point number and rounds it off to the nearest integer.

round(42.4)
42
round(42.6)
43

The abs function computes the absolute value of a number. For a positive number, the absolute value is the number itself.

abs(42)
42

For a negative number, the absolute value is positive.

abs(-42)
42

When we use a function like this, we say we’re calling the function. An expression that calls a function is a function call.

When you call a function, the parentheses are required. If you leave them out, you get an error message.

NOTE: The following cell uses %%expect, which is a Jupyter “magic command” that means we expect the code in this cell to produce an error. For more on this topic, see the Jupyter notebook introduction .

%%expect SyntaxError

abs 42
UsageError: Cell magic `%%expect` not found.

You can ignore the first line of this message; it doesn’t contain any information we need to understand right now. The second line is the code that contains the error, with a caret (^) beneath it to indicate where the error was discovered.

The last line indicates that this is a syntax error, which means that there is something wrong with the structure of the expression. In this example, the problem is that a function call requires parentheses.

Let’s see what happens if you leave out the parentheses and the value.

abs
<function abs(x, /)>

A function name all by itself is a legal expression that has a value. When it’s displayed, the value indicates that abs is a function, and it includes some additional information I’ll explain later.

23.3.6. Strings#

In addition to numbers, Python can also represent sequences of letters, which are called strings because the letters are strung together like beads on a necklace. To write a string, we can put a sequence of letters inside straight quotation marks.

'Hello'
'Hello'

It is also legal to use double quotation marks.

"world"
'world'

Double quotes make it easy to write a string that contains an apostrophe, which is the same symbol as a straight quote.

"it's a small "
"it's a small "

Strings can also contain spaces, punctuation, and digits.

'Well, '
'Well, '

The + operator works with strings; it joins two strings into a single string, which is called concatenation

'Well, ' + "it's a small " + 'world.'
"Well, it's a small world."

The * operator also works with strings; it makes multiple copies of a string and concatenates them.

'Spam, ' * 4
'Spam, Spam, Spam, Spam, '

The other arithmetic operators don’t work with strings.

Python provides a function called len that computes the length of a string.

len('Spam')
4

Notice that len counts the letters between the quotes, but not the quotes.

When you create a string, be sure to use straight quotes. The back quote, also known as a backtick, causes a syntax error.

`Hello`

Cell In[27], line 1
    `Hello`
    ^
SyntaxError: invalid syntax

Smart quotes, also known as curly quotes, are also illegal.

‘Hello’

  Cell In[28], line 1
    ‘Hello’
    ^
SyntaxError: invalid character '‘' (U+2018)

23.4. Data Types#

23.4.1. Values and Types#

So far we’ve seen three kinds of values:

  • 2 is an integer,

  • 42.0 is a floating-point number, and

  • 'Hello' is a string.

A kind of value is called a type. Every value has a type – or we sometimes say it “belongs to” a type.

Python provides a function called type that tells you the type of any value. The type of an integer is int.

type(2)
int

The type of a floating-point number is float.

type(42.0)
float

And the type of a string is str.

type('Hello, World!')
str

The types int, float, and str can be used as functions. For example, int can take a floating-point number and convert it to an integer (always rounding down).

int(42.9)
42

And float can convert an integer to a floating-point value.

float(42)
42.0

Now, here’s something that can be confusing. What do you get if you put a sequence of digits in quotes?

'126'
'126'

It looks like a number, but it is actually a string.

type('126')
str

If you try to use it like a number, you might get an error.

%%expect TypeError

'126' / 3
UsageError: Cell magic `%%expect` not found.

This example generates a TypeError, which means that the values in the expression, which are called operands, have the wrong type. The error message indicates that the / operator does not support the types of these values, which are str and int.

If you have a string that contains digits, you can use int to convert it to an integer.

int('126') / 3
42.0

If you have a string that contains digits and a decimal point, you can use float to convert it to a floating-point number.

float('12.6')
12.6

When you write a large integer, you might be tempted to use commas between groups of digits, as in 1,000,000. This is a legal expression in Python, but the result is not an integer.

1,000,000
(1, 0, 0)

Python interprets 1,000,000 as a comma-separated sequence of integers. We’ll learn more about this kind of sequence later.

You can use underscores to make large numbers easier to read.

1_000_000
1000000

23.4.2. Built-in Data Types#

Data types define the kind of values you work with. Python has standard types built into the interpreter. The commonly used ones are:

No.

Category

Types

Description

1

Numeric Types

int, float, complex

Integer, floating-point, and complex numbers

2

Boolean Type

bool

True/False logic

3

Sequence Types

list, tuple, range

Ordered, mutable/immutable collections; nesting supported

4

Text Sequence

str

Text that supports indexing and slicing

5

Binary Sequence

bytes, bytearray,

Binary data

memoryview

6

Set Types

set, frozenset

Unordered collections of unique elements

7

Mapping Type

dict

Key-value pairs

8

None Type

NoneType

Represents absence of value

../../_images/python-data-types-2.png

Fig. 23.1 Python built-in data types #

23.4.2.1. Numbers#

Python has three basic number types: the integer (e.g., 1), the floating point number (e.g., 1.0), and the complex number. Standard mathematical order of operation is followed for basic arithmetic operations. Note that dividing two integers results in a floating point number and dividing by zero will generate an error.

print(1 + 1)
print(1 * 3)         
print(1 / 2)
print(2 / 2)             ### output 1.0, not 1
print(2 ** 4)       
2
3
0.5
1.0
16

The modulus operation (also known as the mod function) is represented by the percent sign. It returns what remains after the division:

print(4 % 2)
print(5 % 2)
print(9 // 2)
0
1
4

23.4.2.2. String Sequences#

Strings can be created using single or double quotes. You can also wrap double quotes around single quotes if you need to include a quote inside the string.

print('hello')
print("hello")
print("I can't go")
print('I can\'t go')     ### escape sequence: \
hello
hello
I can't go
I can't go

Indexing and Slicing Strings

Strings are sequences of characters. You can access specific elements using square bracket notation. Python indexing starts at zero. Negative indexing in slicing starts with -1 from the end element.

s = 'hello'
print(s[0])
print(s[4])
print(s[-1])
h
o
o

Slice notation allows you to grab parts of a string. Use a colon to specify the start and stop indices. The stop index is not included.

s = 'applebananacherry'
print(s[0:])     ### applebananacherry
print(s[:5])     ### apple
print(s[5:11])   ### banana
print(s[-6:])    ### cherry
print(s[-6:0])   ### 
print(s[-6:-1])  ### cherr (stop exclusive)
applebananacherry
apple
banana
cherry

cherr

23.4.2.3. Sequence: list#

Lists, just like tuple and range, are sequences of elements (similar to the array in many other languages) in square brackets, separated by commas.

my_list = ['a', 'b', 'c']
my_list.append('d')
my_list[0]
my_list[1:3]
my_list[0] = 'NEW'

Lists can contain any data type, including other lists (nested). You can access elements using list indexing. For nested lists, use chaining/stacking square brackets.

lst = [1, 2, [3, 4]]
lst[2][1]             ### 4

nest = [1, 2, 3, [4, 5, ['target']]]
nest[3][2][0]                         ### 'target'
print(nest[3][2][0])                  ### target
print(nest[3][2][0][0])               ### t (a list is a sequence)
target
t

23.4.2.4. Mapping: dictionary#

In Python, a mapping type is a collection that stores data as key–value pairs, where each key is unique and maps to a corresponding value. The most common mapping type is the dictionary (dict), which allows fast lookup, insertion, and modification of values using their keys rather than numerical indexes. An example of a Python dictionary:

student = {
    "name": "Ava",
    "age": 20,
    "major": "Computer Science",
    "is_enrolled": True,
    "courses": [ "Python", "Data Structures", "Calculus" ]
}

student
{'name': 'Ava',
 'age': 20,
 'major': 'Computer Science',
 'is_enrolled': True,
 'courses': ['Python', 'Data Structures', 'Calculus']}

23.4.3. Type Casting#

A number of the built-in functions are constructor functions that can perform type casting. For example:

### literals: 1, 2.8, "3"
print(type(1), type(2.8), type("3"))

x = int(1)   # x will be 1
y = int(2.8) # y will be 2
z = int("3") # z will be 3

print(x, y, z)
print(type(x), type(y), type(z))
<class 'int'> <class 'float'> <class 'str'>
1 2 3
<class 'int'> <class 'int'> <class 'int'>

23.5. 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 importing anything. They are automatically available in every Python program.

Python built-in functions are tools for quick operation (length, convert, output). They can be grouped by their purposes as:

Group

Functions

Notes

Numbers & math

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

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

Type constructors/conversions

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

Convert or construct core types.

Object/attribute introspection

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

vars(obj)obj.__dict__ when available.

Attribute access

getattr, setattr, delattr, hasattr

Dynamic attribute management.

Iteration & functional tools

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

Prefer comprehensions when clearer.

Sequence/char helpers

len, ord, chr, slice

len() works on many containers.

I/O

print, input, open

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

Formatting / representation

format, bin, oct, hex

Also see f-strings for formatting.

Object model (OOP helpers)

object, property, classmethod, staticmethod, super

Define descriptors and class behaviors.

Execution / metaprogramming

compile, eval, exec

Use with care; security concerns for untrusted input.

Environment / namespaces

globals, locals

Introspection of current namespaces.

Help/debugging

help, breakpoint

breakpoint() respects PYTHONBREAKPOINT.

Import

__import__

Low-level import; usually use import statement instead.

23.6. Modules#

A module is a collection of variables and functions. Built-in modules are part of the Python Standard Library, but you must import them before use. They provide extra features: math, dates, OS access, file utilities, random numbers, etc.

To use a variable in a module, you have to use the dot operator (.) between the name of the module and the name of the variable.

For example, the Python math module provides a variable called pi that contains the value of the mathematical constant denoted \(\pi\). We can display its value like math.pi:

import math

print(math.pi)
math.pi
3.141592653589793
3.141592653589793

The math module also contains functions. For example, sqrt computes square roots:

math.sqrt(25)
5.0

And the pow function raises one number to the power of a second number.

math.pow(5, 2)
25.0

At this point we’ve seen two ways to raise a number to a power: we can use the math.pow function or the exponentiation operator, **. Either one is fine, but the operator is used more often than the function.

To see a list of all the Python built-in modules, you can run help('modules') in Python shell or Jupyter Notebook:

help('modules')

Please wait a moment while I gather a list of all available modules...

IPython             alabaster           itertools           rlcompleter
PIL                 antigravity         jedi                rpds
__future__          anyio               jinja2              runpy
__hello__           appnope             json                sched
__phello__          argon2              json5               secrets
_abc                argparse            jsonpointer         select
...
...

23.7. Debugging#

Programmers make mistakes. For whimsical reasons, programming errors are called bugs and the process of tracking them down is called debugging.

Programming, and especially debugging, sometimes brings out strong emotions. If you are struggling with a difficult bug, you might feel angry, sad, or embarrassed.

Preparing for these reactions might help you deal with them. One approach is to think of the computer as an employee with certain strengths, like speed and precision, and particular weaknesses, like lack of empathy and inability to grasp the big picture.

Your job is to be a good manager: find ways to take advantage of the strengths and mitigate the weaknesses. And find ways to use your emotions to engage with the problem, without letting your reactions interfere with your ability to work effectively.

Learning to debug can be frustrating, but it is a valuable skill that is useful for many activities beyond programming. At the end of each chapter there is a section, like this one, with my suggestions for debugging. I hope they help!

23.8. Glossary#

arithmetic operator A symbol, like + and *, that denotes an arithmetic operation like addition or multiplication.

integer A type that represents numbers with no fractional or decimal part.

floating-point: A type that represents integers and numbers with decimal parts.

integer division An operator, //, that divides two numbers and rounds down to an integer.

expression A combination of variables, values, and operators.

value An integer, floating-point number, or string – or one of other kinds of values we will see later.

function A named sequence of statements that performs some useful operation. Functions may or may not take arguments and may or may not produce a result.

function call An expression – or part of an expression – that runs a function. It consists of the function name followed by an argument list in parentheses.

syntax error An error in a program that makes it impossible to parse – and therefore impossible to run.

string A type that represents sequences of characters.

concatenation Joining two strings end-to-end.

type A category of values. The types we have seen so far are integers (type int), floating-point numbers (type float), and strings (type str).

operand One of the values on which an operator operates.

natural language Any of the languages that people speak that evolved naturally.

formal language Any of the languages that people have designed for specific purposes, such as representing mathematical ideas or computer programs. All programming languages are formal languages.

bug An error in a program.

debugging The process of finding and correcting errors.