DekGenius.com
[ Team LiB ] Previous Section Next Section

4.5 Numbers in Action

Probably the best way to understand numeric objects and expressions is to see them in action. So, start up the interactive command line and type some basic, but illustrative operations.

4.5.1 Basic Operations and Variables

First of all, let's exercise some basic math. In the following interaction, we first assign two variables (a and b) to integers, so we can use them later in a larger expression. Variables are simply names—created by you or Python—that are used to keep track of information in your program. We'll say more about this later, but in Python:

  • Variables are created when first assigned a value.

  • Variables are replaced with their values when used in expressions.

  • Variables must be assigned before they can be used in expressions.

  • Variables refer to objects, and are never declared ahead of time.

In other words, the assignments cause these variables to spring into existence automatically.

% python
>>> a = 3           # Name created
>>> b = 4

We've also used a comment here. In Python code, text after a # mark and continuing to the end of the line is considered to be a comment, and is ignored by Python. Comments are a place to write human-readable documentation for your code. Since code you type interactively is temporary, you won't normally write comments there, but they are added to examples to help explain the code.[7] In the next part of this book, we'll meet a related feature—documentation strings—that attaches the text of your comments to objects.

[7] If you're working along, you don't need to type any of the comment text from # through the end of the line; comments are simply ignored by Python, and not a required part of the statements we run.

Now, let's use the integer objects in expressions. At this point, a and b are still 3 and 4, respectively; variables like these are replaced with their values whenever used inside an expression, and expression results are echoed back when working interactively:

>>>  a + 1, a - 1        # Addition (3+1), subtraction (3-1) 
(4, 2)

>>>  b * 3, b / 2        # Multiplication (4*3), division (4/2) 
(12, 2)

>>>  a % 2, b ** 2       # Modulus (remainder), power  
(1, 16)

>>>  2 + 4.0, 2.0 ** b   # Mixed-type conversions 
(6.0, 16.0)

Technically, the results being echoed back here are tuples of two values, because lines typed at the prompt contain two expressions separated by commas; that's why the result are displayed in parenthesis (more on tuples later). Notice that the expressions work because the variables a and b within them have been assigned values; if you use a different variable that has never been assigned, Python reports an error rather than filling in some default value:

>>> c * 2
Traceback (most recent call last):
  File "<stdin>", line 1, in ?
NameError: name 'c' is not defined

You don't need to predeclare variables in Python, but they must be assigned at least once before you can use them at all. Here are two slightly larger expressions to illustrate operator grouping and more about conversions:

>>> b / 2 + a             # Same as ((4 / 2) + 3)
5
>>> print b / (2.0 + a)   # Same as (4 / (2.0 + 3))
0.8

In the first expression, there are no parentheses, so Python automatically groups the components according to its precedence rules—since / is lower in Table 4-3 than +, it binds more tightly, and so is evaluated first. The result is as if the expression had parenthesis as shown in the comment to the right of the code. Also notice that all the numbers are integers in the first expression; because of that, Python performs integer division and addition.

In the second expression, parentheses are added around the + part to force Python to evaluate it first (i.e., before the /). We also made one of the operands floating-point by adding a decimal point: 2.0. Because of the mixed types, Python converts the integer referenced by a to a floating-point value (3.0) before performing the +. It also converts b to a floating-point value (4.0) and performs a floating-point division; (4.0/5.0) yields a floating-point result of 0.8. If all the numbers in this expression were integers, it would invoke integer division (4/5), and the result would be the truncated integer 0 (in Python 2.2, at least—see the discussion of true division ahead).

4.5.2 Numeric Representation

By the way, notice that we used a print statement in the second example; without the print, you'll see something that may look a bit odd at first glance:

>>> b / (2.0 + a)            # Auto echo output: more digits
0.80000000000000004

>>> print b / (2.0 + a)      # print rounds off digits.
0.8

The whole story behind this has to do with the limitations of floating-point hardware, and its inability to exactly represent some values. Since computer architecture is well beyond this book's scope, though, we'll finesse this by saying that all of the digits in the first output are really there, in your computer's floating-point hardware; it's just that you're not normally accustomed to seeing them. We're using this example to demonstrate the difference in output formatting—the interactive prompt's automatic result echo shows more digits than the print statement. If you don't want all the digits, say print.

Note that not all values have so many digits to display:

>>> 1 / 2.0
0.5

And there are more ways to display the bits of a number inside your computer than prints and automatic echoes:

>>> num = 1 / 3.0
>>> num                     # Echoes
0.33333333333333331
>>> print num               # Print rounds
0.333333333333

>>> "%e" % num              # String formatting
'3.333333e-001'
>>> "%2.2f" % num           # String formatting
'0.33'

The last two of these employ string formatting—an expression that allows for format flexibility, explored in the upcoming chapter on strings.

Str and Repr Display Formats

Technically, the difference between default interactive echoes and prints corresponds to the difference between the built-in repr and str functions:

>>> repr(num)               # Used by echoes: as code form
'0.33333333333333331'
>>> str(num)                # Used by print: user-friendly form
'0.333333333333'

Both of these convert arbitrary objects to their string representation: repr (and the interactive prompt) produces results that look as though they were code; str (and the print statement) converts to a typically more user-friendly format. This notion will resurface when we study strings; more on these built-ins in general later in the book.


4.5.3 Division: Classic, Floor, and True

Now that you've seen how division works, you should know that it is scheduled for a slight change in a future Python release (currently, in 3.0, scheduled to appear years after this edition is released). In Python 2.3, things work as just described, but there are actually two different division operators, one of which will change:


X / Y

Classic division. In Python 2.3 and earlier, this operator truncates results down for integers, and keeps remainders for floating-point numbers, as described here. This operator will be changed to true division—always keeping remainders regardless of types—in a future Python release (3.0).


X // Y

Floor division. Added in Python 2.2, this operator always truncates fractional remainders down to their floor, regardless of types.

Floor division was added to address the fact that the result of the current classic division model is dependent on operand types, and so can sometimes be difficult to anticipate in a dynamically-typed language like Python.

Due to possible backward compatibility issues, this is in a state of flux today. In version 2.3, / division works as described by default, and // floor division has been added to truncate result remainders to their floor regardless of types:

>>> (5 / 2), (5 / 2.0), (5 / -2.0), (5 / -2)
(2, 2.5, -2.5, -3)

>>> (5 // 2), (5 // 2.0), (5 // -2.0), (5 // -2)
(2, 2.0, -3.0, -3)

>>> (9 / 3), (9.0 / 3), (9 // 3), (9 // 3.0)
(3, 3.0, 3, 3.0)

In a future Python release, / division will likely be changed to return a true division result which always retains remainders, even for integers—for example, 1/2 will be 0.5, not 0, and 1//2 will still be 0.

Until this change is incorporated completely, you can see the way that the / will likely work in the future, by using a special import of the form: from __future__ import division. This turns the / operator into a true division (keeping remainders), but leaves // as is. Here's how / will eventually behave:

>>> from __future__ import division

>>> (5 / 2), (5 / 2.0), (5 / -2.0), (5 / -2)
(2.5, 2.5, -2.5, -2.5)

>>> (5 // 2), (5 // 2.0), (5 // -2.0), (5 // -2)
(2, 2.0, -3.0, -3)

>>> (9 / 3), (9.0 / 3), (9 // 3), (9 // 3.0)
(3.0, 3.0, 3, 3.0)

Watch for a simple prime number while loop example in Chapter 10, and a corresponding exercise at the end of Part IV, which illustrate the sort of code that may be impacted by this / change. In general, any code that depends on / truncating an integer result may be effected (use the new // instead). As we write this, this change is scheduled to occur in Python 3.0, but be sure to try these expressions in your version to see which behavior applies. Also stay tuned for more on the special from command used here in Chapter 18.

4.5.4 B itwise Operations

Besides the normal numeric operations (addition, subtraction, and so on), Python supports most of the numeric expressions available in the C language. For instance, here it's at work performing bitwise shift and Boolean operations:

>>> x = 1        # 0001
>>> x << 2       # Shift left 2 bits: 0100
4
>>> x | 2        # bitwise OR: 0011
3
>>> x & 1        # bitwise AND: 0001
1

In the first expression, a binary 1 (in base 2, 0001) is shifted left two slots to create a binary 4 (0100). The last two operations perform a binary or (0001|0010 = 0011), and a binary and (0001&0001 = 0001). Such bit masking operations allow us to encode multiple flags and other values within a single integer.

We won't go into much more detail on "bit-twiddling" here. It's supported if you need it, but be aware that it's often not as important in a high-level language such as Python as it is in a low-level language such as C. As a rule of thumb, if you find yourself wanting to flip bits in Python, you should think about which language you're really coding. In general, there are often better ways to encode information in Python than bit strings.[8]

[8] As for most rules, there are exceptions. For instance, if you interface with C libraries that expect bit strings to be passed in, this doesn't apply.

4.5.5 L ong Integers

Now for something more exotic: here's a look at long integers in action. When an integer literal ends with a letter L (or lowercase l), Python creates a long integer. In Python, a long integer can be arbitrarily big—it can have as many digits as you have room for in memory:

>>> 9999999999999999999999999999999999999L + 1
10000000000000000000000000000000000000L

The L at the end of the digit string tells Python to create a long integer object with unlimited precision. As of Python 2.2, even the letter L is largely optional—Python automatically converts normal integers up to long integers, whenever they overflow normal integer precision (usually 32 bits):

>>> 9999999999999999999999999999999999999 + 1
10000000000000000000000000000000000000L

Long integers are a convenient built-in tool. For instance, you can use them to count the national debt in pennies in Python directly (if you are so inclined and have enough memory on your computer). They are also why we were able to raise 2 to such large powers in the examples of Chapter 3:

>>> 2L ** 200
1606938044258990275541962092341162602522202993782792835301376L
>>>
>>> 2 ** 200
1606938044258990275541962092341162602522202993782792835301376L

Because Python must do extra work to support their extended precision, long integer math is usually substantially slower than normal integer math (which usually maps directly to the hardware). If you need the precision, it's built in for you to use; but there is a performance penalty.

A note on version skew: prior to Python 2.2, integers were not automatically converted up to long integers on overflow, so you really had to use the letter L to get the extended precision:

>>> 9999999999999999999999999999999999999 + 1         # Before 2.2
OverflowError: integer literal too large

>>> 9999999999999999999999999999999999999L + 1        # Before 2.2
10000000000000000000000000000000000000L

In Version 2.2 the L is mostly optional. In the future, it is possible that using the letter L may generate a warning. Because of that, you are probably best off letting Python convert up for you automatically when needed, and omitting the L.

4.5.6 Complex Numbers

C omplex numbers are a distinct core object type in Python. If you know what they are, you know why they are useful; if not, consider this section optional reading. Complex numbers are represented as two floating-point numbers—the real and imaginary parts—and are coded by adding a j or J suffix to the imaginary part. We can also write complex numbers with a nonzero real part by adding the two parts with a +. For example, the complex number with a real part of 2 and an imaginary part of -3 is written: 2 + -3j. Here are some examples of complex math at work:

>>> 1j * 1J
(-1+0j)
>>> 2 + 1j * 3
(2+3j)
>>> (2+1j)*3
(6+3j)

Complex numbers also allow us to extract their parts as attributes, support all the usual mathematical expressions, and may be processed with tools in the standard cmath module (the complex version of the standard math module). Complex numbers typically find roles in engineering-oriented programs. Since they are an advanced tool, check Python's language reference manual for additional details.

4.5.7 Hexadecimal and Octal Notation

As mentioned at the start of this section, Python integers can be coded in hexadecimal (base 16) and octal (base 8) notation, in addition to the normal base 10 decimal coding:

  • Octal literals have a leading 0, followed by a string of octal digits 0-7, each of which represents 3 bits.

  • Hexadecimal literals have a leading 0x or 0X, followed by a string of hex digits 0-9 and upper- or lowercase A-F, each of which stands for 4 bits.

Keep in mind that this is simply an alternative syntax for specifying the value of an integer object. For example, the following octal and hexadecimal literals produce normal integers, with the specified values:

>>> 01, 010, 0100              # Octal literals
(1, 8, 64)
>>> 0x01, 0x10, 0xFF           # Hex literals
(1, 16, 255)

Here, the octal value 0100 is decimal 64, and hex 0xFF is decimal 255. Python prints in decimal by default, but provides built-in functions that allow you to convert integers to their octal and hexadecimal digit strings:

>>> oct(64), hex(64), hex(255)
('0100', '0x40', '0xff')

The oct function converts decimal to octal, and hex to hexadecimal. To go the other way, the built-in int function converts a string of digits to an integer; an optional second argument lets you specify the numeric base:

>>> int('0100'), int('0100', 8), int('0x40', 16)
(100, 64, 64)

The eval function, which you'll meet later in this book, treats strings as though they were Python code. It therefore has a similar effect (but usually runs more slowly—it actually compiles and runs the string as a piece of a program):

>>> eval('100'), eval('0100'), eval('0x40')
(100, 64, 64)

Finally, you can also convert integers to octal and hexadecimal strings with a string formatting expression:

>>> "%o %x %X" % (64, 64, 255)
'100 40 FF'

This is covered in Chapter 4.

One warning before moving on, be careful to not begin a string of digits with a leading zero in Python, unless you really mean to code an octal value. Python will treat it as base 8, which may not work as you'd expect—010 is always decimal 8, not decimal 10 (despite what you might think!).

4.5.8 Other Numeric Tools

Python also provides both built-in functions and built-in modules for numeric processing. Here are examples of the built-in math module and a few built-in functions at work.

>>> import math
>>> math.pi, math.e
(3.1415926535897931, 2.7182818284590451)

>>> math.sin(2 * math.pi / 180)
0.034899496702500969

>>> abs(-42), 2**4, pow(2, 4)
(42, 16, 16)

>>> int(2.567), round(2.567), round(2.567, 2)
(2, 3.0, 2.5699999999999998)

The math module contains most of the tools in the C language's math library. As described earlier, the last output here will be just 2.57 if we say print.

Notice that built-in modules such as math must be imported, but built-in functions such as abs are always available without imports. In other words, modules are external components, but built-in functions live in an implied namespace, which Python automatically searches to find names used in your program. This namespace corresponds to the module called __builtin__. There is much more about name resolution in Part IV, Functions; for now, when you hear "module," think "import."

    [ Team LiB ] Previous Section Next Section