On Wed, Aug 31, 2016 at 2:08 PM, Ken Kundert <python-id...@shalmirane.com> wrote: > > What's the mnemonic here? Why "r" for scale factor? > > My thinking was that r stands for real like f stands for float. > With the base 2 scale factors, b stands for binary.
"Real" has historically often been a synonym for "float", and it doesn't really say that it'll be shown in engineering notation. But then, we currently have format codes 'e', 'f', and 'g', and I don't think there's much logic there beyond "exponential", "floating-point", and... "general format"? I think that's a back-formation, frankly, and 'g' was used simply because it comes nicely after 'e' and 'f'. (C's decision, not Python's, fwiw.) I'll stick with 'r' for now, but it could just as easily become 'h' to avoid confusion with %r for repr. >> (2) Support for full prefix names, so we can format (say) "kilograms" as well >> as "kg"? > > This assumes that somehow this code can access the units so that it can switch > between long form 'grams' and short form 'g'. That is a huge expansion in the > complexity for what seems like a small benefit. > AIUI, it's just giving the full word. class ScaledNumber(float): invert = {"μ": 1e6, "m": 1e3, "": 1, "k": 1e-3, "M": 1e-6} words = {"μ": "micro", "m": "milli", "": "", "k": "kilo", "M": "mega"} aliases = {"u": "μ"} def autoscale(self): if self < 1e-6: return None if self < 1e-3: return "μ" if self < 1: return "m" if self < 1e3: return "" if self < 1e6: return "k" if self < 1e9: return "M" return None def __format__(self, fmt): if fmt == "r" or fmt == "R": scale = self.autoscale() fmt = fmt + scale if scale else "f" if fmt.startswith("r"): scale = self.aliases.get(fmt[1], fmt[1]) return "%g%s" % (self * self.invert[scale], scale) if fmt.startswith("R"): scale = self.aliases.get(fmt[1], fmt[1]) return "%g %s" % (self * self.invert[scale], self.words[scale]) return super().__format__(self, fmt) >>> range = ScaledNumber(50e3) >>> print('Attenuation = {:.1f} dB at {:r}m.'.format(-13.7, range)) Attenuation = -13.7 dB at 50km. >>> print('Attenuation = {:.1f} dB at {:R}meters.'.format(-13.7, range)) Attenuation = -13.7 dB at 50 kilometers. >>> print('Attenuation = {:.1f} dB at {:rM}m.'.format(-13.7, range)) Attenuation = -13.7 dB at 0.05Mm. >>> print('Attenuation = {:.1f} dB at {:RM}meters.'.format(-13.7, range)) Attenuation = -13.7 dB at 0.05 megameters. It's a minor flexibility, but could be very useful. As you see, it's still not at all unit-aware; but grammatically, these formats only make sense if followed by an actual unit name. (And not an SI base unit, necessarily - you have to use "gram", not "kilogram", lest you get silly constructs like "microkilogram" for milligram.) Note that this *already works*. You do have to use an explicit class for your scaled numbers, since Python doesn't want you monkey-patching the built-in float type, but if you were to request that float.__format__ grow support for this, it'd be a relatively non-intrusive change. This class could live on PyPI until one day becoming subsumed into core, or just be a permanent third-party float formatting feature. ChrisA _______________________________________________ Python-ideas mailing list Python-ideas@python.org https://mail.python.org/mailman/listinfo/python-ideas Code of Conduct: http://python.org/psf/codeofconduct/