summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorChristian Heimes <christian@cheimes.de>2008-01-31 14:31:45 (GMT)
committerChristian Heimes <christian@cheimes.de>2008-01-31 14:31:45 (GMT)
commit7b3ce6a17ea70e2acec46122e134097ce03d044a (patch)
tree51c11d64bc07786b0e1f9d5a8aaf8336a62a8d66
parent4b8db419c278215ac1c79f4aac2b1453b13e8c83 (diff)
downloadcpython-7b3ce6a17ea70e2acec46122e134097ce03d044a.zip
cpython-7b3ce6a17ea70e2acec46122e134097ce03d044a.tar.gz
cpython-7b3ce6a17ea70e2acec46122e134097ce03d044a.tar.bz2
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). ........
-rw-r--r--Doc/library/itertools.rst13
-rw-r--r--Doc/library/numbers.rst141
-rw-r--r--Lib/_abcoll.py18
-rw-r--r--Lib/numbers.py8
-rwxr-xr-xLib/rational.py86
-rw-r--r--Objects/floatobject.c13
-rw-r--r--Python/import.c2
-rw-r--r--Python/marshal.c2
-rw-r--r--Python/sysmodule.c41
-rwxr-xr-xconfigure8
-rw-r--r--configure.in3
11 files changed, 275 insertions, 60 deletions
diff --git a/Doc/library/itertools.rst b/Doc/library/itertools.rst
index a088683..58ed4fa 100644
--- a/Doc/library/itertools.rst
+++ b/Doc/library/itertools.rst
@@ -511,5 +511,16 @@ which incur interpreter overhead. ::
"grouper(3, 'abcdefg', 'x') --> ('a','b','c'), ('d','e','f'), ('g','x','x')"
return izip(*[chain(iterable, repeat(padvalue, n-1))]*n)
-
+ def roundrobin(*iterables):
+ "roundrobin('abc', 'd', 'ef') --> 'a', 'd', 'e', 'b', 'f', 'c'"
+ # Recipe contributed by George Sakkis
+ pending = len(iterables)
+ nexts = cycle(iter(it).next for it in iterables)
+ while pending:
+ try:
+ for next in nexts:
+ yield next()
+ except StopIteration:
+ pending -= 1
+ nexts = cycle(islice(nexts, pending))
diff --git a/Doc/library/numbers.rst b/Doc/library/numbers.rst
index 4202a50..1d543c8 100644
--- a/Doc/library/numbers.rst
+++ b/Doc/library/numbers.rst
@@ -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)
+
+ # ... \ No newline at end of file
diff --git a/Lib/_abcoll.py b/Lib/_abcoll.py
index 4ce3df4..005f437 100644
--- a/Lib/_abcoll.py
+++ b/Lib/_abcoll.py
@@ -300,16 +300,6 @@ class MutableSet(Set):
self.discard(value)
return value
- def toggle(self, value):
- """Return True if it was added, False if deleted."""
- # XXX This implementation is not thread-safe
- if value in self:
- self.discard(value)
- return False
- else:
- self.add(value)
- return True
-
def clear(self):
"""This is slow (creates N new iterators!) but effective."""
try:
@@ -330,9 +320,13 @@ class MutableSet(Set):
return self
def __ixor__(self, it: Iterable):
- # This calls toggle(), so if that is overridded, we call the override
+ if not isinstance(it, Set):
+ it = self._from_iterable(it)
for value in it:
- self.toggle(it)
+ if value in self:
+ self.discard(value)
+ else:
+ self.add(value)
return self
def __isub__(self, it: Iterable):
diff --git a/Lib/numbers.py b/Lib/numbers.py
index 6c3c3e1..4dd5ca7 100644
--- a/Lib/numbers.py
+++ b/Lib/numbers.py
@@ -291,7 +291,13 @@ class Rational(Real, Exact):
# Concrete implementation of Real's conversion to float.
def __float__(self):
- """float(self) = self.numerator / self.denominator"""
+ """float(self) = self.numerator / self.denominator
+
+ It's important that this conversion use the integer's "true"
+ division rather than casting one side to float before dividing
+ so that ratios of huge integers convert without overflowing.
+
+ """
return self.numerator / self.denominator
diff --git a/Lib/rational.py b/Lib/rational.py
index 8de8f23..06002a3 100755
--- a/Lib/rational.py
+++ b/Lib/rational.py
@@ -178,16 +178,6 @@ class Rational(RationalAbc):
else:
return '%s/%s' % (self.numerator, self.denominator)
- """ XXX This section needs a lot more commentary
-
- * Explain the typical sequence of checks, calls, and fallbacks.
- * Explain the subtle reasons why this logic was needed.
- * It is not clear how common cases are handled (for example, how
- does the ratio of two huge integers get converted to a float
- without overflowing the long-->float conversion.
-
- """
-
def _operator_fallbacks(monomorphic_operator, fallback_operator):
"""Generates forward and reverse operators given a purely-rational
operator and a function from the operator module.
@@ -195,10 +185,82 @@ class Rational(RationalAbc):
Use this like:
__op__, __rop__ = _operator_fallbacks(just_rational_op, operator.op)
+ In general, 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. In
+ Rational, that means that we define __add__ and __radd__ as:
+
+ def __add__(self, other):
+ if isinstance(other, (int, Rational)):
+ # Do the real operation.
+ return Rational(self.numerator * other.denominator +
+ other.numerator * self.denominator,
+ self.denominator * other.denominator)
+ # float and complex don't follow this protocol, and
+ # Rational knows about them, so special case them.
+ elif isinstance(other, float):
+ return float(self) + other
+ elif isinstance(other, complex):
+ return complex(self) + other
+ else:
+ # Let the other type take over.
+ return NotImplemented
+
+ 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 +
+ other.numerator * self.denominator,
+ self.denominator * other.denominator)
+ 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 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 <:
+ Complex. The first three involve 'r + b':
+
+ 1. If B <: Rational, int, float, or complex, we handle
+ that specially, and all is well.
+ 2. If Rational 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
+ 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
+ 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
+ uses similar boilerplate code:
+
+ 4. If B <: RationalAbc, 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
+ base ABC, and fall back to its builtin type. Since this
+ class doesn't subclass a concrete type, there's no
+ implementation to fall back to, so we need to try as
+ hard as possible to return an actual value, or the user
+ will get a TypeError.
+
"""
def forward(a, b):
- if isinstance(b, RationalAbc):
- # Includes ints.
+ if isinstance(b, (int, Rational)):
return monomorphic_operator(a, b)
elif isinstance(b, float):
return fallback_operator(float(a), b)
diff --git a/Objects/floatobject.c b/Objects/floatobject.c
index 8ef3261..5cb3201 100644
--- a/Objects/floatobject.c
+++ b/Objects/floatobject.c
@@ -106,15 +106,9 @@ static PyStructSequence_Desc floatinfo_desc = {
PyObject *
PyFloat_GetInfo(void)
{
- static PyObject* floatinfo;
+ PyObject* floatinfo;
int pos = 0;
- if (floatinfo != NULL) {
- Py_INCREF(floatinfo);
- return floatinfo;
- }
- PyStructSequence_InitType(&FloatInfoType, &floatinfo_desc);
-
floatinfo = PyStructSequence_New(&FloatInfoType);
if (floatinfo == NULL) {
return NULL;
@@ -143,8 +137,6 @@ PyFloat_GetInfo(void)
Py_CLEAR(floatinfo);
return NULL;
}
-
- Py_INCREF(floatinfo);
return floatinfo;
}
@@ -1601,6 +1593,9 @@ _PyFloat_Init(void)
/* Initialize floating point repr */
_PyFloat_DigitsInit();
#endif
+ /* Init float info */
+ if (FloatInfoType.tp_name == 0)
+ PyStructSequence_InitType(&FloatInfoType, &floatinfo_desc);
}
void
diff --git a/Python/import.c b/Python/import.c
index cce854f..77fe168 100644
--- a/Python/import.c
+++ b/Python/import.c
@@ -371,6 +371,8 @@ static char* sys_deletes[] = {
"path", "argv", "ps1", "ps2",
"last_type", "last_value", "last_traceback",
"path_hooks", "path_importer_cache", "meta_path",
+ /* misc stuff */
+ "flags", "float_info",
NULL
};
diff --git a/Python/marshal.c b/Python/marshal.c
index 4c0f088..175ac0e 100644
--- a/Python/marshal.c
+++ b/Python/marshal.c
@@ -482,7 +482,7 @@ r_object(RFILE *p)
{
/* NULL is a valid return value, it does not necessarily means that
an exception is set. */
- PyObject *v, *v2, *v3;
+ PyObject *v, *v2;
long i, n;
int type = r_byte(p);
PyObject *retval;
diff --git a/Python/sysmodule.c b/Python/sysmodule.c
index e536f0a..d3ec827 100644
--- a/Python/sysmodule.c
+++ b/Python/sysmodule.c
@@ -1131,8 +1131,6 @@ make_flags(void)
if (PyErr_Occurred()) {
return NULL;
}
-
- Py_INCREF(seq);
return seq;
}
@@ -1146,6 +1144,11 @@ _PySys_Init(void)
if (m == NULL)
return NULL;
sysdict = PyModule_GetDict(m);
+#define SET_SYS_FROM_STRING(key, value) \
+ v = value; \
+ if (v != NULL) \
+ PyDict_SetItemString(sysdict, key, v); \
+ Py_XDECREF(v)
{
/* XXX: does this work on Win/Win64? (see posix_fstat) */
@@ -1165,19 +1168,16 @@ _PySys_Init(void)
PyDict_GetItemString(sysdict, "displayhook"));
PyDict_SetItemString(sysdict, "__excepthook__",
PyDict_GetItemString(sysdict, "excepthook"));
- PyDict_SetItemString(sysdict, "version",
- v = PyUnicode_FromString(Py_GetVersion()));
- Py_XDECREF(v);
- PyDict_SetItemString(sysdict, "hexversion",
- v = PyLong_FromLong(PY_VERSION_HEX));
- Py_XDECREF(v);
+ SET_SYS_FROM_STRING("version",
+ PyUnicode_FromString(Py_GetVersion()));
+ SET_SYS_FROM_STRING("hexversion",
+ PyLong_FromLong(PY_VERSION_HEX));
svnversion_init();
- v = Py_BuildValue("(UUU)", "CPython", branch, svn_revision);
- PyDict_SetItemString(sysdict, "subversion", v);
- Py_XDECREF(v);
- PyDict_SetItemString(sysdict, "dont_write_bytecode",
- v = PyBool_FromLong(Py_DontWriteBytecodeFlag));
- Py_XDECREF(v);
+ SET_SYS_FROM_STRING("subversion",
+ Py_BuildValue("(UUU)", "CPython", branch,
+ svn_revision));
+ SET_SYS_FROM_STRING("dont_write_bytecode",
+ PyBool_FromLong(Py_DontWriteBytecodeFlag));
/*
* These release level checks are mutually exclusive and cover
* the field, so don't get too fancy with the pre-processor!
@@ -1192,12 +1192,6 @@ _PySys_Init(void)
s = "final";
#endif
-#define SET_SYS_FROM_STRING(key, value) \
- v = value; \
- if (v != NULL) \
- PyDict_SetItemString(sysdict, key, v); \
- Py_XDECREF(v)
-
SET_SYS_FROM_STRING("version_info",
Py_BuildValue("iiiUi", PY_MAJOR_VERSION,
PY_MINOR_VERSION,
@@ -1244,7 +1238,6 @@ _PySys_Init(void)
SET_SYS_FROM_STRING("winver",
PyUnicode_FromString(PyWin_DLLVersionString));
#endif
-#undef SET_SYS_FROM_STRING
if (warnoptions == NULL) {
warnoptions = PyList_New(0);
}
@@ -1255,12 +1248,14 @@ _PySys_Init(void)
PyDict_SetItemString(sysdict, "warnoptions", warnoptions);
}
- PyStructSequence_InitType(&FlagsType, &flags_desc);
- PyDict_SetItemString(sysdict, "flags", make_flags());
+ if (FlagsType.tp_name == 0)
+ PyStructSequence_InitType(&FlagsType, &flags_desc);
+ SET_SYS_FROM_STRING("flags", make_flags());
/* prevent user from creating new instances */
FlagsType.tp_init = NULL;
FlagsType.tp_new = NULL;
+#undef SET_SYS_FROM_STRING
if (PyErr_Occurred())
return NULL;
return m;
diff --git a/configure b/configure
index e530f5c..51a3331 100755
--- a/configure
+++ b/configure
@@ -1,5 +1,5 @@
#! /bin/sh
-# From configure.in Revision: 59829 .
+# From configure.in Revision: 60144 .
# Guess values for system-dependent variables and create Makefiles.
# Generated by GNU Autoconf 2.61 for python 3.0.
#
@@ -14603,6 +14603,12 @@ cat >>confdefs.h <<\_ACEOF
_ACEOF
;;
+ AIX/5)
+cat >>confdefs.h <<\_ACEOF
+#define HAVE_BROKEN_POSIX_SEMAPHORES 1
+_ACEOF
+
+ ;;
esac
{ echo "$as_me:$LINENO: checking if PTHREAD_SCOPE_SYSTEM is supported" >&5
diff --git a/configure.in b/configure.in
index 41e27a7..af0441c 100644
--- a/configure.in
+++ b/configure.in
@@ -1965,6 +1965,9 @@ if test "$posix_threads" = "yes"; then
SunOS/5.8) AC_DEFINE(HAVE_BROKEN_POSIX_SEMAPHORES, 1,
Define if the Posix semaphores do not work on your system)
;;
+ AIX/5) AC_DEFINE(HAVE_BROKEN_POSIX_SEMAPHORES, 1,
+ Define if the Posix semaphores do not work on your system)
+ ;;
esac
AC_MSG_CHECKING(if PTHREAD_SCOPE_SYSTEM is supported)