We will now begin our journey to the core part of the language. First we will introduce what Python objects are, then discuss the most commonly used built-in types. We then discuss the standard type operators and built-in functions (BIFs), followed by an insightful discussion of the different ways to categorize the standard types to gain a better understanding of how they work. Finally, we will conclude by describing some types that Python does not have (mostly as a benefit for those of you with experience in another high-level language).
Python uses the object model abstraction for data storage. Any construct that contains any type of value is an object. Although Python is classified as an “object-oriented programming (OOP) language,” OOP is not required to create perfectly working Python applications. You can certainly write a useful Python script without the use of classes and instances. However, Python’s object syntax and architecture encourage or “provoke” this type of behavior. Let us now take a closer look at what a Python object is.
All Python objects have the following three characteristics: an identity, a type, and a value.
All three are assigned on object creation and are read-only with one exception, the value. (For new-style types and classes, it may possible to change the type of an object, but this is not recommended for the beginner.) If an object supports updates, its value can be changed; otherwise, it is also read-only. Whether an object’s value can be changed is known as an object’s mutability, which we will investigate later on in Section 4.7. These characteristics exist as long as the object does and are reclaimed when an object is deallocated.
Python supports a set of basic (built-in) data types, as well as some auxiliary types that may come into play if your application requires them. Most applications generally use the standard types and create and instantiate classes for all specialized data storage.
Certain Python objects have attributes, data values or executable code such as methods, associated with them. Attributes are accessed in the dotted attribute notation, which includes the name of the associated object, and were introduced in the Core Note in Section 2.14. The most familiar attributes are functions and methods, but some Python types have data attributes associated with them. Objects with data attributes include (but are not limited to): classes, class instances, modules, complex numbers, and files.
• Numbers (separate subtypes; three are integer types)
• Integer
• Boolean
• Long integer
• Floating point real number
• Complex number
• String
• List
• Tuple
• Dictionary
We will also refer to standard types as “primitive data types” in this text because these types represent the primitive data types that Python provides. We will go over each one in detail in Chapters 5, 6, and 7.
• Type
• Null object (None
)
• File
• Set/Frozenset
• Function/Method
• Module
• Class
These are some of the other types you will interact with as you develop as a Python programmer. We will also cover all of these in other chapters of this book with the exception of the type
and None
types, which we will discuss here.
It may seem unusual to regard types themselves as objects since we are attempting to just describe all of Python’s types to you in this chapter. However, if you keep in mind that an object’s set of inherent behaviors and characteristics (such as supported operators and built-in methods) must be defined somewhere, an object’s type is a logical place for this information. The amount of information necessary to describe a type cannot fit into a single string; therefore types cannot simply be strings, nor should this information be stored with the data, so we are back to types as objects.
We will formally introduce the type()
BIF below, but for now, we want to let you know that you can find out the type of an object by calling type()
with that object:
>>> type(42)
<type ’int’>
Let us look at this example more carefully. It does not look tricky by any means, but examine the return value of the call. We get the seemingly innocent output of <type ‘int’>
, but what you need to realize is that this is not just a simple string telling you that 42 is an integer. What you see as <type ‘int’>
is actually a type object. It just so happens that the string representation chosen by its implementors has a string inside it to let you know that it is an int
type object.
Now you may ask yourself, so then what is the type of any type object? Well, let us find out:
>>> type(type(42))
<type ’type’>
Yes, the type of all type objects is type
. The type
type object is also the mother of all types and is the default metaclass for all standard Python classes. It is perfectly fine if you do not understand this now. This will make sense as we learn more about classes and types.
With the unification of types and classes in Python 2.2, type objects are playing a more significant role in both object-oriented programming as well as day-to-day object usage. Classes are now types, and instances are now objects of their respective types.
Python has a special type known as the Null object or NoneType
. It has only one value, None
. The type of None
is NoneType
. It does not have any operators or BIFs. If you are familiar with C, the closest analogy to the None
type is void
, while the None
value is similar to the C value of NULL
. (Other similar objects and values include Perl’s undef
and Java’s void
type and null
value.) None
has no (useful) attributes and always evaluates to having a Boolean False
value.
All standard type objects can be tested for truth value and compared to objects of the same type. Objects have inherent True
or False
values. Objects take a False
value when they are empty, any numeric representation of zero, or the Null object None
.
The following are defined as having false
values in Python:
• None
• False
(Boolean)
• Any numeric zero:
• 0
(integer)
• 0.0
(float)
• 0L
(long integer)
• 0.0+0.0j
(complex)
• ““
(empty string)
• []
(empty list)
• ()
(empty tuple)
• {}
(empty dictionary)
Any value for an object other than those above is considered to have a true
value, i.e., non-empty, non-zero, etc. User-created class instances have a false
value when their nonzero (__nonzero__()
) or length (__len__()
) special methods, if defined, return a zero value.
• Code
• Frame
• Traceback
• Slice
• Ellipsis
• Xrange
We will briefly introduce these internal types here. The general application programmer would typically not interact with these objects directly, but we include them here for completeness. Please refer to the source code or Python internal and online documentation for more information.
In case you were wondering about exceptions, they are now implemented as classes. In older versions of Python, exceptions were implemented as strings.
Code objects are executable pieces of Python source that are byte-compiled, usually as return values from calling the compile()
BIF. Such objects are appropriate for execution by either exec
or by the eval()
BIF. All this will be discussed in greater detail in Chapter 14.
Code objects themselves do not contain any information regarding their execution environment, but they are at the heart of every user-defined function, all of which do contain some execution context. (The actual byte-compiled code as a code object is one attribute belonging to a function.) Along with the code object, a function’s attributes also consist of the administrative support that a function requires, including its name, documentation string, default arguments, and global namespace.
These are objects representing execution stack frames in Python. Frame objects contain all the information the Python interpreter needs to know during a runtime execution environment. Some of its attributes include a link to the previous stack frame, the code object (see above) that is being executed, dictionaries for the local and global namespaces, and the current instruction. Each function call results in a new frame object, and for each frame object, a C stack frame is created as well. One place where you can access a frame object is in a traceback object (see the following section).
When you make an error in Python, an exception is raised. If exceptions are not caught or “handled,” the interpreter exits with some diagnostic information similar to the output shown below:
The traceback object is just a data item that holds the stack trace information for an exception and is created when an exception occurs. If a handler is provided for an exception, this handler is given access to the traceback object.
Slice objects are created using the Python extended slice syntax. This extended syntax allows for different types of indexing. These various types of indexing include stride indexing, multi-dimensional indexing, and indexing using the Ellipsis type. The syntax for multi-dimensional indexing is sequence
[start1
: end1
, start2 :
end2]
, or using the ellipsis, sequence [..., start1
: end1]
. Slice objects can also be generated by the slice()
BIF.
Stride indexing for sequence types allows for a third slice element that allows for “step”-like access with a syntax of sequence[starting_index : ending_index : stride]
.
Support for the stride element of the extended slice syntax have been in Python for a long time, but until 2.3 was only available via the C API or Jython (and previously JPython). Here is an example of stride indexing:
Ellipsis objects are used in extended slice notations as demonstrated above. These objects are used to represent the actual ellipses in the slice syntax (...). Like the Null object None
, ellipsis objects also have a single name, Ellipsis
, and have a Boolean True
value at all times.
XRange objects are created by the BIF xrange()
, a sibling of the range()
BIF, and used when memory is limited and when range()
generates an unusually large data set. You can find out more about range()
and xrange()
in Chapter 8.
For an interesting side adventure into Python types, we invite the reader to take a look at the types
module in the standard Python library.
Comparison operators are used to determine equality of two data values between members of the same type. These comparison operators are supported for all built-in types. Comparisons yield Boolean True
or False
values, based on the validity of the comparison expression. (If you are using Python prior to 2.3 when the Boolean type was introduced, you will see integer values 1 for True
and 0 for False
.) A list of Python’s value comparison operators is given in Table 4.1.
Note that comparisons performed are those that are appropriate for each data type. In other words, numeric types will be compared according to numeric value in sign and magnitude, strings will compare lexicographically, etc.
Also, unlike many other languages, multiple comparisons can be made on the same line, evaluated in left-to-right order:
We would like to note here that comparisons are strictly between object values, meaning that the comparisons are between the data values and not the actual data objects themselves. For the latter, we will defer to the object identity comparison operators described next.
In addition to value comparisons, Python also supports the notion of directly comparing objects themselves. Objects can be assigned to other variables (by reference). Because each variable points to the same (shared) data object, any change effected through one variable will change the object and hence be reflected through all references to the same object.
In order to understand this, you will have to think of variables as linking to objects now and be less concerned with the values themselves. Let us take a look at three examples.
Example 1: foo1 and foo2 reference the same object
foo1 = foo2 = 4.3
When you look at this statement from the value point of view, it appears that you are performing a multiple assignment and assigning the numeric value of 4.3 to both the foo1
and foo2
variables. This is true to a certain degree, but upon lifting the covers, you will find that a numeric object with the contents or value of 4.3 has been created. Then that object’s reference is assigned to both foo1
and foo2
, resulting in both foo1
and foo2
aliased to the same object. Figure 4-1 shows an object with two references.
Example 2: foo1 and foo2 reference the same object
foo1 = 4.3
foo2 = foo1
This example is very much like the first: A numeric object with value 4.3 is created, then assigned to one variable. When foo2 = foo1
occurs, foo2
is directed to the same object as foo1
since Python deals with objects by passing references. foo2
then becomes a new and additional reference for the original value. So both foo1
and foo2
now point to the same object. The same figure above applies here as well.
Example 3: foo1 and foo2 reference different objects
foo1 = 4.3
foo2 = 1.3 + 3.0
This example is different. First, a numeric object is created, then assigned to foo1
. Then a second numeric object is created, and this time assigned to foo2
. Although both objects are storing the exact same value, there are indeed two distinct objects in the system, with foo1
pointing to the first, and foo2
being a reference to the second. Figure 4-2 shows we now have two distinct objects even though both objects have the same value.
Why did we choose to use boxes in our diagrams? Well, a good way to visualize this concept is to imagine a box (with contents inside) as an object. When a variable is assigned an object, that creates a “label” to stick on the box, indicating a reference has been made. Each time a new reference to the same object is made, another sticker is put on the box. When references are abandoned, then a label is removed. A box can be “recycled” only when all the labels have been peeled off the box. How does the system keep track of how many labels are on a box?
Each object has associated with it a counter that tracks the total number of references that exist to that object. This number simply indicates how many variables are “pointing to” any particular object. This is the reference count that we introduced in Chapter 3, Section 3.5.4. Python provides the is
and is not
operators to test if a pair of variables do indeed refer to the same object. Performing a check such as
a is b
is an equivalent expression to
id(a) == id(b)
The object identity comparison operators all share the same precedence level and are presented in Table 4.2.
In the example below, we create a variable, then another that points to the same object.
Both the is
and not
identifiers are Python keywords.
In the above examples with the foo1
and foo2
objects, you will notice that we use floating point values rather than integers. The reason for this is although integers and strings are immutable objects, Python sometimes caches them to be more efficient. This would have caused the examples to appear that Python is not creating a new object when it should have. For example:
In the above example, a
and b
reference the same integer object, but c
and d
do not reference the same float object. If we were purists, we would want a
and b
to work just like c
and d
because we really did ask to create a new integer object rather than an alias, as in b = a
.
Python caches or interns only simple integers that it believes will be used frequently in any Python application. At the time of this writing, Python interns integers in the range(-5, 257)
but this is subject to change, so do not code your application to expect this.
In Python 2.3, the decision was made to no longer intern strings that do not have at least one reference outside of the “interned strings table.” This means that without that reference, interned strings are no longer immortal and subject to garbage collection like everything else. A BIF introduced in 1.5 to request interning of strings, intern()
, has now been deprecated as a result.
Expressions may be linked together or negated using the Boolean logical operators and, or
, and not
, all of which are Python keywords. These Boolean operations are in highest-to-lowest order of precedence in Table 4.3. The not
operator has the highest precedence and is immediately one level below all the comparison operators. The and
and or
operators follow, respectively.
Earlier, we introduced the notion that Python supports multiple comparisons within one expression. These expressions have an implicit and
operator joining them together.
Along with generic operators, which we have just seen, Python also provides some BIFs that can be applied to all the basic object types: cmp()
, repr()
, str()
, type()
, and the single reverse or back quotes (``) operator, which is functionally equivalent to repr()
.
We now formally introduce type()
. In Python versions earlier than 2.2, type()
is a BIF. Since that release, it has become a “factory function.” We will discuss these later on in this chapter, but for now, you may continue to think of type()
as a BIF. The syntax for type()
is:
type(object)
type()
takes an object and returns its type. The return value is a type
object.
In the examples above, we take an integer and a string and obtain their types using the type()
BIF; in order to also verify that types themselves are types, we call type()
on the output of a type()
call.
Note the interesting output from the type()
function. It does not look like a typical Python data type, i.e., a number or string, but is something enclosed by greater-than and less-than signs. This syntax is generally a clue that what you are looking at is an object. Objects may implement a printable string representation; however, this is not always the case. In these scenarios where there is no easy way to “display” an object, Python “pretty-prints” a string representation of the object. The format is usually of the form: <object_something_or_another>
. Any object displayed in this manner generally gives the object type, an object ID or location, or other pertinent information.
The cmp()
BIF CoMPares two objects, say, obj1
and obj2
, and returns a negative number (integer) if obj1
is less than obj2
, a positive number if obj1
is greater than obj2
, and zero if obj1
is equal to obj2
. Notice the similarity in return values as C’s strcmp()
. The comparison used is the one that applies for that type of object, whether it be a standard type or a user-created class; if the latter, cmp()
will call the class’s special __cmp__()
method. More on these special methods in Chapter 13, on Python classes. Here are some samples of using the cmp()
BIF with numbers and strings.
We will look at using cmp()
with other objects later.
The str()
STRing and repr()
REPResentation BIFs or the single back or reverse quote operator ( `` ) come in very handy if the need arises to either re-create an object through evaluation or obtain a human-readable view of the contents of objects, data values, object types, etc. To use these operations, a Python object is provided as an argument and some type of string representation of that object is returned. In the examples that follow, we take some random Python types and convert them to their string representations.
Although all three are similar in nature and functionality, only repr()
and ``
do exactly the same thing, and using them will deliver the “official” string representation of an object that can be evaluated as a valid Python expression (using the eval()
BIF). In contrast, str()
has the job of delivering a “printable” string representation of an object, which may not necessarily be acceptable by eval()
, but will look nice in a print
statement. There is a caveat that while most return values from repr()
can be evaluated, not all can:
The executive summary is that repr()
is Python-friendly while str()
produces human-friendly output. However, with that said, because both types of string representations coincide so often, on many occasions all three return the exact same string.
repr()
and ``
?Occasionally in Python, you will find both an operator and a function that do exactly the same thing. One reason why both an operator and a function exist is that there are times where a function may be more useful than the operator, for example, when you are passing around executable objects like functions and where different functions may be called depending on the data item. Another example is the double-star ( **
) and pow()
BIF, which performs “x
to the y
power” exponentiation for x ** y
or pow(x,y)
.
Python does not support method or function overloading, so you are responsible for any “introspection” of the objects that your functions are called with. (Also see the Python FAQ 4.75.) Fortunately, we have the type()
BIF to help us with just that, introduced earlier in Section 4.3.1.
What’s in a name? Quite a lot, if it is the name of a type. It is often advantageous and/or necessary to base pending computation on the type of object that is received. Fortunately, Python provides a BIF just for that very purpose. type()
returns the type for any Python object, not just the standard types. Using the interactive interpreter, let us take a look at some examples of what type()
returns when we give it various objects.
Types and classes were unified in Python 2.2. You will see output different from that above if you are using a version of Python older than 2.2:
In addition to type()
, there is another useful BIF called isinstance()
. We cover it more formally in Chapter 13 (Object-Oriented Programming), but here we can introduce it to show you how you can use it to help determine the type of an object.
We present a script in Example 4.1 that shows how we can use isinstance()
and type()
in a runtime environment. We follow with a discussion of the use of type()
and how we migrated to using isinstance()
instead for the bulk of the work in this example.
Example 4.1. Checking the Type (typechk.py
)
The function displayNumType()
takes a numeric argument and uses the type()
built-in to indicate its type (or “not a number,” if that is the case).
Running
typechk.py
, we get the following output:
The same function was defined quite differently in the first edition of this book:
As Python evolved in its slow and simple way, so must we. Take a look at our original conditional expression:
if type(num) == type(0)...
If we take a closer look at our code, we see a pair of calls to type()
. As you know, we pay a small price each time a function is called, so if we can reduce that number, it will help with performance.
An alternative to comparing an object’s type with a known object’s type (as we did above and in the example below) is to utilize the types
module, which we briefly mentioned earlier in the chapter. If we do that, then we can use the type object there without having to “calculate it.” We can then change our code to only having one call to the type()
function:
We discussed object value comparison versus object identity comparison earlier in this chapter, and if you realize one key fact, then it will become clear that our code is still not optimal in terms of performance. During runtime, there is always only one type object that represents an integer. In other words, type(0)
, type(42)
, type(-100)
are always the same object: <type ‘int’>
(and this is also the same object as types.IntType
).
If they are always the same object, then why do we have to compare their values since we already know they will be the same? We are “wasting time” extracting the values of both objects and comparing them if they are the same object, and it would be more optimal to just compare the objects themselves. Thus we have a migration of the code above to the following:
if type(num) is types.IntType... # or type(0)
Does that make sense? Object value comparison via the equal sign requires a comparison of their values, but we can bypass this check if the objects themselves are the same. If the objects are different, then we do not even need to check because that means the original variable must be of a different type (since there is only one object of each type). One call like this may not make a difference, but if there are many similar lines of code throughout your application, then it starts to add up.
This is a minor improvement to the previous example and really only makes a difference if your application performs makes many type comparisons like our example. To actually get the integer type object, the interpreter has to look up the types
name first, and then within that module’s dictionary, find IntType
. By using from-import
, you can take away one lookup:
from types import IntType
if type(num) is IntType...
The unification of types and classes in 2.2 has resulted in the expected rise in the use of the isinstance()
BIF. We formally introduce isinstance()
in Chapter 13 (Object-Oriented Programming), but we will give you a quick preview now.
This Boolean function takes an object and one or more type objects and returns True
if the object in question is an instance of one of the type objects. Since types and classes are now the same, int
is now a type (object) and a class. We can use isinstance()
with the built-in types to make our if
statement more convenient and readable:
if isinstance(num, int)...
Using isinstance()
along with type objects is now also the accepted style of usage when introspecting objects’ types, which is how we finally arrive at our updated typechk.py
application above. We also get the added bonus of isinstance()
accepting a tuple of type objects to check against our object with instead of having an if-elif-else
if we were to use only type()
.
A summary of operators and BIFs common to all basic Python types is given in Table 4.5. The progressing shaded groups indicate hierarchical precedence from highest-to-lowest order. Elements grouped with similar shading all have equal priority. Note that these (and most Python) operators are available as functions via the operator
module.
Since Python 2.2 with the unification of types and classes, all of the built-in types are now classes, and with that, all of the “conversion” built-in functions like int()
, type()
, list()
, etc., are now factory functions. This means that although they look and act somewhat like functions, they are actually class names, and when you call one, you are actually instantiating an instance of that type, like a factory producing a good.
The following familiar factory functions were formerly built-in functions:
• int(), long(), float(), complex()
• str(), unicode(), basestring()
• list(), tuple()
• type()
Other types that did not have factory functions now do. In addition, factory functions have been added for completely new types that support the new-style classes. The following is a list of both types of factory functions:
• dict()
• bool()
• set(), frozenset()
• object()
• classmethod()
• staticmethod()
• super()
• property()
• file()
If we were to be maximally verbose in describing the standard types, we would probably call them something like Python’s “basic built-in data object primitive types.”
• “Basic,” indicating that these are the standard or core types that Python provides
• “Built-in,” due to the fact that these types come by default in Python
• “Data,” because they are used for general data storage
• “Object,” because objects are the default abstraction for data and functionality
• “Primitive,” because these types provide the lowest-level granularity of data storage
• “Types,” because that’s what they are: data types!
However, this description does not really give you an idea of how each type works or what functionality applies to them. Indeed, some of them share certain characteristics, such as how they function, and others share commonality with regard to how their data values are accessed. We should also be interested in whether the data that some of these types hold can be updated and what kind of storage they provide.
There are three different models we have come up with to help categorize the standard types, with each model showing us the interrelationships between the types. These models help us obtain a better understanding of how the types are related, as well as how they work.
The first way we can categorize the types is by how many objects can be stored in an object of this type. Python’s types, as well as types from most other languages, can hold either single or multiple values. A type which holds a single literal object we will call atomic or scalar storage, and those which can hold multiple objects we will refer to as container storage. (Container objects are also referred to as composite or compound objects in the documentation, but some of these refer to objects other than types, such as class instances.) Container types bring up the additional issue of whether different types of objects can be stored. All of Python’s container types can hold objects of different types. Table 4.6 categorizes Python’s types by storage model.
Although strings may seem like a container type since they “contain” characters (and usually more than one character), they are not considered as such because Python does not have a character type (see Section 4.9). Thus strings are self-contained literals.
Another way of categorizing the standard types is by asking the question, “Once created, can objects be changed, or can their values be updated?” When we introduced Python types early on, we indicated that certain types allow their values to be updated and others do not. Mutable objects are those whose values can be changed, and immutable objects are those whose values cannot be changed. Table 4.7 illustrates which types support updates and which do not.
Now after looking at the table, a thought that must immediately come to mind is, “Wait a minute! What do you mean that numbers and strings are immutable? I’ve done things like the following”:
“They sure as heck don’t look immutable to me!” That is true to some degree, but looks can be deceiving. What is really happening behind the scenes is that the original objects are actually being replaced in the above examples. Yes, that is right. Read that again.
Rather than referring to the original objects, new objects with the new values were allocated and (re)assigned to the original variable names, and the old objects were garbage-collected. One can confirm this by using the id()
BIF to compare object identities before and after such assignments.
If we added calls to id()
in our example above, we may be able to see that the objects are being changed, as below:
Your mileage will vary with regard to the object IDs as they will differ between executions. On the flip side, lists can be modified without replacing the original object, as illustrated in the code below:
Notice how for each change, the ID for the list remained the same.
Although the previous two models of categorizing the types are useful when being introduced to Python, they are not the primary models for differentiating the types. For that purpose, we use the access model. By this, we mean, how do we access the values of our stored data? There are three categories under the access model: direct, sequence, and mapping. The different access models and which types fall into each respective category are given in Table 4.8.
Direct types indicate single-element, non-container types. All numeric types fit into this category.
Sequence types are those whose elements are sequentially accessible via index values starting at 0. Accessed items can be either single elements or in groups, better known as slices. Types that fall into this category include strings, lists, and tuples. As we mentioned before, Python does not support a character type, so, although strings are literals, they are a sequence type because of the ability to access substrings sequentially.
Mapping types are similar to the indexing properties of sequences, except instead of indexing on a sequential numeric offset, elements (values) are unordered and accessed with a key, thus making mapping types a set of hashed key-value pairs.
We will use this primary model in the next chapter by presenting each access model type and what all types in that category have in common (such as operators and BIFs), then discussing each Python standard type that fits into those categories. Any operators, BIFs, and methods unique to a specific type will be highlighted in their respective sections.
So why this side trip to view the same data types from differing perspectives? Well, first of all, why categorize at all? Because of the high-level data structures that Python provides, we need to differentiate the “primitive” types from those that provide more functionality. Another reason is to be clear on what the expected behavior of a type should be. For example, if we minimize the number of times we ask ourselves, “What are the differences between lists and tuples again?” or “What types are immutable and which are not?” then we have done our job. And finally, certain categories have general characteristics that apply to all types in a certain category. A good craftsman (and craftswoman) should know what is available in his or her toolboxes.
The second part of our inquiry asks, “Why all these different models or perspectives”? It seems that there is no one way of classifying all of the data types. They all have crossed relationships with each other, and we feel it best to expose the different sets of relationships shared by all the types. We also want to show how each type is unique in its own right. No two types map the same across all categories. (Of course, all numeric subtypes do, so we are categorizing them together.) Finally, we believe that understanding all these relationships will ultimately play an important implicit role during development. The more you know about each type, the more you are apt to use the correct ones in the parts of your application where they are the most appropriate, and where you can maximize performance.
We summarize by presenting a cross-reference chart (see Table 4.9) that shows all the standard types, the three different models we use for categorization, and where each type fits into these models.
Before we explore each standard type, we conclude this chapter by giving a list of types that are not supported by Python.
Python does not have a char type to hold either single character or 8-bit integers. Use strings of length one for characters and integers for 8-bit numbers.
Since Python manages memory for you, there is no need to access pointer addresses. The closest to an address that you can get in Python is by looking at an object’s identity using the id()
BIF. Since you have no control over this value, it’s a moot point. However, under Python’s covers, everything is a pointer.
Python’s plain integers are the universal “standard” integer type, obviating the need for three different integer types, e.g., C’s int
, short
, and long
. For the record, Python’s integers are implemented as C long
s. Also, since there is a close relationship between Python’s int
and long
types, users have even fewer things to worry about. You only need to use a single type, the Python integer. Even when the size of an integer is exceed, for example, multiplying two very large numbers, Python automatically gives you a long back instead of overflowing with an error.
C has both a single precision float
type and double-precision double
type. Python’s float
type is actually a C double
. Python does not support a single-precision floating point type because its benefits are outweighed by the overhead required to support two types of floating point types. For those wanting more accuracy and willing to give up a wider range of numbers, Python has a decimal floating point number too, but you have to import the decimal
module to use the Decimal
type. Floats are always estimations. Decimals are exact and arbitrary precision. Decimals make sense concerning things like money where the values are exact. Floats make sense for things that are estimates anyway, such as weights, lengths, and other measurements.
4-1. Python Objects. What three attributes are associated with all Python objects? Briefly describe each one.
4-2. Types. What does immutable mean? Which Python types are mutable and which are not?
4-3. Types. Which Python types are sequences, and how do they differ from mapping types?
4-4. type()
. What does the type()
built-in function do? What kind of object does type()
return?
4-5. str()
and repr()
. What are the differences between the str()
and repr()
built-in functions? Which is equivalent to the backquote ( `` ) operator?
4-6. Object Equality. What do you think is the difference between the expressions type(a) == type(b)
and type(a)
is
type(b)
? Why is the latter preferred? What does isinstance()
have to do it all of this?
4-7. dir()
Built-in Function. In several exercises in Chapter 2, we experimented with a built-in function called dir()
, which takes an object and reveals its attributes. Do the same thing for the types
module. Write down the list of the types that you are familiar with, including all you know about each of these types; then create a separate list of those you are not familiar with. As you learn Python, deplete the “unknown” list so that all of them can be moved to the “familiar with” list.
4-8. Lists and Tuples. How are lists and tuples similar? Different?
4-9. *Interning. Given the following assignments:
a = 10
b = 10
c = 1000
d = 1000
e = 10.0
f = 10.0
What is the output of each of the following and why?
(a) a
is
b
(b) c
is
d
(c) e
is
f