Learning Python - Mark Lutz [86]
The oct function converts decimal to octal, hex to hexadecimal, and bin to binary. To go the other way, the built-in int function converts a string of digits to an integer, and an optional second argument lets you specify the numeric base:
>>> int('64'), int('100', 8), int('40', 16), int('1000000', 2)
(64, 64, 64, 64)
>>> int('0x40', 16), int('0b1000000', 2) # Literals okay too
(64, 64)
The eval function, which you’ll meet later in this book, treats strings as though they were Python code. Therefore, it has a similar effect (but usually runs more slowly—it actually compiles and runs the string as a piece of a program, and it assumes you can trust the source of the string being run; a clever user might be able to submit a string that deletes files on your machine!):
>>> eval('64'), eval('0o100'), eval('0x40'), eval('0b1000000')
(64, 64, 64, 64)
Finally, you can also convert integers to octal and hexadecimal strings with string formatting method calls and expressions:
>>> '{0:o}, {1:x}, {2:b}'.format(64, 64, 64)
'100, 40, 1000000'
>>> '%o, %x, %X' % (64, 255, 255)
'100, ff, FF'
String formatting is covered in more detail in Chapter 7.
Two notes before moving on. First, Python 2.6 users should remember that you can code octals with simply a leading zero, the original octal format in Python:
>>> 0o1, 0o20, 0o377 # New octal format in 2.6 (same as 3.0)
(1, 16, 255)
>>> 01, 020, 0377 # Old octal literals in 2.6 (and earlier)
(1, 16, 255)
In 3.0, the syntax in the second of these examples generates an error. Even though it’s not an error in 2.6, be careful not to begin a string of digits with a leading zero unless you really mean to code an octal value. Python 2.6 will treat it as base 8, which may not work as you’d expect—010 is always decimal 8 in 2.6, not decimal 10 (despite what you may or may not think!). This, along with symmetry with the hex and binary forms, is why the octal format was changed in 3.0—you must use 0o010 in 3.0, and probably should in 2.6.
Secondly, note that these literals can produce arbitrarily long integers. The following, for instance, creates an integer with hex notation and then displays it first in decimal and then in octal and binary with converters (run in 2.6 here to reveal the long precision):
>>> X = 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFF
>>> X
5192296858534827628530496329220095L
>>> oct(X)
'017777777777777777777777777777777777777L'
>>> bin(X)
'0b1111111111111111111111111111111111111111111111111111111111 ...and so on...
Speaking of binary digits, the next section shows tools for processing individual bits.
Bitwise Operations
Besides the normal numeric operations (addition, subtraction, and so on), Python supports most of the numeric expressions available in the C language. This includes operators that treat integers as strings of binary bits. For instance, here it is 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.
This is one area where the binary and hexadecimal number support in Python 2.6 and 3.0 become especially useful—they allow us to code and inspect numbers by bit-strings:
>>> X = 0b0001 # Binary literals
>>> X << 2 # Shift left
4
>>> bin(X << 2) # Binary digits string
'0b100'
>>> bin(X | 0b010) # Bitwise OR
'0b11'
>>> bin(X & 0b1) # Bitwise AND
'0b1'
>>> X = 0xFF # Hex literals
>>> bin(X)
'0b11111111'
>>> X ^ 0b10101010 # Bitwise XOR
85
>>> bin(X ^ 0b10101010)
'0b1010101'
>>> int('1010101', 2) # String to int per base
85
>>> hex(85) # Hex digit string
'0x55'
We won’t go into much more detail on “bit-twiddling” here. It’s supported if you need it, and it comes in handy