Namespace creation with syntax short form
Namespaces are widely used in Python, but it appear that creating a namespace has no short form, like [] for list or {} for dict, but requires the lengthy types.SimpleNamespace, with prior import of types. So an idea is to make a short form with: ns = (answer= 42) Which resembles the types.SimpleNamespace(answer= 42), but leaves out a lot of typing. Syntax for an empty namespace should then be determined. Best regards, MortenZdk
On Fri, Feb 13, 2015 at 11:19:58AM +0100, Morten Z wrote:
Namespaces are widely used in Python, but it appear that creating a namespace has no short form, like [] for list or {} for dict, but requires the lengthy types.SimpleNamespace, with prior import of types.
from types import SimpleNamespace as NS ns = NS(answer=42) Python did without a SimpleNamespace type for 20+ years. I don't think it is important enough to deserve dedicated syntax, especially yet another overloading of parentheses. Not everything needs to be syntax. -- Steve
Steven D'Aprano wrote:
On Fri, Feb 13, 2015 at 11:19:58AM +0100, Morten Z wrote:
Namespaces are widely used in Python, but it appear that creating a namespace has no short form, like [] for list or {} for dict, but requires the lengthy types.SimpleNamespace, with prior import of types.
from types import SimpleNamespace as NS ns = NS(answer=42)
Python did without a SimpleNamespace type for 20+ years. I don't think it is important enough to deserve dedicated syntax, especially yet another overloading of parentheses. Not everything needs to be syntax.
If there were syntactic support for on-the-fly namespace "packing" and "unpacking" that could have a significant impact on coding style, similar to that of named arguments. Functions often start out returning a tuple and are later modified to return a NamedTuple. For backward compatibility reasons you are either stuck with the original data or you need a workaround like os.stat_result which has more attributes than items. "Named (un)packing" could avoid that.
def f(): ... return a:1, b:2, c:3 f() (a:1, b:2, c:3) :c, :b, :a = f() # order doesn't matter :a, :c = f() # we don't care about b a # by default bound name matches attribute name 1 x:a, y:c = f() # we want a different name x, y (1, 3)
On Feb 13, 2015, at 4:05, Peter Otten <__peter__@web.de> wrote:
Steven D'Aprano wrote:
On Fri, Feb 13, 2015 at 11:19:58AM +0100, Morten Z wrote:
Namespaces are widely used in Python, but it appear that creating a namespace has no short form, like [] for list or {} for dict, but requires the lengthy types.SimpleNamespace, with prior import of types.
from types import SimpleNamespace as NS ns = NS(answer=42)
Python did without a SimpleNamespace type for 20+ years. I don't think it is important enough to deserve dedicated syntax, especially yet another overloading of parentheses. Not everything needs to be syntax.
If there were syntactic support for on-the-fly namespace "packing" and "unpacking" that could have a significant impact on coding style, similar to that of named arguments.
Functions often start out returning a tuple and are later modified to return a NamedTuple. For backward compatibility reasons you are either stuck with the original data or you need a workaround like os.stat_result which has more attributes than items.
There's really nothing terrible about that workaround, except that it's significantly harder to implement in Python (with namedtuple) than in C (with structseq). More importantly, I don't see how named unpacking helps that. If your function originally returns a 3-tuple, and people write code that expects a 3-tuple, how do you change it to return a 3-tuple that's also a 4-namespace under your proposal? Meanwhile:
"Named (un)packing" could avoid that.
def f(): ... return a:1, b:2, c:3 f() (a:1, b:2, c:3)
So what type is that? Is there a single type for all "named packing tuples" (like SimpleNamespace), or a separate type for each one (like namedtuple), or some kind of magic that makes issubclass(a, b) true if b has the same names as a plus optionally more names at the end, or ...? How does this interact with existing unpacking? Can I extract the values as a tuple, e.g., "x, *y = f()" to get x=1 and y=(2, 3)? Or the key-value pairs with "**kw = f()"? Or maybe ::kw to get them as a namespace instead of a dict? Can I mix them, with "x, *y, z:c" to get 1, (2,), and 3 (and likewise with **kw or ::ns)? Does this only work with magic namespaces, or with anything with attributes? For example, if I have a Point class, "does x:x = pt" do the same thing as "x = pt.x", or does that only work if I don't use a Point class and instead pass around (x:3, y:4) with no type information? Since the parens are clearly optional, does that mean that "a:3" is a single-named namespace object, or do you need the comma as with tuples? (I think the former may make parsing ambiguous, but I haven't thought it through...)
:c, :b, :a = f() # order doesn't matter :a, :c = f() # we don't care about b a # by default bound name matches attribute name
Why do you need, or want, that last comment? With similar features, you have to be explicit; e.g., if you want to pass a local variable named a to a keyword parameter named a, you have to write a=a, not just =a. How is this case different?
1
x:a, y:c = f() # we want a different name x, y (1, 3)
Andrew Barnert wrote:
On Feb 13, 2015, at 4:05, Peter Otten <__peter__@web.de> wrote:
Steven D'Aprano wrote:
On Fri, Feb 13, 2015 at 11:19:58AM +0100, Morten Z wrote:
Namespaces are widely used in Python, but it appear that creating a namespace has no short form, like [] for list or {} for dict, but requires the lengthy types.SimpleNamespace, with prior import of types.
from types import SimpleNamespace as NS ns = NS(answer=42)
Python did without a SimpleNamespace type for 20+ years. I don't think it is important enough to deserve dedicated syntax, especially yet another overloading of parentheses. Not everything needs to be syntax.
If there were syntactic support for on-the-fly namespace "packing" and "unpacking" that could have a significant impact on coding style, similar to that of named arguments.
Functions often start out returning a tuple and are later modified to return a NamedTuple. For backward compatibility reasons you are either stuck with the original data or you need a workaround like os.stat_result which has more attributes than items.
There's really nothing terrible about that workaround, except that it's significantly harder to implement in Python (with namedtuple) than in C (with structseq).
More importantly, I don't see how named unpacking helps that. If your function originally returns a 3-tuple, and people write code that expects a 3-tuple, how do you change it to return a 3-tuple that's also a 4-namespace under your proposal?
Meanwhile:
"Named (un)packing" could avoid that.
def f(): ... return a:1, b:2, c:3 f() (a:1, b:2, c:3)
So what type is that? Is there a single type for all "named packing tuples" (like SimpleNamespace), or a separate type for each one (like namedtuple), or some kind of magic that makes issubclass(a, b) true if b has the same names as a plus optionally more names at the end, or ...?
I'm an adept of duck typing, so a:1, b:2, c:3 has to create an object with three attributes "a", "b", and "c" and :a, :b, :c = x works on any x with three attributes "a", "b", and "c". Implementationwise
some kind of magic that makes issubclass(a, b) true if b has the same names as a plus optionally more names at the end, or ...?
would be OK, but as there is no order of the names there is also no "end". In general this should start as restrictive as possible, e. g. issubclass(type(a), type(b)) == a is b wouldn't bother me as far as usability is concerned.
How does this interact with existing unpacking? Can I extract the values as a tuple, e.g., "x, *y = f()" to get x=1 and y=(2, 3)? Or the key-value pairs with "**kw = f()"? Or maybe ::kw to get them as a namespace instead of a dict? Can I mix them, with "x, *y, z:c" to get 1, (2,), and 3 (and likewise with **kw or ::ns)?
I'd rather not mix positional and and named (un)packing.
Does this only work with magic namespaces, or with anything with attributes? For example, if I have a Point class, "does x:x = pt" do the same thing as "x = pt.x", or does that only work if I don't use a Point class and instead pass around (x:3, y:4) with no type information?
Since the parens are clearly optional, does that mean that "a:3" is a single-named namespace object, or do you need the comma as with tuples? (I think the former may make parsing ambiguous, but I haven't thought it through...)
No comma, please, if at all possible.
:c, :b, :a = f() # order doesn't matter :a, :c = f() # we don't care about b a # by default bound name matches attribute name
Why do you need, or want, that last comment? With similar features, you have to be explicit; e.g., if you want to pass a local variable named a to a keyword parameter named a, you have to write a=a, not just =a. How is this case different?
I expect the bound name to match that of the attribute in ninety-nine percent or so. Other than that -- no difference.
1
x:a, y:c = f() # we want a different name x, y (1, 3)
On Fri, Feb 13, 2015 at 02:20:48PM +0100, Peter Otten wrote:
a:1, b:2, c:3
has to create an object with three attributes "a", "b", and "c" and
This is ambiguous. Is it one object with three attributes, or a tuple of three namespace objects, each with one attribute?
:a, :b, :c = x
works on any x with three attributes "a", "b", and "c". Implementationwise
I don't like the way that the colon jumps around from after the name to before the name. I don't like the way attributes are sometimes written with a dot and sometimes a colon: x = a:1, b:2 process(x.a) process(x:b) # oops. I think it is awfully mysterious syntax and beginners are going to have a terrible time working out when they should use dot and when they should use colon. -- Steven
Steven D'Aprano wrote:
On Fri, Feb 13, 2015 at 02:20:48PM +0100, Peter Otten wrote:
a:1, b:2, c:3
has to create an object with three attributes "a", "b", and "c" and
This is ambiguous. Is it one object with three attributes, or a tuple of three namespace objects, each with one attribute?
:a, :b, :c = x
works on any x with three attributes "a", "b", and "c". Implementationwise
I don't like the way that the colon jumps around from after the name to before the name. I don't like the way attributes are sometimes written with a dot and sometimes a colon:
x = a:1, b:2 process(x.a) process(x:b) # oops.
I think it is awfully mysterious syntax and beginners are going to have a terrible time working out when they should use dot and when they should use colon.
I regret that I let Andrew lure me into a discussion of those details so soon. The point of my original answer was that if an acceptable syntax can be found real code can profit from it. Personally, I would be OK with limiting this construct to return statements and the lhs of an assignment, but I doubt that this will gather the necessary support.
On Feb 13, 2015, at 5:20, Peter Otten <__peter__@web.de> wrote:
Andrew Barnert wrote:
On Feb 13, 2015, at 4:05, Peter Otten <__peter__@web.de> wrote:
Steven D'Aprano wrote:
On Fri, Feb 13, 2015 at 11:19:58AM +0100, Morten Z wrote:
Namespaces are widely used in Python, but it appear that creating a namespace has no short form, like [] for list or {} for dict, but requires the lengthy types.SimpleNamespace, with prior import of types.
from types import SimpleNamespace as NS ns = NS(answer=42)
Python did without a SimpleNamespace type for 20+ years. I don't think it is important enough to deserve dedicated syntax, especially yet another overloading of parentheses. Not everything needs to be syntax.
If there were syntactic support for on-the-fly namespace "packing" and "unpacking" that could have a significant impact on coding style, similar to that of named arguments.
Functions often start out returning a tuple and are later modified to return a NamedTuple. For backward compatibility reasons you are either stuck with the original data or you need a workaround like os.stat_result which has more attributes than items.
There's really nothing terrible about that workaround, except that it's significantly harder to implement in Python (with namedtuple) than in C (with structseq).
More importantly, I don't see how named unpacking helps that. If your function originally returns a 3-tuple, and people write code that expects a 3-tuple, how do you change it to return a 3-tuple that's also a 4-namespace under your proposal?
Meanwhile:
"Named (un)packing" could avoid that.
def f(): ... return a:1, b:2, c:3 f() (a:1, b:2, c:3)
So what type is that? Is there a single type for all "named packing tuples" (like SimpleNamespace), or a separate type for each one (like namedtuple), or some kind of magic that makes issubclass(a, b) true if b has the same names as a plus optionally more names at the end, or ...?
I'm an adept of duck typing, so
a:1, b:2, c:3
has to create an object with three attributes "a", "b", and "c" and
:a, :b, :c = x
works on any x with three attributes "a", "b", and "c".
That doesn't answer either of my questions. Again: How does this help when you started with a 3-tuple and realize you need a 4th value, ala stat? Since this is your motivating example, if the proposal doesn't help that example, what's the point? What type is this? Just saying "I'm an adept of duck typing" doesn't answer the question. Python objects have types, and this is important.
Implementationwise
some kind of magic that makes issubclass(a, b) true if b has the same names as a plus optionally more names at the end, or ...?
would be OK, but as there is no order of the names there is also no "end".
OK, that makes this seem even _less_ useful for the stat case. The reasons to use namedtuple/structseq in the first place for stat are (a) so existing code written to the original tuple interface can continue to treat the value as a tuple while newer code can access the new attributes that don't fit; and/or (b) because the order has some intrinsic/historical/whatever meaning so it sometimes makes sense to extract it by position. You're proposing something as an improvement for that case, but it fails both ways.
In general this should start as restrictive as possible, e. g. issubclass(type(a), type(b)) == a is b wouldn't bother me as far as usability is concerned.
OK, so a new type for each namespace object?
How does this interact with existing unpacking? Can I extract the values as a tuple, e.g., "x, *y = f()" to get x=1 and y=(2, 3)? Or the key-value pairs with "**kw = f()"? Or maybe ::kw to get them as a namespace instead of a dict? Can I mix them, with "x, *y, z:c" to get 1, (2,), and 3 (and likewise with **kw or ::ns)?
I'd rather not mix positional and and named (un)packing.
Does this only work with magic namespaces, or with anything with attributes? For example, if I have a Point class, "does x:x = pt" do the same thing as "x = pt.x", or does that only work if I don't use a Point class and instead pass around (x:3, y:4) with no type information?
This is a really important question, and you've skipped it. I think you're tying unpacking and packing together too closely in your head--like people who expect that the **kw object you pass as an argument to a function call must end up as the **kw parameter value, or that parameters with default values have anything to do with keyword arguments. In your case, assignment targets and expressions are partially parallel syntax and similar semantics, but the distinction is important. For example, "a, b = 2, 3" does not create a tuple "(a, b)" to assign to. And you can of course do "a[0], b.c = x" and expect that to work. So, you have to consider how your unpacking fits into that. Look at the grammar for assignment targets, and work out where the colons will go. And then, what are the semantics? If you don't know what they should be, that's why it's important to be able to answer questions like "can I use namespace unpacking for any object with the right attributes, or only for magic namespace objects?" If the answer is the former, the semantics can be dead simple: namespace unpacking uses getattr. If not, you need to invent some new mechanism, and it needs to make sense. For that matter, that question is important in its own right. With the former answer, namespace unpacking is perfectly useful on its own--e.g., to extract a couple of attributes out of a stat result. So you can argue for two related but separate proposals on their own merits, giving you a wider range of use cases both to argue for each proposal itself better and to think through the details. And likewise, the thing in the return statement is a kind of expression--in particular, a display expression for some type. That means it has a type and a value that you can ask questions about. I know you said "I regret that I let Andrew lure me into a discussion of those details so soon. The point of my original answer was that if an acceptable syntax can be found real code can profit from it." But your entire proposal is about syntactic sugar, and if you don't actually have an acceptable syntax and aren't willing to answer the questions to work toward one, you don't have a proposal.
Since the parens are clearly optional, does that mean that "a:3" is a single-named namespace object, or do you need the comma as with tuples? (I think the former may make parsing ambiguous, but I haven't thought it through...)
No comma, please, if at all possible.
As pointed out, that immediately makes a:1, b:2 ambiguous as a single 2-namespace or a 2-tuple of 1-namespaces. How do you resolve that? And again, I'm pretty sure that's not the only place this would be ambiguous. (It may be worth looking at how YAML deals with its pretty similar ambiguities, even though it obviously has a different grammar from Python expressions.)
:c, :b, :a = f() # order doesn't matter :a, :c = f() # we don't care about b a # by default bound name matches attribute name
Why do you need, or want, that last comment? With similar features, you have to be explicit; e.g., if you want to pass a local variable named a to a keyword parameter named a, you have to write a=a, not just =a. How is this case different?
I expect the bound name to match that of the attribute in ninety-nine percent or so. Other than that -- no difference.
First, I don't that expectation is true. Look at iterable unpacking. It's certainly _common_ that you return x, y from a function and the result gets assigned to locals named x and y, but it's nowhere near 99%. This is where having real use cases--and being able to answer questions about, e.g., whether you can unpack any object so--would be handy. I'm pretty sure that if I wanted to get the times out of a stat(infile), I'd want the locals to be named something like inatime, inmtime, inctime, not st_atime, st_mtime, and st_ctime. (Sorry for harping on stat, but since it's the one real-life use case you've come up with that isn't a toy example, it's the most obvious thing to look at. If you think there's something special about stat that makes it not relevant in general, please explain what it is and give a better use case.)
1
x:a, y:c = f() # we want a different name x, y (1, 3)
_______________________________________________ Python-ideas mailing list Python-ideas@python.org https://mail.python.org/mailman/listinfo/python-ideas Code of Conduct: http://python.org/psf/codeofconduct/
On Fri, Feb 13, 2015, at 17:16, Andrew Barnert wrote:
OK, that makes this seem even _less_ useful for the stat case.
The reasons to use namedtuple/structseq in the first place for stat are (a) so existing code written to the original tuple interface can continue to treat the value as a tuple while newer code can access the new attributes that don't fit;
What I'm getting is: the reason to want a simple namespace syntax is to get people to stop returning tuples for stuff like this in the first place, so that there is no original tuple interface. The horse has already bolted for the stat case.
On Feb 13, 2015, at 15:51, random832@fastmail.us wrote:
On Fri, Feb 13, 2015, at 17:16, Andrew Barnert wrote:
OK, that makes this seem even _less_ useful for the stat case.
The reasons to use namedtuple/structseq in the first place for stat are (a) so existing code written to the original tuple interface can continue to treat the value as a tuple while newer code can access the new attributes that don't fit;
What I'm getting is: the reason to want a simple namespace syntax is to get people to stop returning tuples for stuff like this in the first place, so that there is no original tuple interface. The horse has already bolted for the stat case.
But (as Greg Ewing said better than me), I don't think there really is a common use case for this at all. Have you ever, say, designed a 2D graphics package and decided to pass around 2-tuples instead of point objects just because it was too hard to create a Point class? And yes, I realize that particular example sucks. But my point is that every example I can think of sucks, and neither the OP nor anyone else has thought of one that doesn't. (And stat, in particular, is not a good example. It was added to a much simpler and weaker language than today's Python, and it was coded in C, not Python, so the reasons for its design aren't likely to be relevant here in the first place.)
Andrew Barnert wrote:
How does this help when you started with a 3-tuple and realize you need a 4th value, ala stat? Since this is your motivating example, if the proposal doesn't help that example, what's the point?
It doesn't help with existing return values, it offers an alternative, similar to keyword-only function arguments.
Morten Z wrote:
Namespaces are widely used in Python, but it appear that creating a namespace has no short form,
Most of the time people create dedicated classes for their namespaces, though. It's very rare to see the use of an ad-hoc namespace such as SimpleNamespace provides. It seems to me like a feature that novice programmers think they want, until they learn that there are better ways of going about it. -- Greg
participants (6)
-
Andrew Barnert
-
Greg Ewing
-
Morten Z
-
Peter Otten
-
random832@fastmail.us
-
Steven D'Aprano