Groups | Search | Server Info | Keyboard shortcuts | Login | Register [http] [https] [nntp] [nntps]
Groups > comp.lang.python > #110217 > unrolled thread
| Started by | Ari Freund <arief@google.com> |
|---|---|
| First post | 2016-06-21 10:34 +0300 |
| Last post | 2016-06-26 18:50 +0300 |
| Articles | 5 — 5 participants |
Back to article view | Back to comp.lang.python
This discussion starts older than the indexed window; earlier articles aren't shown. The article labeled Started by
below is the oldest one visible, not the original post.
Proposal: named return values through dict initialization and unpacking Ari Freund <arief@google.com> - 2016-06-21 10:34 +0300
Re: Proposal: named return values through dict initialization and unpacking Steven D'Aprano <steve@pearwood.info> - 2016-06-22 11:35 +1000
Re: Proposal: named return values through dict initialization and unpacking Michael Selik <michael.selik@gmail.com> - 2016-06-26 18:17 +0000
Re: Proposal: named return values through dict initialization and unpacking arief@google.com - 2016-06-26 08:28 -0700
Re: Proposal: named return values through dict initialization and unpacking Joonas Liik <liik.joonas@gmail.com> - 2016-06-26 18:50 +0300
| From | Ari Freund <arief@google.com> |
|---|---|
| Date | 2016-06-21 10:34 +0300 |
| Subject | Proposal: named return values through dict initialization and unpacking |
| Message-ID | <mailman.0.1466494641.11516.python-list@python.org> |
I'd like to run this idea by the community to see if it's PEP worthy and
hasn't been already rejected.
Background
Just as keyword arguments enhance code readability and diminish the risk of
bugs, so too would named
return values. Currently, we can write
val1, val2, val3 = myfunc()
but we must take care to order the variables correctly, i.e., in the same
order as the corresponding
values in myfunc's return statement. Getting it wrong may result in
difficult-to-detect bugs. This
problem would be alleviated if we could name the return values and
disregard their order.
My proposal consists of two parts.
1. Add a new form of assignment by dictionary unpacking.
Suppose d is a dict whose keys are the strings 'var1', 'var2',
'var3'. Then the assignment
var1, var2, var3 = **d
would set the variables to the corresponding values. Order would not
be important, so
var3, var1, var2 = **d
would have the same effect.
Also, not all keys would need to participate; for example,
var2 = **d
would work just fine.
It would be an error for the same name to appear more than once on
the LHS of the assignment.
It would be an error for a name on the LHS to not be a valid variable
name or for there not to
be a corresponding key in the dict.
It would (possibly) be an error if the dict contained keys that are
not strings (or are strings
that cannot serve as variable names).
2. Extend the dict() form of dictionary initialization to allow an element
to be a variable name, e.g.,
dict(var1, var2, var3, var4=33)
would be equivalent to
dict(var1=var1, var2=var2, var3=var3, var4=33)
I believe each of the above is useful in its own right. Additionally,
their combination yields reasonably
streamlined named return value functionality, as in
def RunShellCmd(cmd):
exit_code, output = commands.getstatusoutput(cmd) # Here we need
to get the order right.
return dict(exit_code, output)
output, exit_code = **RunShellCmd("ls .") # Here we don't care about
order.
[toc] | [next] | [standalone]
| From | Steven D'Aprano <steve@pearwood.info> |
|---|---|
| Date | 2016-06-22 11:35 +1000 |
| Message-ID | <5769eb4a$0$1610$c3e8da3$5496439d@news.astraweb.com> |
| In reply to | #110217 |
On Tue, 21 Jun 2016 05:34 pm, Ari Freund wrote:
> I'd like to run this idea by the community to see if it's PEP worthy and
> hasn't been already rejected.
>
> Background
> Just as keyword arguments enhance code readability and diminish the risk
> of bugs, so too would named
> return values.
I don't think that follows. Keyword arguments work well when a function has
many optional arguments, say, ten or so, but it's rare for functions to
have optional return values, and typically they only return a few values:
usually one, sometimes two, rarely three.
If your function has more than three return values, you really need to
rethink the function. Chances are good that it is either doing too much, or
you should be encapsulating some of the detail of what it returns into
objects:
# Ugly:
fe, fi, fo, fum, spam, eggs, cheese, tomato, foo, bar, baz = func(arg)
# Probably better:
obj = func(arg)
assert hasattr(obj, 'fe')
assert hasattr(obj, 'fi') # etc.
Of course, returning a dict of {key:value} pairs is a kind of encapsulation.
> Currently, we can write
> val1, val2, val3 = myfunc()
> but we must take care to order the variables correctly, i.e., in the same
> order as the corresponding
> values in myfunc's return statement. Getting it wrong may result in
> difficult-to-detect bugs.
Most languages deal with that with type checking, which can eliminate many
(but not all) of such bugs. Python 3 will encourage optional type checking
too. Alternatively, this is one of the issues that TDD and unit-testing can
solve. And in practice, help(func) should never be more than a few key
presses away, so I just don't see this as being a sufficiently large
problem in practice to deserve new syntax.
Yes, I found it annoying for the longest time that I couldn't remember which
order enumerate returned its values: index, item or item, index. But I had
many solutions to turn to, and eventually I memorised it.
> This
> problem would be alleviated if we could name the return values and
> disregard their order.
>
> My proposal consists of two parts.
>
> 1. Add a new form of assignment by dictionary unpacking.
> Suppose d is a dict whose keys are the strings 'var1', 'var2',
> 'var3'. Then the assignment
> var1, var2, var3 = **d
> would set the variables to the corresponding values. Order would
> not
> be important, so
> var3, var1, var2 = **d
> would have the same effect.
But I don't want to use the horrible key names your function uses. I want to
use names which makes sense for my application:
width, counter, aardvark = **d
but d is {'var1': x, 'var2': y, 'var3': z}. What am I to do?
Seriously, this is a real problem for your proposal. It's one thing to be
forced to use a function's ugly parameter names:
result = somefunction(var1=width, var2=counter, var3=aardvark)
It's another to be forced to use variables that match something the function
sets. What if we're already using a variable with that name? What if we
want to use a different name?
Realistically, we would hope that functions will choose informative names,
but informative names tend to be long. Or if they are short, they might be
excessively generic. Your function might return:
number_of_pages, number_of_sections, number_of_chapters
but I might prefer to use:
pagecount, sectioncount, chaptercount
or even:
npage, nsect, nchapt
It is unacceptable if I have to do this:
number_of_pages, number_of_sections, number_of_chapters = **func(args)
npage, nsect, nchapt = (number_of_pages, number_of_sections,
number_of_chapters)
Whatever names your function uses, whether they are overly abstract, generic
names like a, b, c or var1, var2, var3, or painfully detailed long names
like number_of_pages_containing_requested_section_titles, the caller is
generally going to want to use *something else* for the assignment target.
By the way, we can already get pretty close to this today:
d = func(args)
npage, nsect, nchapt = (d[key] for key in
'number_of_pages number_of_sections number_of_chapters'.split())
> Also, not all keys would need to participate; for example,
> var2 = **d
> would work just fine.
> It would be an error for the same name to appear more than once on
> the LHS of the assignment.
Is that necessary? I can currently do this:
spam, eggs, spam, spam, cheese = func(args)
> It would be an error for a name on the LHS to not be a valid
> variable
> name or for there not to
> be a corresponding key in the dict.
So I can't assign directly to legal assignment targets?
# forbidden
mylist[2], obj.foo, mydict['key'] = **func(args)
This is looking less and less useful...
> It would (possibly) be an error if the dict contained keys that are
> not strings (or are strings
> that cannot serve as variable names).
>
> 2. Extend the dict() form of dictionary initialization to allow an
> element to be a variable name, e.g.,
> dict(var1, var2, var3, var4=33)
> would be equivalent to
> dict(var1=var1, var2=var2, var3=var3, var4=33)
How is dict supposed to know what var1 etc. are? The dict constructor has no
more magical insight into the caller's locals as any other function.
Also, this is legal syntax now:
d = {'a': 1}
mydict = dict(d, spam=999)
giving {'spam': 999, 'a': 1}. Your proposal would return:
{'spam': 999, 'd': {'a': 1}}
or would have an ambiguous case where the caller would never be sure which
behaviour takes precedence.
--
Steven
[toc] | [prev] | [next] | [standalone]
| From | Michael Selik <michael.selik@gmail.com> |
|---|---|
| Date | 2016-06-26 18:17 +0000 |
| Message-ID | <mailman.10.1466965034.2358.python-list@python.org> |
| In reply to | #110276 |
On Tue, Jun 21, 2016 at 9:41 PM Steven D'Aprano <steve@pearwood.info> wrote:
> On Tue, 21 Jun 2016 05:34 pm, Ari Freund wrote:
> > var3, var1, var2 = **d
>
> But I don't want to use the key names your function uses. I want to
> use names which makes sense for my application
>
Note that my dict unpacking syntax proposal would solve this quite nicely.
{'var1': width, 'var2': counter, 'var3': aardvark} = d
But the current syntax ain't so bad either. Maybe it's even better, more
readable despite being less concise.
expected = {'var1', 'var2', 'var3'}
excess = d.keys() - expected
if excess:
raise ValueError('unexpected keys {!r}'.format(excess))
width = d['var1']
counter = d['var2']
aardvark = d['var3']
[toc] | [prev] | [next] | [standalone]
| From | arief@google.com |
|---|---|
| Date | 2016-06-26 08:28 -0700 |
| Message-ID | <9bf8880c-12ce-4155-b70d-4e63460ee464@googlegroups.com> |
| In reply to | #110217 |
Thanks everybody. There seems to be a lot of resistance to dict unpacking, in addition to the problem with my proposed shorthand dict() initialization syntax pointed out by Steven D'Aprano, so I won't be pursuing this.
[toc] | [prev] | [next] | [standalone]
| From | Joonas Liik <liik.joonas@gmail.com> |
|---|---|
| Date | 2016-06-26 18:50 +0300 |
| Message-ID | <mailman.7.1466956217.2358.python-list@python.org> |
| In reply to | #110520 |
On 26 June 2016 at 18:28, Ari Freund via Python-list <python-list@python.org> wrote: > Thanks everybody. There seems to be a lot of resistance to dict unpacking, in addition to the problem with my proposed shorthand dict() initialization syntax pointed out by Steven D'Aprano, so I won't be pursuing this. > -- > https://mail.python.org/mailman/listinfo/python-list something like: a, b, c = my_magic_reordering_func(mydict, ["alpha", "beta", "charlie"]) ..is not too far off, altho it is a little repetitive is is trivial to write in current python and reaches the goal of insulating you from some degree of change in the return value. can also be extended to work with namedtuple for example (which you probably should already be using if you want to return so many arguments that this is a problem, returning several arguments is already returning a tuple under the hood afterall)
[toc] | [prev] | [standalone]
Back to top | Article view | comp.lang.python
csiph-web