mirror of
https://github.com/python/cpython.git
synced 2025-08-04 17:08:35 +00:00
Merged revisions 60441-60474 via svnmerge from
svn+ssh://pythondev@svn.python.org/python/trunk ........ r60441 | christian.heimes | 2008-01-30 12:46:00 +0100 (Wed, 30 Jan 2008) | 1 line Removed unused var ........ r60448 | christian.heimes | 2008-01-30 18:21:22 +0100 (Wed, 30 Jan 2008) | 1 line Fixed some references leaks in sys. ........ r60450 | christian.heimes | 2008-01-30 19:58:29 +0100 (Wed, 30 Jan 2008) | 1 line The previous change was causing a segfault after multiple calls to Py_Initialize() and Py_Finalize(). ........ r60463 | raymond.hettinger | 2008-01-30 23:17:31 +0100 (Wed, 30 Jan 2008) | 1 line Update itertool recipes ........ r60464 | christian.heimes | 2008-01-30 23:54:18 +0100 (Wed, 30 Jan 2008) | 1 line Bug #1234: Fixed semaphore errors on AIX 5.2 ........ r60469 | raymond.hettinger | 2008-01-31 02:38:15 +0100 (Thu, 31 Jan 2008) | 6 lines Fix defect in __ixor__ which would get the wrong answer if the input iterable had a duplicate element (two calls to toggle() reverse each other). Borrow the correct code from sets.py. ........ r60470 | raymond.hettinger | 2008-01-31 02:42:11 +0100 (Thu, 31 Jan 2008) | 1 line Missing return ........ r60471 | jeffrey.yasskin | 2008-01-31 08:44:11 +0100 (Thu, 31 Jan 2008) | 4 lines Added more documentation on how mixed-mode arithmetic should be implemented. I also noticed and fixed a bug in Rational's forward operators (they were claiming all instances of numbers.Rational instead of just the concrete types). ........
This commit is contained in:
parent
4b8db419c2
commit
7b3ce6a17e
11 changed files with 275 additions and 60 deletions
|
@ -97,3 +97,144 @@ The numeric tower
|
|||
3-argument form of :func:`pow`, and the bit-string operations: ``<<``,
|
||||
``>>``, ``&``, ``^``, ``|``, ``~``. Provides defaults for :func:`float`,
|
||||
:attr:`Rational.numerator`, and :attr:`Rational.denominator`.
|
||||
|
||||
|
||||
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`
|
||||
implements :func:`hash` as follows::
|
||||
|
||||
def __hash__(self):
|
||||
if self.denominator == 1:
|
||||
# Get integers right.
|
||||
return hash(self.numerator)
|
||||
# Expensive check, but definitely correct.
|
||||
if self == float(self):
|
||||
return hash(float(self))
|
||||
else:
|
||||
# Use tuple's hash to avoid a high collision rate on
|
||||
# simple fractions.
|
||||
return hash((self.numerator, self.denominator))
|
||||
|
||||
|
||||
Adding More Numeric ABCs
|
||||
~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
|
||||
There are, of course, more possible ABCs for numbers, and this would
|
||||
be a poor hierarchy if it precluded the possibility of adding
|
||||
those. You can add ``MyFoo`` between :class:`Complex` and
|
||||
:class:`Real` with::
|
||||
|
||||
class MyFoo(Complex): ...
|
||||
MyFoo.register(Real)
|
||||
|
||||
|
||||
Implementing the arithmetic operations
|
||||
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
|
||||
We want to implement the arithmetic operations so 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. For subtypes of :class:`Integral`, this
|
||||
means that :meth:`__add__` and :meth:`__radd__` should be defined as::
|
||||
|
||||
class MyIntegral(Integral):
|
||||
|
||||
def __add__(self, other):
|
||||
if isinstance(other, MyIntegral):
|
||||
return do_my_adding_stuff(self, other)
|
||||
elif isinstance(other, OtherTypeIKnowAbout):
|
||||
return do_my_other_adding_stuff(self, other)
|
||||
else:
|
||||
return NotImplemented
|
||||
|
||||
def __radd__(self, other):
|
||||
if isinstance(other, MyIntegral):
|
||||
return do_my_adding_stuff(other, self)
|
||||
elif isinstance(other, OtherTypeIKnowAbout):
|
||||
return do_my_other_adding_stuff(other, self)
|
||||
elif isinstance(other, Integral):
|
||||
return int(other) + int(self)
|
||||
elif isinstance(other, Real):
|
||||
return float(other) + float(self)
|
||||
elif isinstance(other, Complex):
|
||||
return complex(other) + complex(self)
|
||||
else:
|
||||
return NotImplemented
|
||||
|
||||
|
||||
There are 5 different cases for a mixed-type operation on subclasses
|
||||
of :class:`Complex`. I'll refer to all of the above code that doesn't
|
||||
refer to ``MyIntegral`` and ``OtherTypeIKnowAbout`` as
|
||||
"boilerplate". ``a`` will be an instance of ``A``, which is a subtype
|
||||
of :class:`Complex` (``a : A <: Complex``), and ``b : B <:
|
||||
Complex``. I'll consider ``a + b``:
|
||||
|
||||
1. If ``A`` defines an :meth:`__add__` which accepts ``b``, all is
|
||||
well.
|
||||
2. If ``A`` falls back to the boilerplate code, and it were to
|
||||
return a value from :meth:`__add__`, we'd miss the possibility
|
||||
that ``B`` defines a more intelligent :meth:`__radd__`, so the
|
||||
boilerplate should return :const:`NotImplemented` from
|
||||
:meth:`__add__`. (Or ``A`` may not implement :meth:`__add__` at
|
||||
all.)
|
||||
3. Then ``B``'s :meth:`__radd__` gets a chance. If it accepts
|
||||
``a``, all is well.
|
||||
4. If it falls back to the boilerplate, there are no more possible
|
||||
methods to try, so this is where the default implementation
|
||||
should live.
|
||||
5. If ``B <: A``, Python tries ``B.__radd__`` before
|
||||
``A.__add__``. This is ok, because it was implemented with
|
||||
knowledge of ``A``, so it can handle those instances before
|
||||
delegating to :class:`Complex`.
|
||||
|
||||
If ``A<:Complex`` and ``B<:Real`` without sharing any other knowledge,
|
||||
then the appropriate shared operation is the one involving the built
|
||||
in :class:`complex`, and both :meth:`__radd__` s land there, so ``a+b
|
||||
== b+a``.
|
||||
|
||||
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::
|
||||
|
||||
def _operator_fallbacks(monomorphic_operator, fallback_operator):
|
||||
def forward(a, b):
|
||||
if isinstance(b, (int, long, Rational)):
|
||||
return monomorphic_operator(a, b)
|
||||
elif isinstance(b, float):
|
||||
return fallback_operator(float(a), b)
|
||||
elif isinstance(b, complex):
|
||||
return fallback_operator(complex(a), b)
|
||||
else:
|
||||
return NotImplemented
|
||||
forward.__name__ = '__' + fallback_operator.__name__ + '__'
|
||||
forward.__doc__ = monomorphic_operator.__doc__
|
||||
|
||||
def reverse(b, a):
|
||||
if isinstance(a, RationalAbc):
|
||||
# Includes ints.
|
||||
return monomorphic_operator(a, b)
|
||||
elif isinstance(a, numbers.Real):
|
||||
return fallback_operator(float(a), float(b))
|
||||
elif isinstance(a, numbers.Complex):
|
||||
return fallback_operator(complex(a), complex(b))
|
||||
else:
|
||||
return NotImplemented
|
||||
reverse.__name__ = '__r' + fallback_operator.__name__ + '__'
|
||||
reverse.__doc__ = monomorphic_operator.__doc__
|
||||
|
||||
return forward, reverse
|
||||
|
||||
def _add(a, b):
|
||||
"""a + b"""
|
||||
return Rational(a.numerator * b.denominator +
|
||||
b.numerator * a.denominator,
|
||||
a.denominator * b.denominator)
|
||||
|
||||
__add__, __radd__ = _operator_fallbacks(_add, operator.add)
|
||||
|
||||
# ...
|
Loading…
Add table
Add a link
Reference in a new issue