Python Cookbook 2Nd Edition Jun 1002005 [Electronic resources] نسخه متنی

اینجــــا یک کتابخانه دیجیتالی است

با بیش از 100000 منبع الکترونیکی رایگان به زبان فارسی ، عربی و انگلیسی

Python Cookbook 2Nd Edition Jun 1002005 [Electronic resources] - نسخه متنی

David Ascher, Alex Martelli, Anna Ravenscroft

| نمايش فراداده ، افزودن یک نقد و بررسی
افزودن به کتابخانه شخصی
ارسال به دوستان
جستجو در متن کتاب
بیشتر
تنظیمات قلم

فونت

اندازه قلم

+ - پیش فرض

حالت نمایش

روز نیمروز شب
جستجو در لغت نامه
بیشتر
لیست موضوعات
توضیحات
افزودن یادداشت جدید

Introduction


Credit: Gustavo Niemeyer, Facundo Batista

Today,
last weekend, next year. These terms sound so common. You have
probably wondered, at least once, about how deeply our lives are
involved in the very idea of time. The concept of time surrounds us,
and, as a consequence, it's also present in the vast
majority of software projects. Even very simple programs may have to
deal with timestamps, delays, timeouts, speed gauges, calendars, and
so on. As befits a general-purpose language that is proud to come
with "batteries included,"
Python's standard library offers solid support for
these application needs, and more support yet comes from third-party
modules and
packages.

Computing tasks involving money are another interesting topic that
catches our attention because it's so closely
related to our daily lives. Python 2.4 introduced support for decimal
numbers (and you can retrofit that support into 2.3, see http://www.taniquetil.com.ar/facundo/bdv/image/library/english/10241_get_decimall),
making Python a good option even for computations where you must
avoid using binary floats, as ones involving money so often are.

This chapter covers exactly these two topics, money and time.
According to the old saying, maybe we should claim the chapter is
really about a single topic, since after all, as
everybody knowstime is money!


The time Module


Python Standard
Library's time module lets Python
applications access a good portion of the time-related functionality
offered by the platform Python is running on. Your
platform's documentation for the equivalent
functions in the C library will therefore be useful, and some
peculiarities of different platforms will affect Python as well.

One of the most used functions from module time is
the one that obtains the current
timetime.time. This
function's return value may be a little cryptic for
the uninitiated: it's a floating-point number that
corresponds to the number of seconds passed since a fixed instant
called the epoch, which may change depending
on your platform but is usually midnight of January 1, 1970.

To check which epoch your platform uses, try, at any Python
interactive interpreter prompt:

>>> import time
>>> print time.asctime(time.gmtime(0))

Notice we're passing 0 (meaning 0 seconds after the
epoch) to the time.gmtime function.
time.gmtime converts any timestamp (in seconds
since the epoch) into a tuple that represents that precise instant of
time in human terms, without applying any kind of time zone
conversion (GMT stands for "Greenwich mean
time", an old but colorful way to refer to what is
now known as UTC, for "Coordinated Universal
Time"). You can also pass a timestamp (in seconds
since the epoch) to time.localtime, which applies
the current local notion of time zone.

It's important to understand the difference, since,
if you have a timestamp that is already offset
to represent a local time, passing it to the
time.localtime function will
not yield the expected resultunless
you're so lucky that your local time zone happens to
coincide with the UTC time zone, of course!

Here is a way to unpack a tuple representing the current local time:

year, month, mday, hour, minute, second, wday, yday = time.localtime( )

While valid, this code is not elegant, and it would certainly not be
practical to use it often. This kind of construct may be completely
avoided, since the tuples returned by the time functions let you
access their elements via meaningful attribute names. Obtaining the
current month then becomes a simple and elegant expression:

  time.localtime( ).tm_mon

Note that we omitted passing any argument to
localtime. When we call
localtime, gmtime, or
asctime without an argument, each of them
conveniently defaults to using the current time.


Two very
useful functions in module time are
strftime, which lets you build a string from a
time tuple, and strptime, which goes the other
way, parsing a string and producing a time tuple. Each of these two
functions accepts a format string that lets you specify exactly what
you want in the resulting string (or, respectively, what you expect
from the string you're parsing) in excruciating
detail. For all the formatting specifications that you can use in the
format strings you pass to these functions, see http://docs.python.org/lib/module-timel.

One last important function in module time is the
time.sleep function, which lets you introduce
delays in Python programs. Even though this
function's POSIX counterpart accepts only an integer
parameter, the Python equivalent supports a float and allows
sub-second delays to be achieved. For instance:

for i in range(10):
time.sleep(0.5)
print "Tick!"

This snippet will take about 5 seconds to execute, emitting
Tick! approximately twice per second.


Time and Date Objects


While module time is
quite useful, the Python Standard Library also includes the
datetime module, which supplies types that provide
better abstractions for the concepts of dates and
timesnamely, the types time,
date, and datetime.
Constructing instances of those types is quite elegant:

  today = datetime.date.today( )
birthday = datetime.date(1977, 5, 4) #May 4
currenttime = datetime.datetime.now( ).time( )
lunchtime = datetime.time(12, 00)
now = datetime.datetime.now( )
epoch = datetime.datetime(1970, 1, 1)
meeting = datetime.datetime(2005, 8, 3, 15, 30)

Further, as you'd expect, instances of these types
offer comfortable information access and useful operations through
their attributes and methods. The following statements create an
instance of the date type, representing the
current day, then obtain the same date in the next year, and finally
print the result in a dotted format:

  today = datetime.date.today( )
next_year = today.replace(year=today.year+1).strftime("%Y.%m.%d")
print next_year

Notice how the year was incremented, using the
replace method. Assigning to the attributes of
date and time instances may sound tempting, but these instances are
immutable (which is a good thing, because it means we can use the
instances as members in a set, or keys in a dictionary!), so new
instances must be created instead of changing existing ones.

Module datetime also provides basic support for
time deltas (differences between instants of
time; you can think of them as basically meaning
durations in time), through the
timedelta type. This type lets you change a given
date by incrementing or decrementing the date by a given time slice,
and it is also the result of taking the difference between times or
dates.

>>> import datetime
>>> NewYearsDay = datetime.date(2005, 01, 01)
>>> NewYearsEve = datetime.date(2004, 12, 31)
>>> oneday = NewYearsDay - NewYearsEve
>>> print oneday
1 day, 0:00:00
>>>

A timedelta instance is internally represented by
days, seconds, and microseconds, but you can construct
timedelta instances by passing any of these
arguments and also other multipliers, like minutes, hours and weeks.
Support for other kinds of deltas, like months, and years, is not
availableon purpose, since their meanings, and operation
results, are debatable. (This feature is, however, offered by the
third-party dateutil packagesee .)

datetime can be described as a
prudent or cautious design.
The decision of not implementing doubtful tasks, and tasks that may
need many different implementations in different systems, reflects
the strategy used to develop all of the module. This way, the module
offers good interfaces for most use cases, and, even more
importantly, a strong and coherent base for third-party modules to
build upon.

Another area where this cautious design strategy for
datetime shows starkly is the
module's time zone support. Even though
datetime offers nice ways to query and set time
zone information, they're not really useful without
an external source to provide nonabstract subclasses of the
tzinfo type. At least two third-party packages
provide time zone support for datetime:
dateutil, mentioned previously, and
pyTZ, available at http://sourceforge.net/projects/pytz/.


Decimal


decimal
is a Python Standard Library module, new in Python 2.4, which finally
brings decimal arithmetic to Python. Thanks to
decimal, we now have a decimal numeric data type,
with bounded precision and floating point. Let's
look at each of these three little phrases in more
detail:

Decimal numeric data type


The number is not stored in binary, but rather, as a sequence of
decimal digits.


With bounded precision


The number of digits each number stores is fixed. (It is a fixed
parameter of each decimal number object, but different decimal number
objects can be set to use different numbers of digits.)


Floating point


The decimal point does not have a fixed place. (To put it another
way: while the number has a fixed amount of digits in
total
, it does not have a fixed amount of digits
after the decimal point. If it did, it would be
a fixed-point, rather than
floating-point, numeric data type).



Such a data type has many uses (the big use case is as the basis for
money computations), particularly because
decimal.Decimal offers many other advantages over
standard binary float. The main advantage is that
all of the decimal numbers that the user can enter (which is to say,
all the decimal numbers with a finite number of digits) can be
represented exactly (in contrast, some of those numbers do not have
an exact representation in binary floating point):

>>> import decimal
>>> 1.1
1.1000000000000001
>>> 2.3
2.2999999999999998
>>> decimal.Decimal("1.1")
Decimal("1.1")
>>> decimal.Decimal("2.3")
Decimal("2.3")

The exactness of the representation carries over into arithmetic. In
binary floating point, for example:

>>> 0.1 + 0.1 + 0.1 - 0.3
5.5511151231257827e-17

Such differences are very close to zero, and yet they prevent
reliable equality testing; moreover, even tiny differences can
accumulate. For this reason, decimal should be
preferred to binary floats in accounting applications that have
strict equality requirements:

>>> d1 = decimal.Decimal("0.1")
>>> d3 = decimal.Decimal("0.3")
>>> d1 + d1 + d1 - d3
Decimal("0.0")

decimal.Decimal instances can be constructed from
integers, strings, or tuples. To create a
decimal.Decimal from a float,
first convert the float to a string. This
necessary step serves as an explicit reminder of the details of the
conversion, including representation error. Decimal numbers include
special values such as NaN (which stands for
"not a number"), positive and
negative Infinity, and -0. Once
constructed, a decimal.Decimal object is
immutable, just like any other number in Python.

The decimal module essentially implements the
rules of arithmetic that are taught in school. Up to a given working
precision, exact, unrounded results are given whenever possible:

>>> 0.9 / 10
0.089999999999999997
>>> decimal.Decimal("0.9") / decimal.Decimal(10)
Decimal("0.09")

Where the number of digits in a result exceeds the working precision,
the number is rounded according to the current rounding method.
Several rounding methods are available; the default is
round-half-even.

The decimal module incorporates the notion of
significant digits, so that, for example,
1.30+1.20 is 2.50. The trailing zero is kept to indicate
significance. This is the usual representation for monetary
applications. For multiplication, the
"schoolbook" approach uses all the
figures in the multiplicands:

>>> decimal.Decimal("1.3") * decimal.Decimal("1.2")
Decimal("1.56")
>>> decimal.Decimal("1.30") * decimal.Decimal("1.20")
Decimal("1.5600")

In addition to the standard numeric properties that
decimal objects share with other built-in number
types, such as float and int,
decimal objects also have several specialized
methods. Check the docs for all of the methods, with details and
examples.

The decimal data type works within a
context, where some configuration aspects are
set. Each thread has its own current context (having a separate
context per thread means that each thread may make changes without
interfering with other threads); the current
thread's current context is accessed or changed
using functions getcontext and
setcontext from the decimal
module.

Unlike hardware-based binary floating point, the precision of the
decimal module can be set by users (defaulting to
28 places). It can be set to be as large as needed for a given
problem:

>>> decimal.getcontext( ).prec = 6            # set the precision to 6...
>>> decimal.Decimal(1) / decimal.Decimal(7)
Decimal("0.142857")
>>> decimal.getcontext( ).prec = 60 # ...and to 60 digits
>>> decimal.Decimal(1) / decimal.Decimal(7)
Decimal("0.142857142857142857142857142857142857142857142857142857142857")

Not everything in decimal can be as simple and
elementary as shown so far, of course. Essentially,
decimal implements the standards for general
decimal arithmetic which you can study in detail at http://www2.hursley.ibm.com/decimal/. In
particular, this means that decimal supports the
concept of signals. Signals represent abnormal
conditions arising from computations (e.g., 1/0,
0/0, Infinity/Infinity).
Depending on the needs of each specific application, signals may be
ignored, considered as informational, or treated as exceptions. For
each signal, there is a flag and a trap enabler. When a signal is
encountered, its flag is incremented from zero, and then, if the trap
enabler is set to one, an exception is raised. This gives programmers
a great deal of power and flexibility in configuring
decimal to meet their exact needs.

Given all of these advantages for decimal, why
would someone want to stick with float? Indeed, is
there any reason why Python (like just about every other widespread
language, with Cobol and Rexx the two major exceptions that easily
come to mind) originally adopted floating-point binary numbers as its
default (or only) noninteger data type? Of coursemany reasons
can be provided, and they're all spelled
speed! Consider:

$ python -mtimeit -s'from decimal import Decimal as D' 'D("1.2")+D("3.4")'
10000 loops, best of 3: 191 usec per loop
$ python -mtimeit -s'from decimal import Decimal as D' '1.2+3.4'
1000000 loops, best of 3: 0.339 usec per loop

This basically translates to: on this machine (an old Athlon 1.2 GHz
PC running Linux), Python can perform almost 3 million sums per
second on floats (using the PC's
arithmetic hardware), but only a bit more than 5 thousand sums per
second on Decimals (all done in software and with
all the niceties shown previously).

Essentially, if your application must sum many tens of millions of
noninteger numbers, you had better stick with
float! When an average machine was a thousand
times slower than it is today (and it wasn't
all that long ago!), such limitations hit even
applications doing relatively small amounts of computation, if the
applications ran on reasonably cheap machines (again, we see time and
money both playing a role!). Rexx and Cobol were born on mainframes
that were not quite as fast as today's cheapest PCs
but thousands of times more expensive. Purchasers of such mainframes
could afford nice and friendly decimal
arithmetic, but most other languages, born on more reasonably priced
machines (or meant for computationally intensive tasks), just
couldn't.

Fortunately, relatively few applications actually need to perform so
much arithmetic on non-integers as to give any observable performance
problems on today's typical machines. Thus, today,
most applications can actually take advantage of
decimal's many beneficial
aspects, including applications that must continue to use Python 2.3,
even though decimal is in the Python Standard
Library only since version 2.4. To learn how you can easily integrate
decimal into Python 2.3, see http://www.taniquetil.com.ar/facundo/bdv/image/library/english/10241_get_decimall.


    / 394