Modified Modules
Many of the already-existing modules have been modified in Python 3.0. I've picked the ones that
I think are the most important and interesting, but you should be aware that there are others, some of which might be the most important and interesting to
you.
The os Module
The os module gained some new functionality. It now handles open files and symlinks much better on *NIX systems. The
fchmod() and
fchown() functions can change the mode and owner of an open file; they work just like
chmod() and
chown() but accept the file object itself rather than its path. The
lchmod() function changes the mode of a symlink. In addition the
os.walk() function can now follow symlinks if the new
followlinks argument is
True (it's
False by default for backward compatibility).
The os.path module has a few useful modifications. The
splitext() function used to split a string on the last dot, which resulted in a strange splitting for *NIX-formatted dot files such as
.bashrc:
>>> os.path.splitext('.bashrc')
('', '.bashrc')
In Python 3.0, leading dots are ignored, which results in a more natural splitting:
>>> os.path.splitext('.bashrc')
('.bashrc', '')
A new function,
os.path.relpath returns the relative path from a given start path. This is very useful when dealing with lots of nested files and directories (which happens often):
>>> os.path.relpath('/a/b/c/d.txt', '/a/b')
'c/d.txt'
os.path.expandvars() used to be a *NIX function that expanded shell variables of the form
$VAR and
${VAR}. Now, it works on Windows too, and can expand environment variables of the form
%VAR% (the Windows shell naming convention):
>>> os.environ['PROJECTS'] = 'c:/projects'
>>> os.path.expandvars('%PROJECTS%/cool_project')
'c:/projects/cool_project'
The
os.path.expanduser function also works on Windows now. It uses the *NIX convention of converting the tilde (
~) symbol into the user's home directory:
>>> os.path.expanduser('~')
'C:\\Documents and Settings\\gigi'
The turtle Module
Python 3.0 supplies an extensive reimplementation of the turtle module. I had never even heard of the turtle module before, and I was pretty surprised to find out it's a Tkinter-based turtle graphics module similar to Logo. Logo is an educational programming language intended to simplify teaching programming to kids. The idea is that you have a "turtle" on-screen that you control with simple commands.
You can work with the turtle interactively from the command line. First, you need to create a turtle object, which automatically pops up a graphics window, and places the turtle in the center pointing to the right:
from turtle import Turtle
t = Turtle()
Subsequently, you can interact with the turtle and tell it what to do. The following commands draw a triangle on the screen by telling the turtle to move forward (
fd) 100 pixels, and then turn 120 degrees to the right (
rt), repeated three times:
>>> for i in range(3):
... t.fd(100)
... t.rt(120)
Here's a short turtle script that draws a yellow and purple checkered board. It uses several new commands and capabilities. The
pu (pen up) command tells the turtle to lift the pen (not draw). The
pd (pen down) command tells it to start drawing again. The
goto command causes the turtle to move to an absolute position (the center of the screen is 0,0). The
color command sets pen and fill colors. The
begin_fill and
end_fill wrap turtle commands and serve to fill a drawn shape. This time, the code turns the turtle to the left with the
lt command:
from turtle import Turtle
t = Turtle()
def draw_square(t, x, y, d, c):
print (x, y)
t.pu()
t.goto(x, y)
t.color('black', c)
t.pd()
t.begin_fill()
for i in range(4):
t.fd(d)
t.lt(90)
t.end_fill()
d = 100 # size of each square will be d x d pixels
n = 4 # will draw a checkered board of size n x n
# Make sure the whole thing is centered
offset = -(d * n / 2)
for x in range(n):
for y in range(n):
c = 'purple' if (x + y) % 2 == 0 else 'yellow'
draw_square(t, offset + x * d, offset + y * d, d, c)
 | |
Figure 1. Fancy Turtle Graphics: This complex looping shape was generated by a short Logo program translated to Python and uses the turtle module. |
Here's another program translated from Logo that draws a fancy loopy shape (see
Figure 1):
from turtle import Turtle
t = Turtle()
for i in range(36):
t.rt(10)
for j in range(36):
t.rt(10)
t.fd(20)
Finally, the
reset command simply erases everything already drawn and moves the turtle to the center of the screen, pointing to the right.
The turtle module is a lot of fun, but you can also use it for more serious purposes such as drawing charts and graphs, circles, and even text.
The ctypes Module
The ctypes module is a very slick foreign function interface. It became part of the standard library in Python 2.5 and continues to improve. It allows you to access C DLLs and shared libraries. For example, here's how you can display a standard Windows message box:
>>> import ctypes
>>> ctypes.windll.user32.MessageBoxW(0, 'ctypes rocks!', 'Title', 0)
This goes almost directly to the Win32 C API. On Unix/Linux/Mac OSX you access libraries with the cdll object. The following code snippet calls the
rand() function in the C runtime library (libc). The code first finds the libc in a cross-platform way (works on Linux and on OSX) using the
find_library() function. It then loads the library using the CDLL call and finally calls
libc.rand() in a loop.
import ctypes
from ctypes.util import find_library
libc_path = find_library('c')
libc = ctypes.CDLL(libc_path)
for i in range(4):
print(libc.rand() % 10)
The ctypes module now has a C99 Bool type. I'm not sure how useful it is. Perhaps you might need it if you have a C dynamic library that uses the C99 Bool type in its API as an input or output argument, or as a member in a struct/union. Here is how you create a
c_bool value from a Python int:
>>> from ctypes import c_bool
>>> c_bool(7)
c_bool(True)
You can pass virtually any Python value to construct a c_bool. The rules are simple: Python's
False is
False (duh!), every zero numeric value (int, float, complex, fractions.Fraction, decimal.Decimal) is
False,
None is
False, an empty string is
False. Everything else (including functions, objects and classes) is
True:
The following code lists all the
False values:
>>> import decimal
>>> import fractions
>>> from ctypes import c_bool
>>> c_bool(False)
c_bool(False)
>>> c_bool(None)
c_bool(False)
>>> c_bool('')
c_bool(False)
>>> c_bool(0)
c_bool(False)
>>> c_bool(0.0)
c_bool(False)
>>> c_bool(decimal.Decimal('0.0'))
c_bool(False)
>>> c_bool(fractions.Fraction(0, 5)
c_bool(False)
>>> c_bool(complex(0, 0))
c_bool(False)
And this code lists a representative sample of
True values:
>>> import decimal
>>> import fractions
>>> from ctypes import c_bool
>>> c_bool(True)
c_bool(True)
>>> c_bool('Something')
c_bool(True)
>>> c_bool(-7)
c_bool(True)
>>> c_bool(0.3)
c_bool(True)
>>> c_bool(5.5)
c_bool(True)
>>> c_bool(-4.4)
c_bool(True)
>>> c_bool(decimal.Decimal('3.0'))
c_bool(True)
>>> c_bool(decimal.Decimal('0.7'))
c_bool(True)
>>> c_bool(decimal.Decimal('-2.2'))
c_bool(True)
>>> c_bool(fractions.Fraction(0, 5)
c_bool(True)
>>> c_bool(complex(3, 0))
c_bool(True)
>>> c_bool(complex(0, 5))
c_bool(True)
>>> c_bool(complex(-11.22, -11.22))
c_bool(True)
You can use the
c_bool type just like a Python bool:
>>> assert(ctypes.c_bool(True))
>>> assert(not ctypes.c_bool(False))
The ctypes module always had arrays that you could slice using the
array[start:end] syntax. Now, you use the full slice syntax
array[start:end:step]. You can define a fixed-size array type by multiplying a ctypes data type by a fixed integer:
int_6_array_type = c_int * 6
The preceding code just creates a type; you need to instantiate it to get a usable array object. You can initialize ctypes arrays with values in the constructor or fill them with a default value (
0 for ints):
a = int_6_array_type()
for i in range(6):
assert a[i] == 0
a[i] = i
Slicing a ctypes array is just like Python's slicing. You can even use the
[::-1] idiom to reverse them:
>>> a[2:5]
[2, 3, 4]
>>> a[2:5:2]
[2, 4]
>>> a[::-1]
[5, 4, 3, 2, 1, 0]
The result of slicing a ctypes array is a Python list and not another ctypes array:
>>> type(a[1:4])
<class 'list'>
Another improvement is better handling of
errno (on *NIX systems) and
GetLastError()/
SetLastError() on Windows. In Python 2.5 you couldn't get to the actual value of
errno or
GetLastError(), because they were reset by other calls. Now, ctypes preserves a thread-local copy of this value if you load the library with the
use_errno/
use_last_error flag. After a call you may call
ctypes.get_errno() or
ctypes.get_last_error() to figure out what the error is.
To find out more about ctypes see
my earlier DevX article on Python 2.5.