6.1. Definitions

As the number of programmers in a room approaches two, the likelihood of an argument about semantics approaches 100 percent. Language is fluid and humans are the masters of words rather than the other way around. Some developers might use terms slightly differently, but becoming familiar with these terms is still useful. This chapter explores these terms and how they compare with each other. If you need a glossary of terms in alphabetical order, you can rely on the official Python glossary at https://docs.python.org/3/ glossary.html to provide canonical definitions.

No doubt, some programmers will read the definitions in this chapter and bring up special cases or exceptions that can be endlessly nitpicked. Rather than being a definitive guide, this chapter is intended to give you accessible definitions, even if they’re not comprehensive. As with everything in programming, there’s always more to learn. ## Python the Language and Python the Interpreter The word python can have multiple meanings. The Python programming language gets its name from the British comedy group Monty Python, rather than the snake (although Python tutorials and documentation use both Monty Python and snake references). Similarly, Python can have two meanings in regard to computer programming.

When we say, “Python runs a program” or “Python will raise an excep- tion,” we’re talking about the Python interpreter—the actual software that reads the text of a .py file and carries out its instructions. When we say, “the Python interpreter,” we’re almost always talking about CPython, the Python interpreter maintained by the Python Software Foundation, avail- able at https://www.python.org. CPython is an implementation of the Python language—that is, software created to follow a specification—but there are others. Although CPython is written in the C programming language, Jython is written in Java for running Python scripts that are interoperable with Java programs. PyPy, a just-in-time compiler for Python that compiles as programs execute, is written in Python.

All of these implementations run source code written in the Python pro- gramming language, which is what we mean when we say, “This is a Python program” or “I’m learning Python.” Ideally, any Python interpreter can run any source code written in the Python language; however, in the real world there’ll be some slight incompatibilities and differences between interpret- ers. CPython is called the Python language’s reference implementation because if there’s a difference between how CPython and another interpreter inter- pret Python code, CPython’s behavior is considered canonical and correct. ## Garbage Collection In many early programming languages, a programmer had to instruct the program to allocate and then deallocate, or free, memory for data struc- tures as needed. Manual memory allocation was the source of numerous bugs, such as memory leaks (where programmers forgot to free memory) or double-free bugs (where programmers freed the same memory twice, leading to data corruption).

To avoid these bugs, Python has garbage collection, a form of automatic memory management that tracks when to allocate and free memory so the programmer doesn’t have to. You can think of garbage collection as memory recycling, because it makes memory available for new data. For example, enter the following into the interactive shell:

>>> def someFunction():
>>>     print('someFunction() called.')
>>>     spam = ['cat', 'dog', 'moose']
>>>
>>> someFunction()
someFunction() called.

When someFunction() is called, Python allocates memory for the list [‘cat’, ‘dog’, ‘moose’] . The programmer doesn’t need to figure out how many bytes of memory to request because Python manages this automati- cally. Python’s garbage collector will free the local variables when the func- tion call returns to make that memory available for other data. Garbage collection makes programming much easier and less bug-prone. ## Literals A literal is text in the source code for a fixed, typed-out value. In the follow- ing code example

>>> age = 42 + len('Zophie')

the 42 and ‘Zophie’ text are integer and string literals. Think of a literal as a value that literally appears in source code text. Only the built-in data types can have literal values in Python source code, so the variable age isn’t a lit- eral value. Table 7-1 lists some example Python literals.

Table 7-1: Examples of Literals in Python

Literal

Data type

42

Integer

3.14

Float

1.4886191506362924e+36

Float

“““Howdy!”“”

String

r’Green:raw-latex:Blue

String

[]

List

{‘name’: ‘Zophie’}

Dictionary

b’:raw-latex:`\x41`

Bytes

True

Boolean

None

NoneType

Nitpickers will argue that some of my choices aren’t literals based on the official Python language documentation. Technically, -5 isn’t a literal in Python because the language defines the negative symbol ( - ) as an opera- tor that operates on the 5 literal. In addition, True , False , and None are con- sidered Python keywords rather than literals, whereas [] and {} are called displays or atoms depending on what part of the official documentation you’re looking at. Regardless, literal is a common term that software profes- sionals will use for all of these examples.

6.1.1. Keywords

Every programming language has its own keywords. The Python keywords are a set of names reserved for use as part of the language and cannot be used as variable names (that is, as identifiers). For example, you cannot have a variable named while because while is a keyword reserved for use in while loops. The following are the Python keywords as of Python 3.9.

and

continue

finally

is

raise

as

def

for

lambda

return

assert

del

from

None

True

async

elif

global

nonlocal

try

await

else

if

not

while

break

except

import

or

with

class

False

in

pass

yield

Note that the Python keywords are always in English and aren’t availablein alternative languages.For example, the following function has identifierswritten in Spanish, but the def and return keywords remain in English.

>>> def agregarDosNúmeros(primerNúmero, segundoNúmero):
>>>     return primerNúmero + segundoNúmero

Unfortunately for the 6.5 billion people who don’t speak it, English dominates the programming field. ## Objects, Values, Instances, and Identities An object is a representation of a piece of data, such as a number, some text, or a more complicated data structure, such as a list or dictionary. All objects can be stored in variables, passed as arguments to function calls, and returned from function calls.

All objects have a value, identity, and data type. The value is the data the object represents, such as the integer 42 or the string ‘hello’ . Although somewhat confusing, some programmers use the term value as a synonym for object, especially for simple data types like integers or strings. For example, a variable that contains 42 is a variable that contains an integer value, but we can also say it’s a variable that contains an integer object with a value of 42 .

An object is created with an identity that is a unique integer you can view by calling the id() function. For example, enter the following code into the interactive shell:

>>> spam = ['cat', 'dog', 'moose']
>>> id(spam)
140025268202688

The variable spam stores an object of the list data type. Its value is [‘cat’, ‘dog’, ‘moose’] . Its identity is 33805656 , although the integer ID varies each time a program runs so you’ll likely get a different ID on your computer. Once created, an object’s identity won’t change for as long as the program runs. Although the data type and the object’s identity will never change, an object’s value can change, as we’ll see in this example:

>>> spam.append('snake')
>>> spam
['cat', 'dog', 'moose', 'snake']
>>> ['cat', 'dog', 'moose', 'snake']
>>> id(spam)
140025268202688

Now the list also contains ‘snake’ . But as you can see from the id(spam) call, its identity hasn’t changed and it’s still the same list. But let’s see what happens when you enter this code:

>>> spam = [1, 2, 3]
>>> id(spam)
140025267702528

The value in spam has been overwritten by a new list object with a new identity: 33838544 instead of 33805656 . An identifier like spam isn’t the same as an identity because multiple identifiers can refer to the same object, as is the case in this example of two variables that are assigned to the same dictionary:

>>> spam = {'name': 'Zophie'}
>>> id(spam)
140025276813824
>>> eggs = spam
>>> id(eggs)
140025276813824

The identities of the spam and eggs identifiers are both 33861824 because they refer to the same dictionary object. Now change the value of spam in the interactive shell:

>>> spam = {'name': 'Zophie'}
>>> eggs = spam
>>> spam['name'] = 'Al'
>>> spam
{'name': 'Al'}
>>> eggs
{'name': 'Al'}

You’ll see that changes to spam 1 mysteriously also appear in eggs 2. The reason is that they both refer to the same object.

VA RI A BLE ME TA PHORS: BOX V S. L A BE L

Many introductory books use boxes as a metaphor for variables, which is an oversimplification. It’s easy to think of variables as a box that a value is stored in, as in Figure 7-1, but this metaphor falls apart when it comes to references. The previous spam and eggs variables don’t store separate dictionaries; rather, they store references to the same dictionary in the computer’s memory.

_images/1.png

Figure 7-1: Many books say you can think of a variable as a box that contains a value.

In Python, all variables are technically references, not containers of values, regardless of their data type. The box metaphor is simple but also flawed. Instead of thinking of variables as boxes, you can think of variables as labels for objects in memory. Figure 7-2 shows labels on the previous spam and eggs examples.

_images/2.png

Figure 7-2: Variables can also be thought of as labels on values.

Because multiple variables can refer to the same object, that object can be “stored” in multiple variables. Multiple boxes can’t store the same object, so it might be easier for you to use the label metaphor instead. Ned Batchelder’s PyCon 2015 talk, “Facts and Myths about Python Names and Values” has more information on this topic at https://youtu.be/_AEJHKGk9ns.

Without understanding that the = assignment operator always copies the reference, not the object, you might introduce bugs by thinking that you’re making a duplicate copy of an object when really you’re copying the reference to the original object. Fortunately, this isn’t an issue for immu- table values like integers, strings, and tuples for reasons that I’ll explain in “Mutable and Immutable” on page 114.

You can use the is operator to compare whether two objects have the same identity. In contrast, the == operator checks only whether object values are the same. You can consider x is y to be shorthand for id(x) == id(y) . Enter the following into the interactive shell to see the difference:

>>> spam = {'name': 'Zophie'}
>>> eggs = spam
>>> spam is eggs
True
>>> spam == eggs
True
>>> bacon = {'name': 'Zophie'}
>>> spam == bacon
True
>>> spam is bacon
False

The variables spam and eggs refer to the same dictionary object 1, so their identities and values are the same. But bacon refers to a separate dic- tionary object 2, even though it contains data identical to spam and eggs . The identical data means bacon has the same value as spam and eggs , but they’re two different objects with two different identities. ## Items In Python, an object that is inside a container object, like a list or diction- ary, is also called an item or an element. For example, the strings in the list [‘dog’, ‘cat’, ‘moose’] are objects but are also called items. ## Mutable and Immutable As noted earlier, all objects in Python have a value, data type, and identity, and of these only the value can change. If you can change the object’s value, it’s a mutable object. If you can’t change its value, it’s an immutable object. Table 7-2 lists some mutable and immutable data types in Python.

Table 7-2: Some of Python’s Mutable and Immutable Data Types

Mutable data types

Immutable data types

List

Integer

Dictionaries

Floating-point number

Sets

Boolean

Bytearray

String

Array

Frozen set

Bytes

Tuple

When you overwrite a variable, it might look like you’re changing the object’s value, as in this interactive shell example:

>>> spam = 'hello'
>>> spam
'hello'
>>> spam = 'goodbye'
>>> spam
'goodbye'

But in this code, you haven’t changed the ‘hello’ object’s value from ‘hello’ to ‘goodbye’ . They’re two separate objects. You’ve only switched spam from referring to the ‘hello’ object to the ‘goodbye’ object. You can check whether this is true by using the id() function to show the two objects’ identities:

>>> spam = 'hello'
>>> id(spam)
140025267728496
>>> spam = 'goodbye'
>>> id(spam)
140025267730672

These two string objects have different identities (40718944 and 40719224) because they’re different objects. But variables that refer to mutable objects can have their values modified in-place. For example, enter the following into the interactive shell:

>>> spam = ['cat', 'dog']
>>> id(spam)
140025268214592
>>> spam.append('moose')
>>> spam[0] = 'snake'
>>> spam
['snake', 'dog', 'moose']
>>> id(spam)
140025268214592

The append() method 1 and item assignment by indexing 2 both mod- ify the value of the list in-place. Even though the list’s value has changed, its identity remains the same (33805576). But when you concatenate a list using the + operator, you create a new object (with a new identity) that overwrites the old list:

>>> spam = spam + ['rat']
>>> spam
['snake', 'dog', 'moose', 'rat']
>>> id(spam)
140025267727232

List concatenation creates a new list with a new identity. When this hap- pens, the old list will eventually be freed from memory by the garbage collec- tor. You’ll have to consult the Python documentation to see which methods and operations modify objects in-place and which overwrite objects. A good rule to keep in mind is that if you see a literal in the source code, such as [‘rat’] in the previous example, Python will most likely create a new object. A method that is called on the object, such as append() , often modifies the object in-place.

Assignment is simpler for objects of immutable data types like integers, strings, or tuples. For example, enter the following into the interactive shell:

>>> bacon = 'Goodbye'
>>> id(bacon)
140025267780464
>>> bacon = 'Hello'
>>> id(bacon)
140025267798960
>>> bacon = bacon + ', world!'
>>> bacon
'Hello, world!'
>>> id(bacon)
140025267779696

bacon[0] = 'J'

Traceback (most recent call last): File "<stdin>", line 1, in <module> TypeError: 'str' object does not support item assignment

Strings are immutable, so you cannot change their value. Although it looks like the string’s value in bacon is being changed from ‘Goodbye’ to ‘Hello’ 1, it’s actually being overwritten by a new string object with a new identity. Similarly, an expression using string concatenation creates a new string object 2 with a new identity. Attempting to modify the string in-place with item assignment isn’t allowed in Python 3.

A tuple’s value is defined as the objects it contains and the order of those objects. Tuples are immutable sequence objects that enclose values in parentheses. This means that items in a tuple can’t be overwritten:

>>> eggs = ('cat', 'dog', [2, 4, 6])
>>> id(eggs)
140025268145792
>>> id(eggs[2])
140025267809280
>>> eggs[2] = eggs[2] + [8, 10]
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
TypeError: 'tuple' object does not support item assignment

But a mutable list inside an immutable tuple can still be modified in-place:

>>> eggs[2].append(8)
>>> eggs[2].append(10)
>>> eggs
('cat', 'dog', [2, 4, 6, 8, 10])
>>> id(eggs)
140025268145792
>>> id(eggs[2])
140025267809280

Although this is an obscure special case, it’s important to keep in mind. The tuple still refers to the same objects, as depicted in Figure 7-3. But if a tuple contains a mutable object and that object changes its value—that is, if the object mutates—the value of the tuple also changes.

I, and almost every Pythonista, call tuples immutable. But whether some tuples can be called mutable depends on your definition. I explore this topic more in my PyCascades 2019 talk, “The Amazing Mutable, Immutable Tuple” at https://invpy.com/amazingtuple/. You can also read Luciano Ramalho’s expla- nation in Chapter 2 of Fluent Python. (O’Reilly Media, 2015)

_images/3.png

Figure 7-3: Although the set of objects in a tuple is immutable, the objects can be mutable.

6.1.2. Indexes, Keys, and Hashes

Python lists and dictionaries are values that can contain multiple other val- ues. To access these values, you use an index operator, which is composed of a pair of square brackets ( [ ] ) and an integer called an index to specify which value you want to access. Enter the following into the interactive shell to see how indexing works with lists:

>>> spam = ['cat', 'dog', 'moose']
>>> spam[0]
'cat'
>>> spam[-2]
'dog'

In this example, 0 is an index. The first index is 0 , not 1 , because Python (as most languages do) uses zero-based indexing. Languages that use one-based indexing are rare: Lua and R are the most predominant. Python also sup- ports negative indexes, where -1 refers to the last item in a list, -2 refers to the second-to-last item, and so on. You can think of a negative index spam[–n] as being the same as spam[len(spam) – n] .

NOTE

Computer scientist and singer-songwriter Stan Kelly-Bootle once joked, “Should array indices start at 0 or 1? My compromise of 0.5 was rejected without, I thought, proper consideration.”

You can also use the index operator on a list literal, although all those square brackets can look confusing and unnecessary in real-world code:

>>> ['cat', 'dog', 'moose'][2]
'moose'

Indexing can also be used for values other than lists, such as on a string to obtain individual characters:

>>> 'Hello, world'[0]
'H'

Python dictionaries are organized into key-value pairs:

>>> spam = {'name': 'Zophie'}
>>> spam['name']
'Zophie'

Although list indexes are limited to integers, a Python dictionary’s index operator is a key and can be any hashable object. A hash is an integer that acts as a sort of fingerprint for a value. An object’s hash never changes for the lifetime of the object, and objects with the same value must have the same hash. The string ‘name’ in this instance is the key for the value ‘Zophie’ . The hash() function will return an object’s hash if the object is hashable. Immutable objects, such as strings, integers, floats, and tuples, can be hashable. Lists (as well as other mutable objects) aren’t hashable. Enter the following into the interactive shell:

>>> hash('hello')
4204925557160297428
>>> hash(42)
42
>>> hash(3.14)
322818021289917443
>>> hash((1, 2, 3))
529344067295497451
>>> hash([1, 2, 3])
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
TypeError: unhashable type: 'list'

Although the details are beyond the scope of this book, the key’s hash is used to find items stored in a dictionary and set data structures. That’s why you can’t use a mutable list for a dictionary’s keys:

>>> d = {}
>>> d[[1, 2, 3]] = 'some value'
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
TypeError: unhashable type: 'list'

A hash is different from an identity. Two different objects with the same value will have different identities but the same hash. For example, enter the following into the interactive shell:

>>> a = ('cat', 'dog', 'moose')
>>> b = ('cat', 'dog', 'moose')
>>> id(a), id(b)
(140025276924608, 140025268148672)
>>> id(a) == id(b)
False
>>> hash(a), hash(b)
(-1608630135395441055, -1608630135395441055)
>>> hash(a) == hash(b)
True

The tuples referred to by a and b have different identities 1, but their identical values mean they’ll have identical hashes 2. Note that a tuple is hashable if it contains only hashable items. Because you can use only hash- able items as keys in a dictionary, you can’t use a tuple that contains an unhashable list as a key. Enter the following into the interactive shell:

>>> tuple1 = ('cat', 'dog')
>>> tuple2 = ('cat', ['apple', 'orange'])
>>> spam = {}
1 >>> spam[tuple1] = 'a value'
2 >>> spam[tuple2] = 'another value'
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
TypeError: unhashable type: 'list'

Notice that tuple1 is hashable 1, but tuple2 contains an unhashable list 2 and so is also unhashable. ## Containers, Sequences, Mapping, and Set Types The words container, sequence, and mapping have meanings in Python that don’t necessarily apply to other programming languages. In Python, a con- tainer is an object of any data type that can contain multiple other objects. Lists and dictionaries are common container types used in Python.

A sequence is an object of any container data type with ordered values accessible through integer indexes. Strings, tuples, lists, and bytes objects are sequence data types. Objects of these types can access values using inte- ger indexes in the index operator (the [ and ] brackets) and can also be passed to the len() function. By “ordered,” we mean that there is a first value, second value, and so on in the sequence. For example, the following two list values aren’t considered equal because their values are ordered differently:

>>> [1, 2, 3] == [3, 2, 1]
False

A mapping is an object of any container data type that uses keys instead of an index. A mapping can be ordered or unordered. Dictionaries in Python 3.4 and earlier are unordered because there is no first or last key-value pair in a dictionary:

>>> spam = {'a': 1, 'b': 2, 'c': 3, 'd': 4}# This is run from CPython 3.5.
>>> list(spam.keys())
['a', 'b', 'c', 'd']
>>> spam['e'] = 5
>>> list(spam.keys())
['a', 'b', 'c', 'd', 'e']

You have no guarantee of getting items in a consistent order from dic- tionaries in early versions of Python. As a result of dictionaries’ unordered nature, two dictionary literals written with different orders for their key- value pairs are still considered equal:

>>> {'a': 1, 'b': 2, 'c': 3} == {'c': 3, 'a': 1, 'b': 2}
True

But starting in CPython 3.6, dictionaries do retain the insertion order of their key-value pairs:

>>> spam = {'a': 1, 'b': 2, 'c': 3, 'd': 4}
>>> list(spam)
['a', 'b', 'c', 'd']
>>> spam['e'] = 5# This is run from CPython 3.6.
>>> list(spam)
['a', 'b', 'c', 'd', 'e']

This is a feature in the CPython 3.6 interpreter but not in other interpreters for Python 3.6. All Python 3.7 interpreters support ordered dictionaries, which became standard in the Python language in 3.7. But just because a dictionary is ordered doesn’t mean that its items are acces- sible through integer indexes: spam[0] won’t evaluate to the first item in an ordered dictionary (unless by coincidence there is a key 0 for the first item). Ordered dictionaries are also considered the same if they contain the same key-value pairs, even if the key-value pairs are in a different order in each dictionary.

The collections module contains many other mapping types, including OrderedDict , ChainMap , Counter , and UserDict , which are described in the online documentation at https://docs.python.org/3/library/collections.html. ## Dunder Methods and Magic Methods Dunder methods, also called magic methods, are special methods in Python whose names begin and end with two underscores. These methods are used for operator overloading. Dunder is short for double underscore. The most familiar dunder method is init() (pronounced “dunder init dunder,” or simply “init”), which initializes objects. Python has a few dozen dunder methods, and Chapter 17 explains them in detail. ## Modules and Packages A module is a Python program that other Python programs can import so they can use the module’s code. The modules that come with Python are collectively called the Python Standard Library, but you can create your own modules as well. If you save a Python program as, for example, spam.py, other programs can run import spam to access the spam.py program’s func- tions, classes, and top-level variables.

A package is a collection of modules that you form by placing a file named init.py inside a folder. You use the folder’s name as the name of the package. Packages can contain multiple modules (that is, .py files) or other packages (other folders containing init.py files).

For more explanation and detail about modules and packages, check out the official Python documentation at https://docs.python.org/3/tutorial/ modules.html. ## Callables and First-Class Objects Functions and methods aren’t the only things that you can call in Python. Any object that implements the callable operator—the two parentheses () — is a callable object. For example, if you have a def hello(): statement, you can think of the code as a variable named hello that contains a function object. Using the callable operator on this variable calls the function in the vari- able: hello() .

Classes are an OOP concept, and a class is an example of a callable object that isn’t a function or method. For example, the date class in the datetime module is called using the callable operator, as in the code datetime.date(2020, 1, 1) . When the class object is called, the code inside the class’s init() method is run. Chapter 15 has more details about classes.

Functions are first-class objects in Python, meaning you can store them in variables, pass them as arguments in function calls, return them from function calls, and do anything else you can do with an object. Think of a def statement as assigning a function object to a variable. For example, you could create a spam() function that you can then call:

>>> def spam():
>>>
>>>     print('Spam! Spam! Spam!')
>>>
>>> spam()
Spam! Spam! Spam!

You can also assign the spam() function object to other variables. When you call the variable you’ve assigned the function object to, Python executes the function:

>>> eggs = spam
>>> eggs()
Spam! Spam! Spam!

These are called aliases, which are different names for existing func- tions. They’re often used if you need to rename a function. But a large amount of existing code uses the old name, and it would be too much work to change it.

The most common use of first-class functions is so you can pass func- tions to other functions. For example, we can define a callTwice() function, which can be passed a function that needs to be called twice:

>>> def callTwice(func):
>>>
>>>     func()
>>>
>>>     func()
>>>
>>> callTwice(spam)
Spam! Spam! Spam!
Spam! Spam! Spam!

You could just write spam() twice in your source code. But you can pass the callTwice() function to any function at runtime rather than having to type the function call twice into the source code beforehand.

You have no guarantee of getting items in a consistent order from dic- tionaries in early versions of Python. As a result of dictionaries’ unordered nature, two dictionary literals written with different orders for their key- value pairs are still considered equal:

>>> {'a': 1, 'b': 2, 'c': 3} == {'c': 3, 'a': 1, 'b': 2}
True

But starting in CPython 3.6, dictionaries do retain the insertion order of their key-value pairs:

>>> spam = {'a': 1, 'b': 2, 'c': 3, 'd': 4}
>>> list(spam)
['a', 'b', 'c', 'd']
>>> spam['e'] = 5
>>> list(spam)
>>> # This is run from CPython 3.6.
['a', 'b', 'c', 'd', 'e']

This is a feature in the CPython 3.6 interpreter but not in other interpreters for Python 3.6. All Python 3.7 interpreters support ordered dictionaries, which became standard in the Python language in 3.7. But just because a dictionary is ordered doesn’t mean that its items are acces- sible through integer indexes: spam[0] won’t evaluate to the first item in an ordered dictionary (unless by coincidence there is a key 0 for the first item). Ordered dictionaries are also considered the same if they contain the same key-value pairs, even if the key-value pairs are in a different order in each dictionary.

The collections module contains many other mapping types, including OrderedDict , ChainMap , Counter , and UserDict , which are described in the online documentation at https://docs.python.org/3/library/collections.html. ## Dunder Methods and Magic Methods Dunder methods, also called magic methods, are special methods in Python whose names begin and end with two underscores. These methods are used for operator overloading. Dunder is short for double underscore. The most familiar dunder method is init() (pronounced “dunder init dunder,” or simply “init”), which initializes objects. Python has a few dozen dunder methods, and Chapter 17 explains them in detail. ## Modules and Packages A module is a Python program that other Python programs can import so they can use the module’s code. The modules that come with Python are collectively called the Python Standard Library, but you can create your own modules as well. If you save a Python program as, for example, spam.py, other programs can run import spam to access the spam.py program’s func- tions, classes, and top-level variables.

A package is a collection of modules that you form by placing a file named init.py inside a folder. You use the folder’s name as the name of the package. Packages can contain multiple modules (that is, .py files) or other packages (other folders containing init.py files).

For more explanation and detail about modules and packages, check out the official Python documentation at https://docs.python.org/3/tutorial/ modules.html. ## Callables and First-Class Objects Functions and methods aren’t the only things that you can call in Python. Any object that implements the callable operator—the two parentheses () — is a callable object. For example, if you have a def hello(): statement, you can think of the code as a variable named hello that contains a function object. Using the callable operator on this variable calls the function in the vari- able: hello() .

Classes are an OOP concept, and a class is an example of a callable object that isn’t a function or method. For example, the date class in the datetime module is called using the callable operator, as in the code datetime.date(2020, 1, 1) . When the class object is called, the code inside the class’s init() method is run. Chapter 15 has more details about classes.

Functions are first-class objects in Python, meaning you can store them in variables, pass them as arguments in function calls, return them from function calls, and do anything else you can do with an object. Think of a def statement as assigning a function object to a variable. For example, you could create a spam() function that you can then call:

>>> def spam():
>>>
>>>     print('Spam! Spam! Spam!')
>>>
>>> spam()
Spam! Spam! Spam!

You can also assign the spam() function object to other variables. When you call the variable you’ve assigned the function object to, Python executes the function:

>>> eggs = spam
>>> eggs()
Spam! Spam! Spam!

These are called aliases, which are different names for existing func- tions. They’re often used if you need to rename a function. But a large amount of existing code uses the old name, and it would be too much work to change it.

The most common use of first-class functions is so you can pass func- tions to other functions. For example, we can define a callTwice() function, which can be passed a function that needs to be called twice:

>>> def callTwice(func):
>>>
>>>     func()
>>>
>>>     func()
>>> callTwice(spam)
Spam! Spam! Spam!
Spam! Spam! Spam!

You could just write spam() twice in your source code. But you can pass the callTwice() function to any function at runtime rather than having to type the function call twice into the source code beforehand.