-------- Original message --------From: dn via Python-list
<python-list@python.org> Date: 7/7/20 16:04 (GMT+10:00) To: 'Python'
<python-list@python.org> Subject: Questioning the effects of multiple
assignment TLDR; if you are a Python 'Master' then feel free to skim the first
part (which you should know hands-down), until the excerpts from 'the manual'
and from there I'll be interested in learning from you...Yesterday I asked a
junior prog to expand an __init__() to accept either a series of (>1) scalars
(as it does now), or to take similar values but presented as a tuple. He was a
bit concerned that I didn't want to introduce a (separate) keyword-argument,
until 'we' remembered starred-parameters. He then set about experimenting.
Here's a dichotomy that surfaced as part of our 'play':-(my problem is: I can't
(reasonably) answer his question...)If you read this code:NB The print-ing does
not follow the input-sequence, because that's the point to be made... >>> def
f( a, *b, c=0 ):Shouldn't that def be ...>>> def f(a, c=0, *b):???...
print( a, type( a ) )... print( c, type( c ) )... print( b )... >>> f(
1, 'two', 3, 'four' )[I had to force "c" to become a keyword argument, but
other than that, we'll be using these three parameters and four
argument-values, again]Question 1: did you correctly predict the output?1
<class 'int'>0 <class 'int'>('two', 3, 'four')Ahah, "c" went to default because
there was no way to identify when the "*b" 'stopped' and "c" started - so all
the values 'went' to become "b" (were all "consumed by"...).Why did I also
print "b" differently?Building tension!Please read on, gentle reader...Let's
make two small changes:- amend the last line of the function to be similar:...
print( b, type( b ) )- make proper use of the function's API: >>> f( 1,
'two', 3, c='four' )Question 2: can you predict the output of "a"? Well
duh!(same as before)1 <class 'int'>Question 3: has your predicted output of "c"
changed? Yes? Good!(Web.Refs below, explain; should you wish...)four <class
'str'>Question 4: can you correctly predict the content of "b" and its
type?('two', 3) <class 'tuple'>That makes sense, doesn't it? The arguments were
presented to the function as a tuple, and those not assigned to a scalar value
("a" and "c") were kept as a tuple when assigned to "b".Jolly good show,
chaps!(which made my young(er) colleague very happy, because now he could see
that by checking the length of the parameter, such would reveal if the
arguments were being passed as scalars or as a tuple.Aside: however, it made me
think how handy it would be if the newly-drafted PEP 622 -- Structural Pattern
Matching were available today (proposed for v3.10,
https://www.python.org/dev/peps/pep-0622/) because (YAGNI-aside) we could then
more-easily empower the API to accept other/more collections!Why am I writing
then?Because during the same conversations I was
'demonstrating'/training/playing with some code that is (apparently) very
similar - and yet, it's not. Oops!Sticking with the same, toy-data, let's code:
>>> a, *b, c = 1, 'two', 3, 'four' >>> a, type( a ) >>> c, type( c ) >>> b,
type( b )Question 5: what do you expect "a" and "c" to deliver in this
context?(1, <class 'int'>)('four', <class 'str'>)Happy so far?Question 6: (for
maximum effect, re-read snippets from above, then) what do you expect from
"b"?(['two', 3], <class 'list'>)List? A list? What's this "list" stuff???When
"b" was a parameter (above) it was assigned a tuple!Are you as shocked as
I?Have you learned something?(will it ever be useful?)Has the world stopped
turning?Can you explain why these two (apparently) logical assignment processes
have been designed to realise different result-objects?NB The list cf tuple
difference is 'legal' - at least in the sense that it is documented/expected
behavior:-Python Reference Manual: 7.2. Assignment statementsAssignment
statements are used to (re)bind names to values and to modify attributes or
items of mutable objects:...An assignment statement evaluates the expression
list (remember that this can be a single expression or a comma-separated list,
the latter yielding a tuple) and assigns the single resulting object to each of
the target lists, from left to right....A list of the remaining items in the
iterable is then assigned to the starred target (the list can be
empty).https://docs.python.org/3/reference/simple_stmts.html#assignment-statementsPython
Reference Manual: 6.3.4. CallsA call calls a callable object (e.g., a
function) with a possibly empty series of arguments:...If there are more
positional arguments than there are formal parameter slots, a TypeError
exception is raised, unless a formal parameter using the syntax *identifier is
present; in this case, that formal parameter receives a tuple containing the
excess positional arguments (or an empty tuple if there were no excess
positional
arguments).https://docs.python.org/dev/reference/expressions.html#calls--
Regards,=dn-- https://mail.python.org/mailman/listinfo/python-list
--
https://mail.python.org/mailman/listinfo/python-list