Most of Python’s number types are fairly typical and will probably seem familiar if you’ve used almost any other programming language in the past. They can be used to keep track of your bank balance, the distance to Mars, the number of visitors to your website, and just about any other numeric quantity.
In Python, numbers are not really a single object type, but a category of similar types. Python supports the usual numeric types (integers and floating points), as well as literals for creating numbers and expressions for processing them. In addition, Python provides more advanced numeric programming support and objects for more advanced work. A complete inventory of Python’s numeric toolbox includes:
Integer and floating-point objects
Complex number objects
Decimal: fixed-precision objects
Fraction: rational number objects
Sets: collections with numeric operations
Booleans: true and false
Built-in functions and modules: round, math, random, etc.
Expressions; unlimited integer precision; bitwise operations; hex, octal, and binary formats
Third-party extensions: vectors, libraries, visualization, plotting, etc.
Because the types in this list’s first bullet item tend to see the most action in Python code, this chapter starts with basic numbers and fundamentals, then moves on to explore the other types on this list, which serve specialized roles. We’ll also study sets here, which have both numeric and collection qualities, but are generally considered more the former than the latter. Before we jump into code, though, the next few sections get us started with a brief overview of how we write and process numbers in our scripts.
Among its basic types, Python provides integers, which are positive and negative whole numbers, and floating-point numbers, which are numbers with a fractional part (sometimes called “floats” for verbal economy). Python also allows us to write integers using hexadecimal, octal, and binary literals; offers a complex number type; and allows integers to have unlimited precision—they can grow to have as many digits as your memory space allows. Table 5-1 shows what Python’s numeric types look like when written out in a program as literals or constructor function calls.
Table 5-1. Numeric literals and constructors
|
Literal |
Interpretation |
|---|---|
|
|
Integers (unlimited size) |
|
|
Floating-point numbers |
|
|
Octal, hex, and binary literals in 3.X |
|
|
Octal, octal, hex, and binary literals in 2.X |
|
|
Complex number literals |
|
|
Sets: 2.X and 3.X construction forms |
|
|
Decimal and fraction extension types |
|
|
Boolean type and constants |
In general, Python’s numeric type literals are straightforward to write, but a few coding concepts are worth highlighting here:
Integers are written as strings of decimal digits. Floating-point numbers have a decimal point and/or an optional signed exponent introduced by an e or E and followed by an optional sign. If you write a number with a decimal point or exponent, Python makes it a floating-point object and uses floating-point (not integer) math when the object is used in an expression. Floating-point numbers are implemented as C “doubles” in standard CPython, and therefore get as much precision as the C compiler used to build the Python interpreter gives to doubles.
In Python 2.X there are two integer types, normal (often 32 bits) and long (unlimited precision), and an integer may end in an l or L to force it to become a long integer. Because integers are automatically converted to long integers when their values overflow their allocated bits, you never need to type the letter L yourself—Python automatically converts up to long integer when extra precision is needed.
In Python 3.X, the normal and long integer types have been merged—there is only integer, which automatically supports the unlimited precision of Python 2.X’s separate long integer type. Because of this, integers can no longer be coded with a trailing l or L, and integers never print with this character either. Apart from this, most programs are unaffected by this change, unless they do type testing that checks for 2.X long integers.
Integers may be coded in decimal (base 10), hexadecimal (base 16), octal (base 8), or binary (base 2), the last three of which are common in some programming domains. Hexadecimals start with a leading 0x or 0X, followed by a string of hexadecimal digits (0–9 and A–F). Hex digits may be coded in lower- or uppercase. Octal literals start with a leading 0o or 0O (zero and lower- or uppercase letter o), followed by a string of digits (0–7). In 2.X, octal literals can also be coded with just a leading 0, but not in 3.X—this original octal form is too easily confused with decimal, and is replaced by the new 0o format, which can also be used in 2.X as of 2.6. Binary literals, new as of 2.6 and 3.0, begin with a leading 0b or 0B, followed by binary digits (0–1).
Note that all of these literals produce integer objects in program code; they are just alternative syntaxes for specifying values. The built-in calls hex(I), oct(I), and bin(I) convert an integer to its representation string in these three bases, and int(str, base) converts a runtime string to an integer per a given base.
Python complex literals are written as realpart+imaginarypart, where the imaginarypart is terminated with a j or J. The realpart is technically optional, so the imaginarypart may appear on its own. Internally, complex numbers are implemented as pairs of floating-point numbers, but all numeric operations perform complex math when applied to complex numbers. Complex numbers may also be created with the complex(real, imag) built-in call.
As we’ll see later in this chapter, there are additional numeric types at the end of Table 5-1 that serve more advanced or specialized roles. You create some of these by calling functions in imported modules (e.g., decimals and fractions), and others have literal syntax all their own (e.g., sets).
Besides the built-in number literals and construction calls shown in Table 5-1, Python provides a set of tools for processing number objects:
+, -, *, /, >>, **, &, etc.
pow, abs, round, int, hex, bin, etc.
random, math, etc.
We’ll meet all of these as we go along.
Although numbers are primarily processed with expressions, built-ins, and modules, they also have a handful of type-specific methods today, which we’ll meet in this chapter as well. Floating-point numbers, for example, have an as_integer_ratio method that is useful for the fraction number type, and an is_integer method to test if the number is an integer. Integers have various attributes, including a new bit_length method introduced in Python 3.1 that gives the number of bits necessary to represent the object’s value. Moreover, as part collection and part number, sets also support both methods and expressions.
Since expressions are the most essential tool for most number types, though, let’s turn to them next.
Perhaps the most fundamental tool that processes numbers is the expression: a combination of numbers (or other objects) and operators that computes a value when executed by Python. In Python, you write expressions using the usual mathematical notation and operator symbols. For instance, to add two numbers X and Y you would say X + Y, which tells Python to apply the + operator to the values named by X and Y. The result of the expression is the sum of X and Y, another number object.
Table 5-2 lists all the operator expressions available in Python. Many are self-explanatory; for instance, the usual mathematical operators (+, −, *, /, and so on) are supported. A few will be familiar if you’ve used other languages in the past: % computes a division remainder, << performs a bitwise left-shift, & computes a bitwise AND result, and so on. Others are more Python-specific, and not all are numeric in nature: for example, the is operator tests object identity (i.e., address in memory, a strict form of equality), and lambda creates unnamed functions.
Table 5-2. Python expression operators and precedence
|
Operators |
Description |
|---|---|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
Logical AND ( |
|
|
Logical negation |
|
|
Membership (iterables, sets) |
|
|
Object identity tests |
|
|
Magnitude comparison, set subset and superset; |
|
|
|
|
|
Bitwise XOR, set symmetric difference |
|
|
Bitwise AND, set intersection |
|
|
Shift |
|
|
Addition, concatenation; Subtraction, set difference |
|
|
Remainder, format; Division: true and floor |
|
|
Negation, identity |
|
|
Bitwise NOT (inversion) |
|
|
Power (exponentiation) |
|
|
Indexing (sequence, mapping, others) |
|
|
Slicing |
|
|
Call (function, method, class, other callable) |
|
|
Attribute reference |
|
|
Tuple, expression, generator expression |
|
|
List, list comprehension |
|
|
Dictionary, set, set and dictionary comprehensions |
Since this book addresses both Python 2.X and 3.X, here are some notes about version differences and recent additions related to the operators in Table 5-2:
In Python 2.X, value inequality can be written as either X != Y or X <> Y. In Python 3.X, the latter of these options is removed because it is redundant. In either version, best practice is to use X != Y for all value inequality tests.
In Python 2.X, a backquotes expression `X` works the same as repr(X) and converts objects to display strings. Due to its obscurity, this expression is removed in Python 3.X; use the more readable str and repr built-in functions, described in “Numeric Display Formats.”
The X // Y floor division expression always truncates fractional remainders in both Python 2.X and 3.X. The X / Y expression performs true division in 3.X (retaining remainders) and classic division in 2.X (truncating for integers). See Division: Classic, Floor, and True.
The syntax [...] is used for both list literals and list comprehension expressions. The latter of these performs an implied loop and collects expression results in a new list. See Chapter 4, Chapter 14, and Chapter 20 for examples.
The syntax (...) is used for tuples and expression grouping, as well as generator expressions—a form of list comprehension that produces results on demand, instead of building a result list. See Chapter 4 and Chapter 20 for examples. The parentheses may sometimes be omitted in all three contexts.
The syntax {...} is used for dictionary literals, and in Python 3.X and 2.7 for set literals and both dictionary and set comprehensions. See the set coverage in this chapter as well as Chapter 4, Chapter 8, Chapter 14, and Chapter 20 for examples.
The yield and ternary if/else selection expressions are available in Python 2.5 and later. The former returns send(...) arguments in generators; the latter is shorthand for a multiline if statement. yield requires parentheses if not alone on the right side of an assignment statement.
Comparison operators may be chained: X < Y < Z produces the same result as X < Y and Y < Z. See Comparisons: Normal and Chained for details.
In recent Pythons, the slice expression X[I:J:K] is equivalent to indexing with a slice object: X[slice(I, J, K)].
In Python 2.X, magnitude comparisons of mixed types are allowed, and convert numbers to a common type, and order other mixed types according to type names. In Python 3.X, nonnumeric mixed-type magnitude comparisons are not allowed and raise exceptions; this includes sorts by proxy.
Magnitude comparisons for dictionaries are also no longer supported in Python 3.X (though equality tests are); comparing sorted(aDict.items()) is one possible replacement.
We’ll see most of the operators in Table 5-2 in action later; first, though, we need to take a quick look at the ways these operators may be combined in expressions.
As in most languages, in Python, you code more complex expressions by stringing together the operator expressions in Table 5-2. For instance, the sum of two multiplications might be written as a mix of variables and operators:
A * B + C * D
So, how does Python know which operation to perform first? The answer to this question lies in operator precedence. When you write an expression with more than one operator, Python groups its parts according to what are called precedence rules, and this grouping determines the order in which the expression’s parts are computed. Table 5-2 is ordered by operator precedence:
Operators lower in the table have higher precedence, and so bind more tightly in mixed expressions.
Operators in the same row in Table 5-2 generally group from left to right when combined (except for exponentiation, which groups right to left, and comparisons, which chain left to right).
For example, if you write X + Y * Z, Python evaluates the multiplication first (Y * Z), then adds that result to X because * has higher precedence (is lower in the table) than +. Similarly, in this section’s original example, both multiplications (A * B and C * D) will happen before their results are added.
You can forget about precedence completely if you’re careful to group parts of expressions with parentheses. When you enclose subexpressions in parentheses, you override Python’s precedence rules; Python always evaluates expressions in parentheses first before using their results in the enclosing expressions.
For instance, instead of coding X + Y * Z, you could write one of the following to force Python to evaluate the expression in the desired order:
(X + Y) * Z X + (Y * Z)
In the first case, + is applied to X and Y first, because this subexpression is wrapped in parentheses. In the second case, the * is performed first (just as if there were no parentheses at all). Generally speaking, adding parentheses in large expressions is a good idea—it not only forces the evaluation order you want, but also aids readability.
Besides mixing operators in expressions, you can also mix numeric types. For instance, you can add an integer to a floating-point number:
40 + 3.14
But this leads to another question: what type is the result—integer or floating point? The answer is simple, especially if you’ve used almost any other language before: in mixed-type numeric expressions, Python first converts operands up to the type of the most complicated operand, and then performs the math on same-type operands. This behavior is similar to type conversions in the C language.
Python ranks the complexity of numeric types like so: integers are simpler than floating-point numbers, which are simpler than complex numbers. So, when an integer is mixed with a floating point, as in the preceding example, the integer is converted up to a floating-point value first, and floating-point math yields the floating-point result:
>>> 40 + 3.14 # Integer to float, float math/result
43.14
Similarly, any mixed-type expression where one operand is a complex number results in the other operand being converted up to a complex number, and the expression yields a complex result. In Python 2.X, normal integers are also converted to long integers whenever their values are too large to fit in a normal integer; in 3.X, integers subsume longs entirely.
You can force the issue by calling built-in functions to convert types manually:
>>>int(3.1415)# Truncates float to integer 3 >>>float(3)# Converts integer to float 3.0
However, you won’t usually need to do this: because Python automatically converts up to the more complex type within an expression, the results are normally what you want.
Also, keep in mind that all these mixed-type conversions apply only when mixing numeric types (e.g., an integer and a floating point) in an expression, including those using numeric and comparison operators. In general, Python does not convert across any other type boundaries automatically. Adding a string to an integer, for example, results in an error, unless you manually convert one or the other; watch for an example when we meet strings in Chapter 7.
In Python 2.X, nonnumeric mixed types can be compared, but no conversions are performed—mixed types compare according to a rule that seems deterministic but not aesthetically pleasing: it compares the string names of the objects’ types. In 3.X, nonnumeric mixed-type magnitude comparisons are never allowed and raise exceptions. Note that this applies to comparison operators such as > only; other operators like + do not allow mixed nonnumeric types in either 3.X or 2.X.
Although we’re focusing on built-in numbers right now, all Python operators may be overloaded (i.e., implemented) by Python classes and C extension types to work on objects you create. For instance, you’ll see later that objects coded with classes may be added or concatenated with x+y expressions, indexed with x[i] expressions, and so on.
Furthermore, Python itself automatically overloads some operators, such that they perform different actions depending on the type of built-in objects being processed. For example, the + operator performs addition when applied to numbers but performs concatenation when applied to sequence objects such as strings and lists. In fact, + can mean anything at all when applied to objects you define with classes.
As we saw in the prior chapter, this property is usually called polymorphism—a term indicating that the meaning of an operation depends on the type of the objects being operated on. We’ll revisit this concept when we explore functions in Chapter 16, because it becomes a much more obvious feature in that context.