On Tue, Apr 05, 2022 at 02:17:00PM +1000, Chris Angelico wrote: > Do you ever have one module that's using statute miles and > another that's using nautical miles, but *not both in the same > module*? The only reason to have them namespaced to modules is to > allow different modules to use them independently. If your application > needs to use both statute and nautical miles in the same module (most > likely the main module), then it's going to have an issue, and your > proposal adds a ton of complexity (that's a real unit, by the way, I > totally didn't make it up) for no benefit whatsoever.
That's not the real problem. The real problem is that my program may: * import ham, which registers mile as 1609.3412 m * import spam, which registers mile as 1609.344 m * import cheese, which registers mile as 1609.3472 m * import aardvark, which registers mile as 1609.3426 m * import hovercraft, which registers mile as 1853.181 m and then do calculations in miles, before converting to metres, and the results I get will be subtly (or not so subtly) different depending on the order I import those modules. (By the way, none of the above are nautical miles; of which there are at least three.) > "If I import * from cheddar first, then camembert, then I have issues". And that is why you shouldn't `import *`. This is an old, well-known issue with wildcard imports. > What's the difference? You're looking at a fundamentally identical > problem, and thinking that it's fundamentally solved by module-level > separation? Show me some evidence. You are correct that this is fundamentally identical to the problem that namespaces are designed to solve. This is why modern languages don't have one single system-wide namespace. We have 30+ years of Python programming, and 40-odd years of programming prior to Python, showing that the solution to the name collusion problem is to have distinct namespaces rather than one single system-wide namespace that everything writes to. That's exactly my point. Of course if I do this: from spam import mile from eggs import mile then I have a namespace collision that results in last value winning. But that's kinda obvious doncha think? :-) Importantly, just doing from spam import mile import eggs will not collide, except under the very unusual case that eggs gets up to no good by writing to the importing module's namespace. (Is that even possible? At import time, can eggs tell which module is importing it?) > Have you ever mutated sys.modules? Not directly, no, except by the approved method of calling `import`, which never over-writes an existing entry, only adds new entries. Nor have I ever mutated the central registry of codecs to *replace* an existing encoder (like UTF-8) with my own. Likewise for error handlers. There's only a relatively small number of each, and the two registries change so rarely that there is next to zero chance that I might accidently trample over an existing codecs or error handler with my own. And I do not expect that arbitrary imports will make changes to those registries. Never have I worried that `import spam` might change the meaning of the 'utf-8' codec, or replace some error handler with one with the same name but different behaviour. But if there were thousands of codecs, and every second module I imported could potentially add or delete those codecs, then I would have to worry about these things. The system would be unworkable and we would have to find a better one. With units, there are thousands of named units, with many name collisions. The system would be unworkable with only a single interpreter-wide registry. > > This is exactly analogous to the situation Python would have if there > > were no per-module globals, just the system-wide builtins, and every > > library stored top-level variables and functions in that namespace. > > *shudders* > > Straw man. It's more like using decimal.getcontext() and making > changes. The situation is analogous, but not identical. The decimal context is not a interpreter-wide registry of long-lasting entities intended to be used by any and all modules. It is a per-thread part of the decimal API. Its not even a very close analogy: aside from sharing the vague concept of "global state" with your units registry, there's nothing like registering a unit ("furlongs per fortnight") in the decimal context. There are only eight settings, and you cannot set arbitary attributes in decimal contexts. The decimal context environment isn't even truly interpreter-wide. It is per thread storage, so every thread has its own independent environment. Other modules (except possibly your application's main module) are not expected to modify the current context, although that's not enforced. (This is Python: you can shoot yourself in the foot if you really want to.) It would be considered *badly-behaved* for other modules or functions to directly modify the current decimal context instead of using localcontext() to temporarily change the current context. P.S. localcontext makes a copy of the context. Just sayin'. > That's global. Do we have per-module Decimal contexts? If you want it, you can have it. See PEP 567. > In fact, the only way to change context is globally - though you can > do so temporarily. That means you do not have any module-level > separation *at all*. I don't hear the Decimal folks screaming about > that. They don't need to, because arbitrary modules don't make changes to the decimal current context. That would be harmful, so well-behaved code uses localcontext(), and badly-behaved code doesn't get used. But with your central, interpreter-wide registry of units, modules which wish to use a named unit have no other choice than to register it with the central registry. If my module aarvark.py wants to use a named unit, the pottle (a historical measurement equal to 1/2 a British gallon), what can I do? I can check the registry to see if pottle is already defined. If it isn't, great, I can install it, and it will now be visible to every module, whether they need it or not. But what if its already there, with a different value? Now I have only two equally bad choices: 1. overwrite the system-wide pottle entry, breaking other modules; 2. or do without. Because the registry is system-wide, I cannot define my own pottle unit without nuking other module's pottle unit. -- Steve _______________________________________________ Python-ideas mailing list -- python-ideas@python.org To unsubscribe send an email to python-ideas-le...@python.org https://mail.python.org/mailman3/lists/python-ideas.python.org/ Message archived at https://mail.python.org/archives/list/python-ideas@python.org/message/YO54JFNX576JT62TVSS4FZTQFTZIVURF/ Code of Conduct: http://python.org/psf/codeofconduct/