From 3feef61742facb3cbf53f9df1cb89062452597f2 Mon Sep 17 00:00:00 2001 From: Christian Heimes Date: Mon, 11 Feb 2008 06:19:17 +0000 Subject: [PATCH] Merged revisions 60481,60485,60489-60492,60494-60496,60498-60499,60501-60503,60505-60506,60508-60509,60523-60524,60532,60543,60545,60547-60548,60552,60554,60556-60559,60561-60562,60569,60571-60572,60574,60576-60583,60585-60586,60589,60591,60594-60595,60597-60598,60600-60601,60606-60612,60615,60617,60619-60621,60623-60625,60627-60629,60631,60633,60635,60647,60650,60652,60654,60656,60658-60659,60664-60666,60668-60670,60672,60676,60678,60680-60683,60685-60686,60688,60690,60692-60694,60697-60706,60708-60712,60714-60724 via svnmerge from svn+ssh://pythondev@svn.python.org/python/trunk ........ r60701 | georg.brandl | 2008-02-09 22:36:15 +0100 (Sat, 09 Feb 2008) | 2 lines Needs only 2.4 now. ........ r60702 | georg.brandl | 2008-02-09 22:38:54 +0100 (Sat, 09 Feb 2008) | 2 lines Docs are rst now. ........ r60703 | georg.brandl | 2008-02-09 23:00:00 +0100 (Sat, 09 Feb 2008) | 2 lines Fix link. ........ r60704 | georg.brandl | 2008-02-10 00:09:25 +0100 (Sun, 10 Feb 2008) | 2 lines Fix for newest doctools. ........ r60709 | raymond.hettinger | 2008-02-10 08:21:09 +0100 (Sun, 10 Feb 2008) | 1 line Clarify that decimal also supports fixed-point arithmetic. ........ r60710 | nick.coghlan | 2008-02-10 08:32:52 +0100 (Sun, 10 Feb 2008) | 1 line Add missing NEWS entry for r60695 ........ r60712 | mark.dickinson | 2008-02-10 15:58:38 +0100 (Sun, 10 Feb 2008) | 3 lines Turn classmethods into staticmethods, and avoid calling the constructor of subclasses of Rational. (See discussion in issue #1682.) ........ r60715 | mark.dickinson | 2008-02-10 16:19:58 +0100 (Sun, 10 Feb 2008) | 2 lines Typos in decimal comment and documentation ........ r60716 | skip.montanaro | 2008-02-10 16:31:54 +0100 (Sun, 10 Feb 2008) | 2 lines Get the saying right. ;-) ........ r60717 | skip.montanaro | 2008-02-10 16:32:16 +0100 (Sun, 10 Feb 2008) | 2 lines whoops - revert ........ r60718 | mark.dickinson | 2008-02-10 20:23:36 +0100 (Sun, 10 Feb 2008) | 2 lines Remove reference to Rational ........ r60719 | raymond.hettinger | 2008-02-10 21:35:16 +0100 (Sun, 10 Feb 2008) | 1 line Complete an open todo on pickletools -- add a pickle optimizer. ........ r60721 | mark.dickinson | 2008-02-10 22:29:51 +0100 (Sun, 10 Feb 2008) | 3 lines Rename rational.Rational to fractions.Fraction, to avoid name clash with numbers.Rational. See issue #1682 for related discussion. ........ r60722 | christian.heimes | 2008-02-11 03:26:22 +0100 (Mon, 11 Feb 2008) | 1 line The test requires the network resource ........ r60723 | mark.dickinson | 2008-02-11 04:11:55 +0100 (Mon, 11 Feb 2008) | 3 lines Put an extra space into the repr of a Fraction: Fraction(1, 2) instead of Fraction(1,2). ........ --- Demo/classes/README | 1 - Doc/README.txt | 4 +- Doc/conf.py | 22 ++-- Doc/extending/windows.rst | 2 +- Doc/library/decimal.rst | 22 +++- Doc/library/{rational.rst => fractions.rst} | 38 +++--- Doc/library/numbers.rst | 10 +- Doc/library/pickletools.rst | 7 ++ Doc/whatsnew/2.6.rst | 22 ++-- Lib/decimal.py | 2 +- Lib/{rational.py => fractions.py} | 113 +++++++++--------- Lib/pickletools.py | 31 ++++- Lib/test/test_builtin.py | 4 +- .../{test_rational.py => test_fractions.py} | 56 ++++----- Modules/_collectionsmodule.c | 2 + 15 files changed, 190 insertions(+), 146 deletions(-) rename Doc/library/{rational.rst => fractions.rst} (62%) rename Lib/{rational.py => fractions.py} (83%) rename Lib/test/{test_rational.py => test_fractions.py} (91%) diff --git a/Demo/classes/README b/Demo/classes/README index 1d41f6af337..e5bc2893eca 100644 --- a/Demo/classes/README +++ b/Demo/classes/README @@ -4,7 +4,6 @@ Complex.py Complex numbers Dates.py Date manipulation package by Tim Peters Dbm.py Wrapper around built-in dbm, supporting arbitrary values Range.py Example of a generator: re-implement built-in range() -Rat.py Rational numbers Rev.py Yield the reverse of a sequence Vec.py A simple vector class bitvec.py A bit-vector class by Jan-Hein B\"uhrman diff --git a/Doc/README.txt b/Doc/README.txt index a93542fed51..202a1ce305f 100644 --- a/Doc/README.txt +++ b/Doc/README.txt @@ -14,7 +14,7 @@ those familiar with the previous docs written in LaTeX. Building the docs ================= -You need to install Python 2.5.1 or higher (but Python 3.0 is not supported yet); +You need to install Python 2.4 or higher (but Python 3.0 is not supported yet); the toolset used to build the docs are written in Python. The toolset used to build the documentation is called *Sphinx*, it is not included in this tree, but maintained separately in the Python Subversion repository. Also @@ -55,7 +55,7 @@ Available make targets are: * "latex", which builds LaTeX source files that can be run with "pdflatex" to produce PDF documents. - + * "linkcheck", which checks all external references to see whether they are broken, redirected or malformed, and outputs this information to stdout as well as a plain-text (.txt) file. diff --git a/Doc/conf.py b/Doc/conf.py index 273c76c98ee..1c8dd71b494 100644 --- a/Doc/conf.py +++ b/Doc/conf.py @@ -38,17 +38,17 @@ today = '' today_fmt = '%B %d, %Y' # List of files that shouldn't be included in the build. -unused_files = [ - 'whatsnew/2.0.rst', - 'whatsnew/2.1.rst', - 'whatsnew/2.2.rst', - 'whatsnew/2.3.rst', - 'whatsnew/2.4.rst', - 'whatsnew/2.5.rst', - 'whatsnew/2.6.rst', - 'maclib/scrap.rst', - 'library/xmllib.rst', - 'library/xml.etree.rst', +unused_docs = [ + 'whatsnew/2.0', + 'whatsnew/2.1', + 'whatsnew/2.2', + 'whatsnew/2.3', + 'whatsnew/2.4', + 'whatsnew/2.5', + 'whatsnew/2.6', + 'maclib/scrap', + 'library/xmllib', + 'library/xml.etree', ] # Relative filename of the reference count data file. diff --git a/Doc/extending/windows.rst b/Doc/extending/windows.rst index a34ba2bf995..a0782a74842 100644 --- a/Doc/extending/windows.rst +++ b/Doc/extending/windows.rst @@ -179,7 +179,7 @@ and add the following to the module initialization function:: MyObject_Type.ob_type = &PyType_Type; -Refer to section 3 of the `Python FAQ `_ for +Refer to section 3 of the `Python FAQ `_ for details on why you must do this. diff --git a/Doc/library/decimal.rst b/Doc/library/decimal.rst index f6b96b23b8c..422436f4882 100644 --- a/Doc/library/decimal.rst +++ b/Doc/library/decimal.rst @@ -1,6 +1,6 @@ -:mod:`decimal` --- Decimal floating point arithmetic -==================================================== +:mod:`decimal` --- Decimal fixed point and floating point arithmetic +==================================================================== .. module:: decimal :synopsis: Implementation of the General Decimal Arithmetic Specification. @@ -16,6 +16,11 @@ The :mod:`decimal` module provides support for decimal floating point arithmetic. It offers several advantages over the :class:`float` datatype: +* Decimal "is based on a floating-point model which was designed with people + in mind, and necessarily has a paramount guiding principle -- computers must + provide an arithmetic that works in the same way as the arithmetic that + people learn at school." -- excerpt from the decimal arithmetic specification. + * Decimal numbers can be represented exactly. In contrast, numbers like :const:`1.1` do not have an exact representation in binary floating point. End users typically would not expect :const:`1.1` to display as @@ -25,7 +30,7 @@ arithmetic. It offers several advantages over the :class:`float` datatype: + 0.1 + 0.1 - 0.3`` is exactly equal to zero. In binary floating point, the result is :const:`5.5511151231257827e-017`. While near to zero, the differences prevent reliable equality testing and differences can accumulate. For this - reason, decimal would be preferred in accounting applications which have strict + reason, decimal is preferred in accounting applications which have strict equality invariants. * The decimal module incorporates a notion of significant places so that ``1.30 @@ -50,6 +55,13 @@ arithmetic. It offers several advantages over the :class:`float` datatype: standards. While the built-in float type exposes only a modest portion of its capabilities, the decimal module exposes all required parts of the standard. When needed, the programmer has full control over rounding and signal handling. + This includes an option to enforce exact arithmetic by using exceptions + to block any inexact operations. + +* The decimal module was designed to support "without prejudice, both exact + unrounded decimal arithmetic (sometimes called fixed-point arithmetic) + and rounded floating-point arithmetic." -- excerpt from the decimal + arithmetic specification. The module design is centered around three concepts: the decimal number, the context for arithmetic, and signals. @@ -832,7 +844,7 @@ described below. In addition, the module provides three pre-made contexts: :const:`ROUND_HALF_EVEN`. All flags are cleared. No traps are enabled (so that exceptions are not raised during computations). - Because the trapped are disabled, this context is useful for applications that + Because the traps are disabled, this context is useful for applications that prefer to have result value of :const:`NaN` or :const:`Infinity` instead of raising exceptions. This allows an application to complete a run in the presence of conditions that would otherwise halt the program. @@ -1245,7 +1257,7 @@ quiet or signaling :const:`NaN` always returns :const:`False` (even when doing :const:`True`. An attempt to compare two Decimals using any of the ``<``, ``<=``, ``>`` or ``>=`` operators will raise the :exc:`InvalidOperation` signal if either operand is a :const:`NaN`, and return :const:`False` if this signal is -trapped. Note that the General Decimal Arithmetic specification does not +not trapped. Note that the General Decimal Arithmetic specification does not specify the behavior of direct comparisons; these rules for comparisons involving a :const:`NaN` were taken from the IEEE 854 standard (see Table 3 in section 5.7). To ensure strict standards-compliance, use the :meth:`compare` diff --git a/Doc/library/rational.rst b/Doc/library/fractions.rst similarity index 62% rename from Doc/library/rational.rst rename to Doc/library/fractions.rst index 8ed702f5f8c..5f30caf138d 100644 --- a/Doc/library/rational.rst +++ b/Doc/library/fractions.rst @@ -1,28 +1,28 @@ -:mod:`rational` --- Rational numbers +:mod:`fractions` --- Rational numbers ==================================== -.. module:: rational +.. module:: fractions :synopsis: Rational numbers. .. moduleauthor:: Jeffrey Yasskin .. sectionauthor:: Jeffrey Yasskin .. versionadded:: 2.6 -The :mod:`rational` module defines an immutable, infinite-precision +The :mod:`fractions` module defines an immutable, infinite-precision Rational number class. -.. class:: Rational(numerator=0, denominator=1) - Rational(other_rational) - Rational(string) +.. class:: Fraction(numerator=0, denominator=1) + Fraction(other_fraction) + Fraction(string) The first version requires that *numerator* and *denominator* are instances of :class:`numbers.Integral` and returns a new - ``Rational`` representing ``numerator/denominator``. If + ``Fraction`` representing ``numerator/denominator``. If *denominator* is :const:`0`, raises a :exc:`ZeroDivisionError`. The - second version requires that *other_rational* is an instance of - :class:`numbers.Rational` and returns an instance of + second version requires that *other_fraction* is an instance of + :class:`numbers.Fraction` and returns an instance of :class:`Rational` with the same value. The third version expects a string of the form ``[-+]?[0-9]+(/[0-9]+)?``, optionally surrounded by spaces. @@ -31,39 +31,39 @@ Rational number class. :class:`numbers.Rational` and is immutable and hashable. -.. method:: Rational.from_float(flt) +.. method:: Fraction.from_float(flt) - This classmethod constructs a :class:`Rational` representing the + This classmethod constructs a :class:`Fraction` representing the exact value of *flt*, which must be a :class:`float`. Beware that - ``Rational.from_float(0.3)`` is not the same value as ``Rational(3, + ``Fraction.from_float(0.3)`` is not the same value as ``Rational(3, 10)`` -.. method:: Rational.from_decimal(dec) +.. method:: Fraction.from_decimal(dec) - This classmethod constructs a :class:`Rational` representing the + This classmethod constructs a :class:`Fraction` representing the exact value of *dec*, which must be a :class:`decimal.Decimal`. -.. method:: Rational.__floor__() +.. method:: Fraction.__floor__() Returns the greatest :class:`int` ``<= self``. Will be accessible through :func:`math.floor` in Py3k. -.. method:: Rational.__ceil__() +.. method:: Fraction.__ceil__() Returns the least :class:`int` ``>= self``. Will be accessible through :func:`math.ceil` in Py3k. -.. method:: Rational.__round__() - Rational.__round__(ndigits) +.. method:: Fraction.__round__() + Fraction.__round__(ndigits) The first version returns the nearest :class:`int` to ``self``, rounding half to even. The second version rounds ``self`` to the - nearest multiple of ``Rational(1, 10**ndigits)`` (logically, if + nearest multiple of ``Fraction(1, 10**ndigits)`` (logically, if ``ndigits`` is negative), again rounding half toward even. Will be accessible through :func:`round` in Py3k. diff --git a/Doc/library/numbers.rst b/Doc/library/numbers.rst index 1d543c8b4bd..d78595e76da 100644 --- a/Doc/library/numbers.rst +++ b/Doc/library/numbers.rst @@ -104,7 +104,7 @@ Notes for type implementors Implementors should be careful to make equal numbers equal and hash them to the same values. This may be subtle if there are two different -extensions of the real numbers. For example, :class:`rational.Rational` +extensions of the real numbers. For example, :class:`fractions.Fraction` implements :func:`hash` as follows:: def __hash__(self): @@ -199,11 +199,11 @@ in :class:`complex`, and both :meth:`__radd__` s land there, so ``a+b Because most of the operations on any given type will be very similar, it can be useful to define a helper function which generates the forward and reverse instances of any given operator. For example, -:class:`rational.Rational` uses:: +:class:`fractions.Fraction` uses:: def _operator_fallbacks(monomorphic_operator, fallback_operator): def forward(a, b): - if isinstance(b, (int, long, Rational)): + if isinstance(b, (int, long, Fraction)): return monomorphic_operator(a, b) elif isinstance(b, float): return fallback_operator(float(a), b) @@ -215,7 +215,7 @@ forward and reverse instances of any given operator. For example, forward.__doc__ = monomorphic_operator.__doc__ def reverse(b, a): - if isinstance(a, RationalAbc): + if isinstance(a, Rational): # Includes ints. return monomorphic_operator(a, b) elif isinstance(a, numbers.Real): @@ -231,7 +231,7 @@ forward and reverse instances of any given operator. For example, def _add(a, b): """a + b""" - return Rational(a.numerator * b.denominator + + return Fraction(a.numerator * b.denominator + b.numerator * a.denominator, a.denominator * b.denominator) diff --git a/Doc/library/pickletools.rst b/Doc/library/pickletools.rst index 3fc38ff237f..3dc06acd413 100644 --- a/Doc/library/pickletools.rst +++ b/Doc/library/pickletools.rst @@ -33,3 +33,10 @@ probably won't find the :mod:`pickletools` module relevant. the opcode's argument; *pos* is the position at which this opcode is located. *pickle* can be a string or a file-like object. +.. function:: optimize(picklestring) + + Returns a new equivalent pickle string after eliminating unused ``PUT`` + opcodes. The optimized pickle is shorter, takes less transmission time, + requires less storage space, and unpickles more efficiently. + + .. versionadded:: 2.6 diff --git a/Doc/whatsnew/2.6.rst b/Doc/whatsnew/2.6.rst index cbc8b8fdacb..d37c5ac3042 100644 --- a/Doc/whatsnew/2.6.rst +++ b/Doc/whatsnew/2.6.rst @@ -578,8 +578,8 @@ and comparisons. :class:`Rational` numbers derive from :class:`Real`, have :attr:`numerator` and :attr:`denominator` properties, and can be -converted to floats. Python 2.6 adds a simple rational-number class -in the :mod:`rational` module. +converted to floats. Python 2.6 adds a simple rational-number class, +:class:`Fraction`, in the :mod:`fractions` module. :class:`Integral` numbers derive from :class:`Rational`, and can be shifted left and right with ``<<`` and ``>>``, @@ -598,29 +598,29 @@ one, :func:`trunc`, that's been backported to Python 2.6. -The Rational Module +The Fraction Module -------------------------------------------------- To fill out the hierarchy of numeric types, a rational-number class -has been added as the :mod:`rational` module. Rational numbers are +has been added as the :mod:`fractions` module. Rational numbers are represented as a fraction; rational numbers can exactly represent numbers such as two-thirds that floating-point numbers can only approximate. -The :class:`Rational` constructor takes two :class:`Integral` values +The :class:`Fraction` constructor takes two :class:`Integral` values that will be the numerator and denominator of the resulting fraction. :: - >>> from rational import Rational - >>> a = Rational(2, 3) - >>> b = Rational(2, 5) + >>> from fractions import Fraction + >>> a = Fraction(2, 3) + >>> b = Fraction(2, 5) >>> float(a), float(b) (0.66666666666666663, 0.40000000000000002) >>> a+b - rational.Rational(16,15) + Fraction(16, 15) >>> a/b - rational.Rational(5,3) + Fraction(5, 3) -The :mod:`rational` module is based upon an implementation by Sjoerd +The :mod:`fractions` module is based upon an implementation by Sjoerd Mullender that was in Python's :file:`Demo/classes/` directory for a long time. This implementation was significantly updated by Jeffrey Yaskin. diff --git a/Lib/decimal.py b/Lib/decimal.py index 55faf993bec..873f7c069e0 100644 --- a/Lib/decimal.py +++ b/Lib/decimal.py @@ -802,7 +802,7 @@ class Decimal(_numbers.Real, _numbers.Inexact): # != comparisons involving a NaN always return True # <, >, <= and >= comparisons involving a (quiet or signaling) # NaN signal InvalidOperation, and return False if the - # InvalidOperation is trapped. + # InvalidOperation is not trapped. # # This behavior is designed to conform as closely as possible to # that specified by IEEE 754. diff --git a/Lib/rational.py b/Lib/fractions.py similarity index 83% rename from Lib/rational.py rename to Lib/fractions.py index 6002964a88c..25b9f020bf7 100755 --- a/Lib/rational.py +++ b/Lib/fractions.py @@ -1,16 +1,15 @@ # Originally contributed by Sjoerd Mullender. # Significantly modified by Jeffrey Yasskin . -"""Rational, infinite-precision, real numbers.""" +"""Fraction, infinite-precision, real numbers.""" import math import numbers import operator import re -__all__ = ["Rational"] +__all__ = ["Fraction"] -RationalAbc = numbers.Rational def gcd(a, b): @@ -38,15 +37,15 @@ _RATIONAL_FORMAT = re.compile(r""" """, re.VERBOSE) -class Rational(RationalAbc): +class Fraction(numbers.Rational): """This class implements rational numbers. - Rational(8, 6) will produce a rational number equivalent to + Fraction(8, 6) will produce a rational number equivalent to 4/3. Both arguments must be Integral. The numerator defaults to 0 - and the denominator defaults to 1 so that Rational(3) == 3 and - Rational() == 0. + and the denominator defaults to 1 so that Fraction(3) == 3 and + Fraction() == 0. - Rationals can also be constructed from strings of the form + Fraction can also be constructed from strings of the form '[-+]?[0-9]+((/|.)[0-9]+)?', optionally surrounded by spaces. """ @@ -61,7 +60,7 @@ class Rational(RationalAbc): numerator/denominator pair. """ - self = super(Rational, cls).__new__(cls) + self = super(Fraction, cls).__new__(cls) if denominator == 1: if isinstance(numerator, str): @@ -69,7 +68,7 @@ class Rational(RationalAbc): input = numerator m = _RATIONAL_FORMAT.match(input) if m is None: - raise ValueError('Invalid literal for Rational: ' + input) + raise ValueError('Invalid literal for Fraction: ' + input) numerator = m.group('num') decimal = m.group('decimal') if decimal: @@ -86,7 +85,7 @@ class Rational(RationalAbc): numerator = -numerator elif (not isinstance(numerator, numbers.Integral) and - isinstance(numerator, RationalAbc)): + isinstance(numerator, numbers.Rational)): # Handle copies from other rationals. other_rational = numerator numerator = other_rational.numerator @@ -94,11 +93,11 @@ class Rational(RationalAbc): if (not isinstance(numerator, numbers.Integral) or not isinstance(denominator, numbers.Integral)): - raise TypeError("Rational(%(numerator)s, %(denominator)s):" + raise TypeError("Fraction(%(numerator)s, %(denominator)s):" " Both arguments must be integral." % locals()) if denominator == 0: - raise ZeroDivisionError('Rational(%s, 0)' % numerator) + raise ZeroDivisionError('Fraction(%s, 0)' % numerator) g = gcd(numerator, denominator) self._numerator = int(numerator // g) @@ -109,7 +108,7 @@ class Rational(RationalAbc): def from_float(cls, f): """Converts a finite float to a rational number, exactly. - Beware that Rational.from_float(0.3) != Rational(3, 10). + Beware that Fraction.from_float(0.3) != Fraction(3, 10). """ if not isinstance(f, float): @@ -141,7 +140,7 @@ class Rational(RationalAbc): @classmethod def from_continued_fraction(cls, seq): - 'Build a Rational from a continued fraction expessed as a sequence' + 'Build a Fraction from a continued fraction expessed as a sequence' n, d = 1, 0 for e in reversed(seq): n, d = d, n @@ -168,7 +167,7 @@ class Rational(RationalAbc): if self.denominator <= max_denominator: return self cf = self.as_continued_fraction() - result = Rational(0) + result = Fraction(0) for i in range(1, len(cf)): new = self.from_continued_fraction(cf[:i]) if new.denominator > max_denominator: @@ -186,7 +185,7 @@ class Rational(RationalAbc): def __repr__(self): """repr(self)""" - return ('Rational(%r,%r)' % (self.numerator, self.denominator)) + return ('Fraction(%r,%r)' % (self.numerator, self.denominator)) def __str__(self): """str(self)""" @@ -206,13 +205,13 @@ class Rational(RationalAbc): that mixed-mode operations either call an implementation whose author knew about the types of both arguments, or convert both to the nearest built in type and do the operation there. In - Rational, that means that we define __add__ and __radd__ as: + Fraction, that means that we define __add__ and __radd__ as: def __add__(self, other): # Both types have numerators/denominator attributes, # so do the operation directly - if isinstance(other, (int, Rational)): - return Rational(self.numerator * other.denominator + + if isinstance(other, (int, Fraction)): + return Fraction(self.numerator * other.denominator + other.numerator * self.denominator, self.denominator * other.denominator) # float and complex don't have those operations, but we @@ -227,8 +226,8 @@ class Rational(RationalAbc): def __radd__(self, other): # radd handles more types than add because there's # nothing left to fall back to. - if isinstance(other, RationalAbc): - return Rational(self.numerator * other.denominator + + if isinstance(other, numbers.Rational): + return Fraction(self.numerator * other.denominator + other.numerator * self.denominator, self.denominator * other.denominator) elif isinstance(other, Real): @@ -239,32 +238,32 @@ class Rational(RationalAbc): There are 5 different cases for a mixed-type addition on - Rational. I'll refer to all of the above code that doesn't - refer to Rational, float, or complex as "boilerplate". 'r' - will be an instance of Rational, which is a subtype of - RationalAbc (r : Rational <: RationalAbc), and b : B <: + Fraction. I'll refer to all of the above code that doesn't + refer to Fraction, float, or complex as "boilerplate". 'r' + will be an instance of Fraction, which is a subtype of + Rational (r : Fraction <: Rational), and b : B <: Complex. The first three involve 'r + b': - 1. If B <: Rational, int, float, or complex, we handle + 1. If B <: Fraction, int, float, or complex, we handle that specially, and all is well. - 2. If Rational falls back to the boilerplate code, and it + 2. If Fraction falls back to the boilerplate code, and it were to return a value from __add__, we'd miss the possibility that B defines a more intelligent __radd__, so the boilerplate should return NotImplemented from - __add__. In particular, we don't handle RationalAbc + __add__. In particular, we don't handle Rational here, even though we could get an exact answer, in case the other type wants to do something special. - 3. If B <: Rational, Python tries B.__radd__ before - Rational.__add__. This is ok, because it was - implemented with knowledge of Rational, so it can + 3. If B <: Fraction, Python tries B.__radd__ before + Fraction.__add__. This is ok, because it was + implemented with knowledge of Fraction, so it can handle those instances before delegating to Real or Complex. The next two situations describe 'b + r'. We assume that b - didn't know about Rational in its implementation, and that it + didn't know about Fraction in its implementation, and that it uses similar boilerplate code: - 4. If B <: RationalAbc, then __radd_ converts both to the + 4. If B <: Rational, then __radd_ converts both to the builtin rational type (hey look, that's us) and proceeds. 5. Otherwise, __radd__ tries to find the nearest common @@ -276,7 +275,7 @@ class Rational(RationalAbc): """ def forward(a, b): - if isinstance(b, (int, Rational)): + if isinstance(b, (int, Fraction)): return monomorphic_operator(a, b) elif isinstance(b, float): return fallback_operator(float(a), b) @@ -288,7 +287,7 @@ class Rational(RationalAbc): forward.__doc__ = monomorphic_operator.__doc__ def reverse(b, a): - if isinstance(a, RationalAbc): + if isinstance(a, numbers.Rational): # Includes ints. return monomorphic_operator(a, b) elif isinstance(a, numbers.Real): @@ -304,7 +303,7 @@ class Rational(RationalAbc): def _add(a, b): """a + b""" - return Rational(a.numerator * b.denominator + + return Fraction(a.numerator * b.denominator + b.numerator * a.denominator, a.denominator * b.denominator) @@ -312,7 +311,7 @@ class Rational(RationalAbc): def _sub(a, b): """a - b""" - return Rational(a.numerator * b.denominator - + return Fraction(a.numerator * b.denominator - b.numerator * a.denominator, a.denominator * b.denominator) @@ -320,13 +319,13 @@ class Rational(RationalAbc): def _mul(a, b): """a * b""" - return Rational(a.numerator * b.numerator, a.denominator * b.denominator) + return Fraction(a.numerator * b.numerator, a.denominator * b.denominator) __mul__, __rmul__ = _operator_fallbacks(_mul, operator.mul) def _div(a, b): """a / b""" - return Rational(a.numerator * b.denominator, + return Fraction(a.numerator * b.denominator, a.denominator * b.numerator) __truediv__, __rtruediv__ = _operator_fallbacks(_div, operator.truediv) @@ -357,14 +356,14 @@ class Rational(RationalAbc): result will be rational. """ - if isinstance(b, RationalAbc): + if isinstance(b, numbers.Rational): if b.denominator == 1: power = b.numerator if power >= 0: - return Rational(a.numerator ** power, + return Fraction(a.numerator ** power, a.denominator ** power) else: - return Rational(a.denominator ** -power, + return Fraction(a.denominator ** -power, a.numerator ** -power) else: # A fractional power will generally produce an @@ -379,8 +378,8 @@ class Rational(RationalAbc): # If a is an int, keep it that way if possible. return a ** b.numerator - if isinstance(a, RationalAbc): - return Rational(a.numerator, a.denominator) ** b + if isinstance(a, numbers.Rational): + return Fraction(a.numerator, a.denominator) ** b if b.denominator == 1: return a ** b.numerator @@ -388,16 +387,16 @@ class Rational(RationalAbc): return a ** float(b) def __pos__(a): - """+a: Coerces a subclass instance to Rational""" - return Rational(a.numerator, a.denominator) + """+a: Coerces a subclass instance to Fraction""" + return Fraction(a.numerator, a.denominator) def __neg__(a): """-a""" - return Rational(-a.numerator, a.denominator) + return Fraction(-a.numerator, a.denominator) def __abs__(a): """abs(a)""" - return Rational(abs(a.numerator), a.denominator) + return Fraction(abs(a.numerator), a.denominator) def __trunc__(a): """trunc(a)""" @@ -433,12 +432,12 @@ class Rational(RationalAbc): return floor + 1 shift = 10**abs(ndigits) # See _operator_fallbacks.forward to check that the results of - # these operations will always be Rational and therefore have + # these operations will always be Fraction and therefore have # round(). if ndigits > 0: - return Rational(round(self * shift), shift) + return Fraction(round(self * shift), shift) else: - return Rational(round(self / shift) * shift) + return Fraction(round(self / shift) * shift) def __hash__(self): """hash(self) @@ -461,7 +460,7 @@ class Rational(RationalAbc): def __eq__(a, b): """a == b""" - if isinstance(b, RationalAbc): + if isinstance(b, numbers.Rational): return (a.numerator == b.numerator and a.denominator == b.denominator) if isinstance(b, numbers.Complex) and b.imag == 0: @@ -488,7 +487,7 @@ class Rational(RationalAbc): if isinstance(b, float): b = a.from_float(b) try: - # XXX: If b <: Real but not <: RationalAbc, this is likely + # XXX: If b <: Real but not <: Rational, this is likely # to fall back to a float. If the actual values differ by # less than MIN_FLOAT, this could falsely call them equal, # which would make <= inconsistent with ==. Better ways of @@ -496,7 +495,7 @@ class Rational(RationalAbc): diff = a - b except TypeError: return NotImplemented - if isinstance(diff, RationalAbc): + if isinstance(diff, numbers.Rational): return op(diff.numerator, 0) return op(diff, 0) @@ -526,11 +525,11 @@ class Rational(RationalAbc): return (self.__class__, (str(self),)) def __copy__(self): - if type(self) == Rational: + if type(self) == Fraction: return self # I'm immutable; therefore I am my own clone return self.__class__(self.numerator, self.denominator) def __deepcopy__(self, memo): - if type(self) == Rational: + if type(self) == Fraction: return self # My components are also immutable return self.__class__(self.numerator, self.denominator) diff --git a/Lib/pickletools.py b/Lib/pickletools.py index 0665cd0bcab..1b6967ae463 100644 --- a/Lib/pickletools.py +++ b/Lib/pickletools.py @@ -14,9 +14,7 @@ import codecs import pickle import re -__all__ = ['dis', - 'genops', - ] +__all__ = ['dis', 'genops', 'optimize'] bytes_types = pickle.bytes_types @@ -1835,6 +1833,33 @@ def genops(pickle): assert opcode.name == 'STOP' break +############################################################################## +# A pickle optimizer. + +def optimize(p): + 'Optimize a pickle string by removing unused PUT opcodes' + gets = set() # set of args used by a GET opcode + puts = [] # (arg, startpos, stoppos) for the PUT opcodes + prevpos = None # set to pos if previous opcode was a PUT + for opcode, arg, pos in genops(p): + if prevpos is not None: + puts.append((prevarg, prevpos, pos)) + prevpos = None + if 'PUT' in opcode.name: + prevarg, prevpos = arg, pos + elif 'GET' in opcode.name: + gets.add(arg) + + # Copy the pickle string except for PUTS without a corresponding GET + s = [] + i = 0 + for arg, start, stop in puts: + j = stop if (arg in gets) else start + s.append(p[i:j]) + i = stop + s.append(p[i:]) + return ''.join(s) + ############################################################################## # A symbolic pickle disassembler. diff --git a/Lib/test/test_builtin.py b/Lib/test/test_builtin.py index b17133a1ceb..f781db3d7de 100644 --- a/Lib/test/test_builtin.py +++ b/Lib/test/test_builtin.py @@ -5,7 +5,7 @@ from test.test_support import fcmp, TESTFN, unlink, run_unittest, \ run_with_locale from operator import neg -import sys, warnings, random, collections, io, rational +import sys, warnings, random, collections, io, rational, fractions warnings.filterwarnings("ignore", "hex../oct.. of negative int", FutureWarning, __name__) warnings.filterwarnings("ignore", "integer argument expected", @@ -607,7 +607,7 @@ class BuiltinTest(unittest.TestCase): n, d = f.as_integer_ratio() self.assertEqual(float(n).__truediv__(d), f) - R = rational.Rational + R = fractions.Fraction self.assertEqual(R(0, 1), R(*float(0.0).as_integer_ratio())) self.assertEqual(R(5, 2), diff --git a/Lib/test/test_rational.py b/Lib/test/test_fractions.py similarity index 91% rename from Lib/test/test_rational.py rename to Lib/test/test_fractions.py index 92d8a14aee1..00fd549d723 100644 --- a/Lib/test/test_rational.py +++ b/Lib/test/test_fractions.py @@ -1,15 +1,15 @@ -"""Tests for Lib/rational.py.""" +"""Tests for Lib/fractions.py.""" from decimal import Decimal from test.test_support import run_unittest, verbose import math import operator -import rational +import fractions import unittest from copy import copy, deepcopy from pickle import dumps, loads -R = rational.Rational -gcd = rational.gcd +R = fractions.Fraction +gcd = fractions.gcd class GcdTest(unittest.TestCase): @@ -31,7 +31,7 @@ def _components(r): return (r.numerator, r.denominator) -class RationalTest(unittest.TestCase): +class FractionTest(unittest.TestCase): def assertTypedEquals(self, expected, actual): """Asserts that both the types and values are the same.""" @@ -60,7 +60,7 @@ class RationalTest(unittest.TestCase): self.assertEquals((7, 15), _components(R(7, 15))) self.assertEquals((10**23, 1), _components(R(10**23))) - self.assertRaisesMessage(ZeroDivisionError, "Rational(12, 0)", + self.assertRaisesMessage(ZeroDivisionError, "Fraction(12, 0)", R, 12, 0) self.assertRaises(TypeError, R, 1.5) self.assertRaises(TypeError, R, 1.5 + 3j) @@ -81,41 +81,41 @@ class RationalTest(unittest.TestCase): self.assertEquals((3, 5), _components(R(" .6 "))) self.assertRaisesMessage( - ZeroDivisionError, "Rational(3, 0)", + ZeroDivisionError, "Fraction(3, 0)", R, "3/0") self.assertRaisesMessage( - ValueError, "Invalid literal for Rational: 3/", + ValueError, "Invalid literal for Fraction: 3/", R, "3/") self.assertRaisesMessage( - ValueError, "Invalid literal for Rational: 3 /2", + ValueError, "Invalid literal for Fraction: 3 /2", R, "3 /2") self.assertRaisesMessage( # Denominators don't need a sign. - ValueError, "Invalid literal for Rational: 3/+2", + ValueError, "Invalid literal for Fraction: 3/+2", R, "3/+2") self.assertRaisesMessage( # Imitate float's parsing. - ValueError, "Invalid literal for Rational: + 3/2", + ValueError, "Invalid literal for Fraction: + 3/2", R, "+ 3/2") self.assertRaisesMessage( # Avoid treating '.' as a regex special character. - ValueError, "Invalid literal for Rational: 3a2", + ValueError, "Invalid literal for Fraction: 3a2", R, "3a2") self.assertRaisesMessage( # Only parse ordinary decimals, not scientific form. - ValueError, "Invalid literal for Rational: 3.2e4", + ValueError, "Invalid literal for Fraction: 3.2e4", R, "3.2e4") self.assertRaisesMessage( # Don't accept combinations of decimals and rationals. - ValueError, "Invalid literal for Rational: 3/7.2", + ValueError, "Invalid literal for Fraction: 3/7.2", R, "3/7.2") self.assertRaisesMessage( # Don't accept combinations of decimals and rationals. - ValueError, "Invalid literal for Rational: 3.2/7", + ValueError, "Invalid literal for Fraction: 3.2/7", R, "3.2/7") self.assertRaisesMessage( # Allow 3. and .3, but not . - ValueError, "Invalid literal for Rational: .", + ValueError, "Invalid literal for Fraction: .", R, ".") def testImmutable(self): @@ -136,7 +136,7 @@ class RationalTest(unittest.TestCase): def testFromFloat(self): self.assertRaisesMessage( - TypeError, "Rational.from_float() only takes floats, not 3 (int)", + TypeError, "Fraction.from_float() only takes floats, not 3 (int)", R.from_float, 3) self.assertEquals((0, 1), _components(R.from_float(-0.0))) @@ -152,19 +152,19 @@ class RationalTest(unittest.TestCase): inf = 1e1000 nan = inf - inf self.assertRaisesMessage( - TypeError, "Cannot convert inf to Rational.", + TypeError, "Cannot convert inf to Fraction.", R.from_float, inf) self.assertRaisesMessage( - TypeError, "Cannot convert -inf to Rational.", + TypeError, "Cannot convert -inf to Fraction.", R.from_float, -inf) self.assertRaisesMessage( - TypeError, "Cannot convert nan to Rational.", + TypeError, "Cannot convert nan to Fraction.", R.from_float, nan) def testFromDecimal(self): self.assertRaisesMessage( TypeError, - "Rational.from_decimal() only takes Decimals, not 3 (int)", + "Fraction.from_decimal() only takes Decimals, not 3 (int)", R.from_decimal, 3) self.assertEquals(R(0), R.from_decimal(Decimal("-0"))) self.assertEquals(R(5, 10), R.from_decimal(Decimal("0.5"))) @@ -174,16 +174,16 @@ class RationalTest(unittest.TestCase): R.from_decimal(Decimal("0." + "9" * 30))) self.assertRaisesMessage( - TypeError, "Cannot convert Infinity to Rational.", + TypeError, "Cannot convert Infinity to Fraction.", R.from_decimal, Decimal("inf")) self.assertRaisesMessage( - TypeError, "Cannot convert -Infinity to Rational.", + TypeError, "Cannot convert -Infinity to Fraction.", R.from_decimal, Decimal("-inf")) self.assertRaisesMessage( - TypeError, "Cannot convert NaN to Rational.", + TypeError, "Cannot convert NaN to Fraction.", R.from_decimal, Decimal("nan")) self.assertRaisesMessage( - TypeError, "Cannot convert sNaN to Rational.", + TypeError, "Cannot convert sNaN to Fraction.", R.from_decimal, Decimal("snan")) def testFromContinuedFraction(self): @@ -316,7 +316,7 @@ class RationalTest(unittest.TestCase): # Decimal refuses mixed comparisons. self.assertRaisesMessage( TypeError, - "unsupported operand type(s) for +: 'Rational' and 'Decimal'", + "unsupported operand type(s) for +: 'Fraction' and 'Decimal'", operator.add, R(3,11), Decimal('3.1415926')) self.assertNotEquals(R(5, 2), Decimal('2.5')) @@ -378,7 +378,7 @@ class RationalTest(unittest.TestCase): self.assertFalse(R(5, 2) == 2) def testStringification(self): - self.assertEquals("Rational(7,3)", repr(R(7, 3))) + self.assertEquals("Fraction(7,3)", repr(R(7, 3))) self.assertEquals("7/3", str(R(7, 3))) self.assertEquals("7", str(R(7, 1))) @@ -421,7 +421,7 @@ class RationalTest(unittest.TestCase): self.assertEqual(id(r), id(deepcopy(r))) def test_main(): - run_unittest(RationalTest, GcdTest) + run_unittest(FractionTest, GcdTest) if __name__ == '__main__': test_main() diff --git a/Modules/_collectionsmodule.c b/Modules/_collectionsmodule.c index 87d744dbee8..74b3ea28ff2 100644 --- a/Modules/_collectionsmodule.c +++ b/Modules/_collectionsmodule.c @@ -1182,6 +1182,8 @@ defdict_reduce(defdictobject *dd) static PyMethodDef defdict_methods[] = { {"__missing__", (PyCFunction)defdict_missing, METH_O, defdict_missing_doc}, + {"copy", (PyCFunction)defdict_copy, METH_NOARGS, + defdict_copy_doc}, {"__copy__", (PyCFunction)defdict_copy, METH_NOARGS, defdict_copy_doc}, {"__reduce__", (PyCFunction)defdict_reduce, METH_NOARGS,