diff options
author | Christian Heimes <christian@cheimes.de> | 2008-01-31 14:31:45 (GMT) |
---|---|---|
committer | Christian Heimes <christian@cheimes.de> | 2008-01-31 14:31:45 (GMT) |
commit | 7b3ce6a17ea70e2acec46122e134097ce03d044a (patch) | |
tree | 51c11d64bc07786b0e1f9d5a8aaf8336a62a8d66 | |
parent | 4b8db419c278215ac1c79f4aac2b1453b13e8c83 (diff) | |
download | cpython-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.rst | 13 | ||||
-rw-r--r-- | Doc/library/numbers.rst | 141 | ||||
-rw-r--r-- | Lib/_abcoll.py | 18 | ||||
-rw-r--r-- | Lib/numbers.py | 8 | ||||
-rwxr-xr-x | Lib/rational.py | 86 | ||||
-rw-r--r-- | Objects/floatobject.c | 13 | ||||
-rw-r--r-- | Python/import.c | 2 | ||||
-rw-r--r-- | Python/marshal.c | 2 | ||||
-rw-r--r-- | Python/sysmodule.c | 41 | ||||
-rwxr-xr-x | configure | 8 | ||||
-rw-r--r-- | configure.in | 3 |
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; @@ -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) |