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 -- [email protected]
To unsubscribe send an email to [email protected]
https://mail.python.org/mailman3/lists/python-ideas.python.org/
Message archived at
https://mail.python.org/archives/list/[email protected]/message/6O6I6AFZ2QIKOK3TGSY75OQQDHKBW5AL/
Code of Conduct: http://python.org/psf/codeofconduct/