On Wed, May 5, 2021, 02:11 Steven D'Aprano <[email protected]> wrote:

> My comments follow, interleaved with Matt's.
>
>
> On Mon, May 03, 2021 at 11:30:51PM +0100, Matt del Valle wrote:
>
> > But you've pretty much perfectly identified the benefits here, I'll just
> > elaborate on them a bit.
> >
> > - the indentation visually separates blocks of conceptually-grouped
> > attributes/methods in the actual code (a gain in clarity when code is
> read)
>
> Indeed, that is something I often miss: a way to conceptually group
> named functions, classes and variables which is lighter weight than
> separating them into a new file.
>
> But you don't need a new keyword for that. A new keyword would be nice,
> but grouping alone may not be sufficient to justify a keyword.
>
>
> > - the dot notation you use to invoke such methods improves the experience
> > for library consumers by giving a small amount conceptually-linked
> > autocompletions at each namespace step within a class with a large API,
> > rather getting a huge flat list.
>
> We don't need a new keyword for people to separate names with dots.
>
> Although I agree with your position regarding nested APIs, *to a point*,
> I should mention that, for what it is worth, it goes against the Zen:
>
> Flat is better than nested.
>
>
> [...]
> > - you can put functions inside a namespace block, which would become
> > methods if you had put them in a class block
>
> This is a feature which I have *really* missed.
>
>
> > - you don't have the same (in some cases extremely unintuitive)
> > scoping/variable binding rules that you do within a class block (see the
> > link in my doc). It's all just module scope.
>
> On the other hand, I don't think I like this. What I would expect is
> that namespaces ought to be a separate scope.
>
> To give an example:
>
>     def spam():
>         return "spam spam spam!"
>
>     def eggs():
>         return spam()
>
>     namespace Shop:
>         def spam():
>             return "There's not much call for spam here."
>         def eggs():
>             return spam()
>
>     print(eggs())
>     # should print "spam spam spam!"
>     print(Shop.eggs())
>     # should print "There's not much call for spam here."
>
>
> If we have a namespace concept, it should actually be a namespace, not
> an weird compiler directive to bind names in the surrounding global
> scope.
>

I agree with this. Great proposal though.


>
> > - it mode clearly indicates intent (you don't want a whole new class,
> just
> > a new namespace)
>
> Indeed.
>
>
> > When using the namespaces within a method (using self):
> >
> > - It allows you to namespace out your instance attributes without needing
> > to create intermediate objects (an improvement to the memory footprint,
> and
> > less do-nothing classes to clutter up your codebase)
> >
> > - While the above point of space complexity will not alway be relevant I
> > think the more salient point is that creating intermediate objects for
> > namespacing is often cognitively more effort than it's worth. And humans
> > are lazy creatures by nature. So I feel like having an easy and intuitive
> > way of doing it would have a positive effect on people's usage patterns.
> > It's one of those things where you likely wouldn't appreciate the
> benefits
> > until you'd actually gotten to play around with it a bit in the wild.
>
>
> I'm not entirely sure what this means.
>
>
> > For example, you could rewrite this:
> >
> > class Legs:
> >   def __init__(self, left, right):
> >       self.left, self.right = left, right
> >
> >
> > class Biped:
> >     def __init__(self):
> >         self.legs = Legs(left=LeftLeg(), right=RightLeg())
> >
> >
> > As this:
> >
> > class Biped:
> >     def __init__(self):
> >         namespace self.legs:
> >             left, right = LeftLeg(), RightLeg()
>
>
> Oh, I hope that's not what you consider a good use-case! For starters,
> the "before" with two classes seems to be a total misuse of classes.
> `Legs` is a do-nothing class, and `self.legs` seems to be adding an
> unnecessary level of indirection that has no functional or conceptual
> benefit.
>
> I hope that the purpose of "namespace" is not to encourage people to
> write bad code like the above more easily.
>
>
> > And sure, the benefit for a single instance of this is small. But across
> a
> > large codebase it adds up. It completely takes away the tradeoff between
> > having neatly namespaced code where it makes sense to do so and writing a
> > lot of needless intermediate classes.
> >
> > SimpleNamespace does not help you here as much as you would think because
> > it cannot be understood by static code analysis tools when invoked like
> > this:
> >
> > class Biped:
> >     def __init__(self):
> >         self.legs = SimpleNamespace(left=LeftLeg(), right=RightLeg())
>
> Surely that's just a limitation of the *specific* tools. There is no
> reason why they couldn't be upgraded to understand SimpleNamespace.
>
>
> [...]
> > > 2. If __dict__ contains "B.C" and "B", then presumably the interpreter
> > > would need to try combinations against the outer __dict__ as well as
> B. Is
> > > the namespace proxy you've mentioned intended to prevent further
> lookup in
> > > the "B" attribute?
> > >
> >
> > The namespace proxy must know its fully-qualified name all the way up to
> > its parent scope (this is the bit that would require some magic in the
> > python implementation), so it only needs to forward on a single attribute
> > lookup to its parent scope. It does not need to perform several
> > intermediate lookups on all of its parent namespaces.
> >
> > So in the case of:
> >
> > namespace A:
> >     namespace B:
> >         C = True
> >
> >
> > >>>A.B
> > <namespace object <A.B> of <module '__main__' (built-in)>>
> >
> >
> > Note that namespace B 'knows' that its name is 'A.B', not just 'B'
>
> If I have understood you, that means that things will break when you do:
>
>     Z = A
>     del A
>     Z.B.C  # NameError name 'A.B' is not defined
>
> Objects should not rely on their parents keeping the name they were
> originally defined under.
>
>
>
> [...]
> > Traversing all the way through A.B.C does involve 2 intermediate lookups
> > (looking up 'A.B' on the parent scope from namespace A, then looking up
> > 'A.B.C' on the parent scope from namespace A.B). But once you have a
> > reference to a deeply nested namespace, looking up any value on it is
> only
> > a single lookup step.
>
> That's no different from the situation today:
>
>     obj = spam.eggs.cheese.aardvark.hovercraft
>     obj.eels  # only one lookup needed
>
>
> > > 3. Can namespaces be nested? If so, will their attributed they always
> > > resolve to flat set of attributes in the encapsulating class?
> > >
> >
> > Yes, namespaces can be nested arbitrarily, and they will always set their
> > attributes in the nearest real scope (module/class/locals). There's an
> > example of this early on in the doc:
> >
> > namespace constants:
> >     NAMESPACED_CONSTANT = True
> >
> >     namespace inner:
> >         ANOTHER_CONSTANT = "hi"
> >
> > Which is like:
> >
> > vars(sys.modules[__name__])["constants.NAMESPACED_CONSTANT"] =
> > Truevars(sys.modules[__name__])["constants.inner.ANOTHER_CONSTANT"] =
> > "hi"
>
> Can I just say that referencing `vars(sys.modules[__name__])` *really*
> works against the clarity of your examples?
>
> Are there situations where that couldn't be written as
>
>     globals()["constants.NAMESPACED_CONSTANT"]
>
> instead?
>
> And remind me, what's `Truevars`?
>
>
>
> --
> Steve
> _______________________________________________
> 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/JF6OWQJ7JRH4CGUWU3APCDMSAB37MR67/
> Code of Conduct: http://python.org/psf/codeofconduct/
>
_______________________________________________
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/FUKJQCPMSBWFQ6YHZCKLOXJOD2BV2YKA/
Code of Conduct: http://python.org/psf/codeofconduct/

Reply via email to