RSS Feed
Download our iPhone app
Browse DevX
Sign up for e-mail newsletters from DevX


A Developer's Guide to Python 3.0: Numbers, Strings, and Data : Page 4

Python 3.0 makes critical—and not-backwardly-compatible—changes to data types. Find out how these changes will affect your code.


PEP 3141: A Type Hierarchy for Numbers

Python 3.0 introduces a module called numbers (available in Python 2.6, too). This module contains abstract base classes (ABCs) for different number types. In effect, it creates a hierarchy of number types, where each type is a subtype of a more general type. This hierarchy was inspired by Scheme's numeric tower. There are five different ABCs: Number, Complex, Real, Rational, and Integral. Each one (except Number) is a sub-class of its predecessor. For example, an Integer is a subtype of Rational because you can think of every integer X as a rational number X/1, that is a rational number where X is the numerator and 1 is the denominator.

The Number base class doesn't correspond to any actual number type. It's there just in case you want to verify that a value is a number. Usually, in this case it is sufficient to check whether the argument is a subclass of numbers.Complex, but in rare cases you may want to introduce a new kind of number between Number and Complex by registering, and not by sub-classing Complex.

The Complex, Real, and Integer ABCs are implemented by the complex, float, and int types, respectively:

   >>> issubclass(int, numbers.Integral)
   >>> issubclass(float, numbers.Real)
   >>> issubclass(complex, numbers.Complex)
   >>> issubclass(complex, numbers.Real)
The Rational ABC is implemented by the fractions.Fraction type from the new fractions module. This module has also a gcd method for finding the greatest common denominator and a couple of conversion functions: from_float() and from_decimal(). It would have been cleaner and more consistent to add a rational numeric type to the language, but that ended up as a class in a module:

   >>> issubclass(fractions.Fraction, numbers.Rational)
   >>> fractions.gcd(Fraction(1,3), Fraction(1,2))
   Fraction(1, 6)
   >>> fractions.gcd(Fraction(2,6), Fraction(2,3))
   Fraction(1, 3)
   >>> fractions.gcd(6,9)
   >>> Fraction.from_float(0.5)
   Fraction(1, 2)
   >>> import math      
   >>> Fraction.from_float(math.pi)
   Fraction(884279719003555, 281474976710656)
   >>> Fraction.from_decimal(decimal.Decimal('3.5'))
   Fraction(7, 2)
The Decimal class from the decimal module for fixed point and floating point arithmetic (introduced in Python 2.4) doesn't participate in the party, and doesn't implement any of the number ABCs. The PEP-3141 mentions that, after consulting the authors of the decimal module, it was decided it was better not to integrate it at this time.

This semi-formalism of number types allows functions that expect certain types of numbers as arguments, so you can check and verify the arguments more easily.

For example, suppose you want to write a square function that takes a number and multiplies it by itself—but you want to stay in the realm of real numbers, and you always expect the result to be positive. Here's a trivial implementation that doesn't check types:

   def square(x):
     return x * x
The preceding code will fail to return a positive result if you call it with a complex number:

   >>> square(complex(0,1))
You could try an explicit check to ensure that the argument is an int or float, but that's somewhat clunky—especially if you want to support rational numbers too:

   >>> def square(x):
   ...   assert type(x) == int or \
   ...          type(x) == float or \
   ...          type(x) == Fraction
   ...   return x * x
   >>> from fractions import Fraction
   >>> square(5)
   >>> square(4.4)
   >>> square(Fraction(1,3))
   Fraction(1, 9)
   >>> square(complex(0,1))
   Traceback (most recent call last):
     File "<stdin>", line 1, in <module>
     File "<stdin>", line 4, in square
You may also come up with your own numeric types by either subclassing one of the existing types, or one of the ABCs from the numbers module. Then you'll be able to pass your new numbers to code that expects those specific types, and checks its arguments based on an ABC. This can be useful, for example, when you work with integers in a limited domain, or with real numbers that have fixed point semantics but limited precision. I won't give a full-fledged example here, because you need to implement a lot of methods to comply with any of the ABCs.

Close Icon
Thanks for your registration, follow us on our social networks to keep up-to-date