6.2. Commonly Confused Terms

Technical jargon is confusing enough, especially for terms that have related but distinct definitions. To make matters worse, languages, operating systems, and fields in computing might use different terms to mean the same thing or the same terms to mean different things. To communicate clearly with other pro- grammers, you’ll need to learn the difference between the following terms. ## Statements vs. Expressions Expressions are the instructions made up of operators and values that evalu- ate to a single value. A value can be a variable (which contains a value) or a function call (which returns a value). So, 2 + 2 is an expression that evalu- ates down to the single value of 4 . But len(myName) > 4 and myName.isupper() or myName == ‘Zophie’ are expressions as well. A value by itself is also an expres- sion that evaluates to itself.

Statements are, effectively, all other instructions in Python. These include if statements, for statements, def statements, return statements, and so on. Statements do not evaluate to a value. Some statements can include expres- sions, such as an assignment statement like spam = 2 + 2 or an if statement like if myName == ‘Zophie’: .

Although Python 3 uses a print() function, Python 2 instead has a print statement. The difference might seem like just the introduction of parenthe- ses, but it’s important to note that the Python 3 print() function has a return value (which is always None ), can be passed as an argument to other functions, and can be assigned to a variable. None of these actions are possible with statements. However, you can still use the parentheses in Python 2, as in the following interactive shell example:

print 'Hello, world!' # run in Python 2

>>> print('Hello, world!') # run in Python 2
Hello, world!

Although this looks like a function call 1, it’s actually a print statement with a string value wrapped in parentheses, the same way assigning spam = (2 + 2) is equivalent to spam = 2 + 2 . In Python 2 and 3, you can pass multi- ple values to the print statement or print() function, respectively. In Python 3, this would look like the following:

>>> print('Hello', 'world') # run in Python 3
Hello world

But using this same code in Python 2 would be interpreted as passing a tuple of two string values in a print statement, producing this output:

>>>  print('Hello', 'world') # run in Python 2
Hello world

A statement and an expression composed of a function call have subtle but real differences. ## Block vs. Clause vs. Body The terms block, clause, and body are often used interchangeably to refer to a group of Python instructions. A block begins with indentation and ends when that indentation returns to the previous indent level. For example, the code that follows an if or for statement is called the statement’s block. A new block is required following statements that end with a colon, such as if , else , for , while , def , class , and so on.

But Python does allow one-line blocks. This is valid, although not rec- ommended, Python syntax:

>>> name= 'Zophie'
>>> if name == 'Zophie': print('Hello, kitty!')
Hello, kitty!

By using the semicolon, you can also have multiple instructions in the if statement’s block:

>>> if name == 'Zophie': print('Hello, kitty!'); print('Do you want a treat?')
Hello, kitty!
Do you want a treat?

But you can’t have one-liners with other statements that require new blocks. The following isn’t valid Python code:

if name == 'Zophie': if age < 2: print('Hello, kitten!')

This is invalid because if an else statement is on the next line, it would be ambiguous as to which if statement the else statement would refer to.

The official Python documentation prefers the term clause rather than block (https://docs.python.org/3/reference/compound_stmts.html). The following code is a clause:

>>> if name == 'Zophie':
>>>     print('Hello, kitty!')
>>>     print('Do you want a treat?')
Hello, kitty!
Do you want a treat?

The if statement is the clause header, and the two print() calls nested in the if are the clause suite or body. The official Python documentation uses block to refer to a piece of Python code that executes as a unit, such as a module, a function, or a class definition (https://docs.python.org/3/reference/ executionmodel.html). ## Variable vs. Attribute Variables are simply names that refer to objects. Attributes are, to quote the official documentation, “any name following a dot” (https://docs.python.org/3/ tutorial/classes.html#python-scopes-and-namespaces). Attributes are associated with objects (the name before the dot/period). For example, enter the fol- lowing into the interactive shell:

>>> import datetime
>>> spam = datetime.datetime.now()
>>> spam.year
2022
>>> spam.month
8

In this code example, spam is a variable that contains a datetime object (returned from datetime.datetime.now() ), and year and month are attributes of that object. Even in the case of, say, sys.exit() , the exit() function is consid- ered an attribute of the sys module object.

Other languages call attributes fields, properties, or member variables. ## Function vs. Method A function is a collection of code that runs when called. A method is a function (or a callable, described in the next section) that is associated with a class, just as an attribute is a variable associated with an object. Functions include built-in functions or functions associated with a module. For example, enter the following into the interactive shell:

>>> len('Hello')
5
>>> 'Hello'.upper()
'HELLO'
>>> import math
>>> math.sqrt(25)
5.0

In this example, len() is a function and upper() is a string method. Methods are also considered attributes of the objects they’re associated with. Note that a period doesn’t necessarily mean you’re dealing with a method instead of a function. The sqrt() function is associated with math , which is a module, not a class. ## Iterable vs. Iterator Python’s for loops are versatile. The statement for i in range(3): will run a block of code three times. The range(3) call isn’t just Python’s way of telling a for loop, “repeat some code three times.” Calling range(3) returns a range object, just like calling list(‘cat’) returns a list object. Both of these objects are examples of iterable objects (or simply, iterables).

You use iterables in for loops. Enter the following into the interactive shell to see a for loop iterate over a range object and a list object:

>>> for i in range(3):
>>>
>>>     print(i) # body of the for loop
0
1
2
>>>  for i in ['c', 'a', 't']:
>>>
>>>     print(i) # body of the for loop
c
a
t

Iterables also include all sequence types, such as range, list, tuple, and string objects, but also some container objects, such as dictionary, set, and file objects.

However, more is going on under the hood in these for loop examples. Behind the scenes, Python is calling the built-in iter() and next() functions for the for loop. When used in a for loop, iterable objects are passed to the built-in iter() function, which returns iterator objects. Although the iterable object contains the items, the iterator object keeps track of which item is next to be used in a loop. On each iteration of the loop, the iterator object is passed to the built-in next() function to return the next item in the iter- able. We can call the iter() and next() functions manually to directly see how for loops work. Enter the following into the interactive shell to perform the same instructions as the previous loop example:

>>> iterableObj = range(3)
>>> iterableObj
range(0, 3)
>>> iteratorObj = iter(iterableObj)
>>> i = next(iteratorObj)
>>> print(i) # body of the for loop
0
>>> i = next(iteratorObj)
>>> print(i) # body of the for loop
1
>>> i = next(iteratorObj)
>>> print(i) # body of the for loop
2
>>> i = next(iteratorObj)
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
1 StopIteration

Notice that if you call next() after the last item in the iterable has been returned, Python raises a StopIteration exception 1. Instead of crashing your programs with this error message, Python’s for loops catch this excep- tion to know when they should stop looping.

An iterator can only iterate over the items in an iterable once. This is similar to how you can only use open() and readlines() to read the contents of a file once before having to reopen the file to read its contents again. If you want to iterate over the iterable again, you must call iter() again to cre- ate another iterator object. You can create as many iterator objects as you want; each will independently track the next item it should return. Enter the following into the interactive shell to see how this works:

>>> iterableObj = list('cat')
>>> iterableObj
['c', 'a', 't']
>>> iteratorObj1 = iter(iterableObj)
>>> iteratorObj2 = iter(iterableObj)
>>> next(iteratorObj1)
'c'
>>> next(iteratorObj1)
'a'
>>> next(iteratorObj2)
'c'

Remember that iterable objects are passed as an argument to the iter() function, whereas the object returned from iter() calls is an itera- tor object. Iterator objects are passed to the next() function. When you create your own data types with class statements, you can implement the iter() and next() special methods to use your objects in for loops. ## Syntax vs. Runtime vs. Semantic Errors There are many ways to categorize bugs. But at a high level you could divide programming errors into three types: syntax errors, runtime errors, and semantic errors.

Syntax is the set of rules for the valid instructions in a given program- ming language. A syntax error, such as a missing parenthesis, a period instead of a comma, or some other typo will immediately generate a SyntaxError . Syntax errors are also known as parsing errors, which occur when the Python interpreter can’t parse the text of the source code into valid instructions. In English, this error would be the equivalent of having incorrect grammar or a string of nonsense words like, “by uncontaminated cheese certainly it’s.” Computers require specific instructions and can’t read the programmer’s mind to determine what the program should do, so a program with a syntax error won’t even run.

A runtime error is when a running program fails to perform some task, such as trying to open a file that doesn’t exist or dividing a number by zero. In English, a runtime error is the equivalent of giving an impossible instruction like, “Draw a square with three sides.” If a runtime error isn’t addressed, the program will crash and display a traceback. But you can catch runtime errors using try - except statements that run error handling code. For example, enter the following into the interactive shell:

>>> slices = 8
>>> eaters = 0
>>> print('Each person eats', slices / eaters, 'slices.')
---------------------------------------------------------------------------

ZeroDivisionError                         Traceback (most recent call last)

<ipython-input-22-a3f3c81ce68a> in <module>
      1 slices = 8
      2 eaters = 0
----> 3 print('Each person eats', slices / eaters, 'slices.')


ZeroDivisionError: division by zero

This code will display this traceback when you run it:

Traceback (most recent call last):
File "<pyshell#4>", line 1, in <module>

print('Each person eats', slices / eaters, 'slices.')

ZeroDivisionError: division by zero

It’s helpful to remember that the line number the traceback mentions is only the point at which the Python interpreter detected an error. The true cause of the error might be on the previous line of code or even much ear- lier in the program.

Syntax errors in the source code are caught by the interpreter before the program runs, but syntax errors can also happen at runtime. The eval() function can take a string of Python code and run it, which might produce a SyntaxError at runtime. For example, eval(‘print(“Hello, world)’) is missing a closing double quote, which the program won’t encounter until the code calls eval() .

A semantic error (also called a logical error) is a more subtle bug. Semantic errors won’t cause error messages or crashes, but the computer carries out instructions in a way the programmer didn’t intend. In English, the equiva- lent of a semantic error would be telling the computer, “Buy a carton of milk from the store and if they have eggs, buy a dozen.” The computer would then buy 13 cartons of milk because the store had eggs. For better or worse, com- puters do exactly what you tell them to. For example, enter the following into the interactive shell:

>>> print('The sum of 4 and 2 is', '4' + '2')

You would get the following output:

The sum of 4 and 2 is 42

Obviously, 42 isn’t the answer. But notice that the program didn’t crash. Because Python’s + operator adds integer values and concatenates string values, mistakenly using the string values ‘4’ and ‘2’ instead of integers caused unintended behavior. ## Parameters vs. Arguments Parameters are the variable names between the parentheses in a def state- ment. Arguments are the values passed in a function call, which are then assigned to the parameters. For example, enter the following into the inter- active shell:

>>> def greeting(name, species):
>>>     print(name + ' is a ' + species)
>>> greeting('Zophie', 'cat')

In the def statement, name and species are parameters 1. In the func- tion call, ‘Zophie’ and ‘cat’ are arguments 2. These two terms are often confused with each other. Remember that parameters and arguments are just other names for variables and values, respectively, when they are used in this context. ## Type Coercion vs. Type Casting You can convert an object of one type to an object of another type. For example, int(‘42’) converts a string ‘42’ to an integer 42 . In actuality, the string object ‘42’ isn’t converted so much as the int() function creates a new integer object based on the original object. When conversion is done explic- itly like this, we’re casting the object, although programmers often still refer to this process as converting the object.

Python will often implicitly do a type conversion, such as when evaluat- ing the expression 2 + 3.0 to 5.0 . Values, such as the 2 and 3.0 , are coerced to a common data type that the operator can work with. This conversion, which is done implicitly, is called type coercion.

Coercion can sometimes lead to surprising results. The Boolean True and False values in Python can be coerced to the integer values 1 and 0 , respectively. Although you’d never write Booleans as those values in real- world code, this means that the expression True + False + True is the equiva- lent of 1 + 0 + 1 and evaluates to 2 . After learning this, you might think that passing a list of Booleans to sum() would be a good way to count the number of True values in a list. But it turns out that calling the count() list method is faster. ## Properties vs. Attributes In many languages, the terms property and attribute are used synonymously, but in Python these words have distinct meanings. An attribute, explained in “Variable vs. Attribute” on page 124, is a name associated with an object. Attributes include the object’s member variables and methods.

Other languages, such as Java, have getter and setter methods for classes. Instead of being able to directly assign an attribute a (potentially invalid) value, a program must call the setter method for that attribute. The code inside the setter method can ensure that the member variable only has a valid value assigned to it. The getter method reads an attribute’s value. If an attribute is named, say, accountBalance , the setter and getter methods are usually named setAccountBalance() and getAccountBalance() , respectively.

In Python, properties allow programmers to use getters and setters with much cleaner syntax. Chapter 17 explores Python properties in more detail. ## Bytecode vs. Machine Code Source code is compiled into a form of instructions called machine code that the CPU directly carries out. Machine code is composed of instructions from the CPU’s instruction set, the computer’s built-in set of commands. A compiled program composed of machine code is called a binary. A ven- erable language like C has compiler software that can compile C source code into binaries for almost every CPU available. But if a language such as Python wants to run on the same set of CPUs, a large amount of work would have to go into writing Python compilers for each of them.

There is another way of turning source code into machine-usable code. Instead of creating machine code that is carried out directly by CPU hard- ware, you could create bytecode. Also called portable code or p-code, bytecode is carried out by a software interpreter program instead of directly by the CPU. Python bytecode is composed of instructions from an instruction set, although no real-world hardware CPU carries out these instructions. Instead, the software interpreter executes the bytecode. Python bytecode is stored in the .pyc files you sometimes see alongside your .py source files. The CPython interpreter, which is written in C, can compile Python source code into Python bytecode and then carry out the instructions. (The same goes for the Java Virtual Machine [JVM] software, which carries out Java byte- code.) Because it’s written in C, CPython has a Python interpreter and can be compiled for any CPU that C already has a compiler for.

The PyCon 2016 talk, “Playing with Python Bytecode” by Scott Sanderson and Joe Jevnik, is an excellent resource to learn more about this topic (https:// youtu.be/mxjv9KqzwjI). ## Script vs. Program, Scripting Language vs. Programming Language The differences between a script and a program, or even a scripting lan- guage and a programming language, are vague and arbitrary. It’s fair to say that all scripts are programs and all scripting languages are programming languages. But scripting languages are sometimes regarded as easier or “not real” programming languages.

One way to distinguish scripts from programs is by how the code executes. Scripts written in scripting languages are interpreted directly from the source code, whereas programs written in programming languages are compiled into binaries. But Python is commonly thought of as a script- ing language, even though there is a compilation step to bytecode when a Python program is run. Meanwhile, Java isn’t commonly thought of as a scripting language, even though it produces bytecode instead of machine code binaries, just like Python. Technically, languages aren’t compiled or interpreted; rather, there are compiler or interpreter implementations of a language, and it’s possible to create a compiler or interpreter for any language.

The differences can be argued but ultimately aren’t very important. Scripting languages aren’t necessarily less powerful, nor are compiled programming languages more difficult to work with. ## Library vs. Framework vs. SDK vs. Engine vs. API Using other people’s code is a great time-saver. You can often find code to use packaged as libraries, frameworks, SDKs, engines, or APIs. The differ- ences between these entities are subtle but important.

A library is a generic term for a collection of code made by a third party. A library can contain functions, classes, or other pieces of code for a developer to use. A Python library might take the form of a package, a set of packages, or even just a single module. Libraries are often specific to a particular language. The developer doesn’t need to know how the library code works; they only need to know how to call or interface with the code in a library. A standard library, such as the Python standard library, is a code library that is assumed to be available to all implementations of a program- ming language.

A framework is a collection of code that operates with inversion of control; the developer creates functions that the framework will call as needed, as opposed to the developer’s code calling functions in the framework. Inversion of control is often described as “don’t call us, we’ll call you.” For example, writing code for a web app framework involves creating functions for the web pages that the framework will call when a web request comes in. A software development kit (SDK) includes code libraries, documenta- tion, and software tools to assist in creating applications for a particular operating system or platform. For example, the Android SDK and iOS SDK are used to create mobile apps for Android and iOS, respectively. The Java Development Kit (JDK) is an SDK for creating applications for the JVM. An engine is a large, self-contained system that can be externally con- trolled by the developer’s software. Developers usually call functions in an engine to perform a large, complex task. Examples of engines include game engines, physics engines, recommendation engines, database engines, chess engines, and search engines.

An application programming interface (API) is the public-facing interface for a library, SDK, framework, or engine. The API specifies how to call the functions or make requests of the library to access resources. The library cre- ators will (hopefully) make documentation for the API available. Many pop- ular social networks and websites make an HTTP API available for programs to access their services rather than a human with a web browser. Using these APIs allows you to write programs that can, for example, automatically post on Facebook or read Twitter timelines.