On 2 February 2012 03:47, Nick Coghlan <ncogh...@gmail.com> wrote: > Rather than being timestamp specific, such a protocol would be a > general numeric protocol. If (integer, numerator, denominator) is used > (i.e. a "mixed number" in mathematical terms), then "__from_mixed__" > would be an appropriate name. If (integer, fractional, exponent) is > used (i.e. a fixed point notation), then "__from_fixed__" would work. > > # Algorithm for a "from mixed numbers" protocol, assuming division > doesn't lose precision... > def __from_mixed__(cls, integer, numerator, denominator): > return cls(integer) + cls(numerator) / cls(denominator) > > # Algorithm for a "from fixed point" protocol, assuming negative > exponents don't lose precision... > def __from_fixed__(cls, integer, mantissa, base, exponent): > return cls(integer) + cls(mantissa) * cls(base) ** cls(exponent) > > >From a *usage* point of view, this idea is actually the same as the > proposal currently in the PEP. The difference is that instead of > adding custom support for a few particular types directly to time and > os, it instead defines a more general purpose protocol that covers not > only this use case, but also any other situation where high precision > fractions are relevant. > > One interesting question with a named protocol approach is whether > such a protocol should *require* explicit support, or if it should > fall back to the underlying mathematical operations. Since the > conversions to float and int in the timestamp case are already known > to be lossy, permitting lossy conversion via the mathematical > equivalents seems reasonable, suggesting possible protocol definitions > as follows: > > # Algorithm for a potentially precision-losing "from mixed numbers" > protocol > def from_mixed(cls, integer, numerator, denominator): > try: > factory = cls.__from_mixed__ > except AttributeError: > return cls(integer) + cls(numerator) / cls(denominator) > return factory(integer, numerator, denominator) > > # Algorithm for a potentially lossy "from fixed point" protocol > def from_fixed(cls, integer, mantissa, base, exponent): > try: > factory = cls.__from_fixed__ > except AttributeError: > return cls(integer) + cls(mantissa) * cls(base) ** cls(exponent) > return factory(integer, mantissa, base, exponent)
The key problem with a protocol is that the implementer has to make these decisions. The callback approach defers that decision to the end user. After all, the end user is the one who knows for his app whether precision loss is acceptable. You could probably also have a standard named protocol which can be used as a callback in straightforward cases time.time(callback=timedelta.__from_mixed__) That's wordy, and a bit ugly, though. The callback code could special-case types and look for __from_mixed__, I guess. Or use an ABC, and have the code that uses the callback do if issubclass(cb, MixedNumberABC): return cb.__from_mixed__(whole, num, den) else: return cb(whole, num, den) (The second branch is the one that allows the user to override the predefined types that work - if you omit that, you're back to a named protocol and ABCs don't gain you much beyond documentation). Part of me feels that there's a use case for generic functions in here, but maybe not (as it's overloading on the return type). Let's not open that discussion again, though. Paul. _______________________________________________ Python-Dev mailing list Python-Dev@python.org http://mail.python.org/mailman/listinfo/python-dev Unsubscribe: http://mail.python.org/mailman/options/python-dev/archive%40mail-archive.com