On Fri, Jul 26, 2019 at 01:37:29PM -0700, Andrew Barnert wrote: > It’s not clear to me whether people want a module-like, class-like, or > function-like namespace here, but I do think it’s probably exactly one > of those three. And it’ll be a lot easier to define and implement—and > for everyone to understand future Python code—if it is.
I'm not going to speak for "people", but for me, I think that the answer should be obvious: it is module-like. I'm not even sure what you mean by "function-like" or "class-like" namespaces. Functions aren't namespaces (except in the sense that they are objects with a __dict__ and so support attributes): you can't access a function locals from the outside. Classes are namespaces, but if you want a namespace that behaves like a class, use a class. For me, the idea is to (optionally) decouple module objects from .py files. Of course most of the time when you want a module object, it is most useful to split the code out into a seperate file, and that won't change. But there are times when you have a relatively small amount of code that cries to be split off into a seperate namespace, but shifting it into a seperate physical file is inconvenient. Going from one physical file to two physical files is a significant jump in project complexity that's not always worth the cost. So a namespace object that behaves like a module but doesn't need to split off into a seperate .py file would be helpful. The code in a namespace object ought to behave precisely the same as if it were copied into a seperate file. (*Almost* -- see below.) That is, given some module file containing code: # module.py code I should be able to copy the content of the file ("code"), and paste it into another file like this: # another.py with Namespace("module") as module: # paste and indent here code and the resulting namespace object bound to the name "module" should be (almost) identical to what you would have got from calling "import module" in the first place. A few possible differences: 1. It isn't clear what the namespace __file__ and __package__ attributes ought to contain, or if it should have them at all. 2. For introspection purposes, and to allow for possible future changes, it might be wise to use a subclass of ModuleType for namespace objects. 3. The repr will probably be different. One more difference needs a bit of explanation: The standard scoping rule used by Python is LEGB, which goes: - Local - Enclosing functions (non-local/closures) - Global (module) - Builtins with a slight variation on that for code inside classes. So when you run code in module.py, the global namespace it sees is module.py i.e. itself. But consider what happens when you copy the code from module.py and paste it into another file, into a namespace. It would be surprising if the code inside the namespace with block couldn't see module level globals. So we need a slight variation on the scoping rule: - Local - Enclosing functions (non-local/closures) - Namespace - Global (module) - Builtins (and an analogous change for when classes are involved). To make it more clear with a concrete example: a = 1 b = 2 c = 3 with Namespace("ns") as ns: a = 100 b = 200 def function(): a = 999 return a, b, c ns.function() ought to return (999, 200, 3). This implies a small difference in behaviour for code in a namespace versus a seperate physical file. If I copied that code block out of ns above, and pasted it into a physical file, then running function() would raise: NameError: name 'c' is not defined I think that this slight difference will be considered desirable, unsurprising (it would be surprising if namespaces didn't see their surrounding global scope, and even more surprising if external modules could pry into another module!) and hopefully uncontroversial. > I don’t think anyone cares about fast locals here, and I hope nobody > cares about things like super magic. A function shouldn't become slower just because you move it into a namespace. And classes ought to behave the same, including super, whether they were defined inside or outside a namespace. > But the differences in how > variables in the namespace interact with functions and classes (and > new-kind-of-namespace-things) defined within the namespace do probably > matter. And you’d probably want to know what to expect from eval/exec, > locals/globals/vars, etc., Absolutely! There are a couple of tricky corner cases involving eval (and exec?) already, related to comprehensions. They will need to be ironed out. I've spent some time (unsuccessfully) trying to get this to work using a class statement: @namespace class ns: a = 100 def function(): print(a) but I couldn't get the function inside the class to see the surrounding "a". If Batuhan Taskaya has solved that problem using a with statement, it sounds like there's no technical barriers to starting with a third party library, iron out the corner cases, and then consider moving it into the std lib. Or even a keyword: namespace ns: code which would eliminate the need to repeat ourselves when naming the namespace. > Meanwhile, do we even need to name the namespace? How do you refer to it without binding it to a name? Does it need an internal ns.__name__ attribute? I think that would be useful for the repr, introspection etc. It is sad that we have to repeat ourselves: with namespace("ns") as ns but that's a limitation of the language. We could solve it with a keyword, but let's get a working, usable prototype first before worrying about justifying a keyword. -- Steven _______________________________________________ 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/6O6I6AFZ2QIKOK3TGSY75OQQDHKBW5AL/ Code of Conduct: http://python.org/psf/codeofconduct/