Pythonification of the asterisk-based collection packing/unpacking syntax

Steven D'Aprano steve+comp.lang.python at pearwood.info
Tue Dec 27 17:57:26 EST 2011


On Mon, 26 Dec 2011 13:41:34 -0800, Eelco wrote:

> On Dec 25, 6:05 pm, Steven D'Aprano <steve
> +comp.lang.pyt... at pearwood.info> wrote:
>> On Sun, 25 Dec 2011 07:38:17 -0800, Eelco wrote:
[...]

>> > How is 'head, *tail = sequence' or semantically entirely
>> > equivalently, 'head, tail::list = sequence' any different then? Of
>> > course after interpretation/compilation, what it boils down to is
>> > that we are constructing a list and binding it to the identifier
>> > tail, but that is not how it is formulated in python as a language
>>
>> I'm afraid it is.
>>
>> Here's the definition of assignment in Python 3:
>> http://docs.python.org/py3k/reference/simple_stmts.html#assignment-
>> statements
> 
> Que?

You have claimed that "constructing a list and binding it to the 
identifier" is not how iterator unpacking is formulated in Python the 
language. But that is wrong. That *is* how iterator unpacking is 
formulated in Python the language. The reference manual goes into detail 
on how assignment is defined in Python. You should read it.


> 'head, *tail = sequence'
> 
> Is how one currently unpacks a head and tail in idiomatic python
> 
> This is semantically equivalent to
> 
> 'head = sequence[0]'
> 'tail = list(sequence[1:])'

There's a reason this feature is called *iterable* unpacking: it operates 
on any iterable object, not just indexable sequences. 

'head, *tail = sequence' is not just semantically equivalent to, but 
*actually is* implemented as something very close to:

temp = iter(sequence)
head = next(temp)
tail = list(temp)
del temp

Extended iterable unpacking, as in 'head, *middle, tail = sequence' is a 
little more complex, but otherwise the same: it operates using the 
iterator protocol, not indexing. I recommend you read the PEP, if you 
haven't already done so.

http://www.python.org/dev/peps/pep-3132/


> But these forms are linguistically different, in too many different ways
> to mention.

How about mentioning even one way? Because I have no idea what 
differences you are referring to, or why you think they are important.


>> > We dont have
>> > something of the form 'tail = list_tail(sequence)'.
>>
>> I'm afraid we do. See the definition of assignment again.
> 
> Que?

Again, you make a claim about Python which is contradicted by the 
documented behaviour of the language. You claim that we DON'T have 
something of the form 'tail = list_tail(sequence)', but that is *exactly* 
what we DO have: extracting the tail from an iterator.

Obviously there is no built-in function "list_tail" but we get the same 
effect by just using list() on an iterator after advancing past the first 
item.


> My claim is that the two semantically identical formulations above do
> not have isomorphic linguistic form. As far as I can make sense of your
> words, you seem to be disputing this claim, but its a claim as much
> worth debating as that the sun rises in the east.

"Isomorphic linguistic form"? Are you referring to the idea from 
linguistics that there are analogies between the structure of phonic and 
semantic units? E.g. that the structures (phoneme, syllable, word) and 
(sememe, onomateme, sentence) are analogous.

I don't see why this is relevant, or which units you are referring to -- 
the human-language description of what Python does, high-level Python 
language features, Python byte-code, or machine code. Not that it 
matters, but it is unlikely that any of those are isomorphic in the 
linguistic sense, and I am not making any general claim that they are.

If not, I have no idea what you are getting at, except possibly trying to 
hide behind obfuscation.



>> > Rather, we annotate
>> > the identifier 'tail' with an attribute that unquestionably
>> > destinates it to become a list*. It is no longer that 'tail' will
>> > just take anything that pops out of the expression on the right hand
>> > side;
>>
>> Of course it will. Python is a dynamically typed language. It doesn't
>> suddenly develop static types to ensure that 'tail' becomes a list;
>> 'tail' is bound to a list because that's what the assignment statement
>> provides.
> 
> How python accomplishes any of this under the hood is entirely
> immaterial. The form is that of a compile-time type constraint,
> regardless of whether the BDFL ever thought about it in these terms.

Compile-time type constraints only have meaning in statically-typed 
languages. Otherwise, you are applying an analogy that simply doesn't 
fit, like claiming that the motion of paper airplanes and birds are 
equivalent just because in English we use the word "fly" to describe them 
both.

The differences between what Python actually does and compile-time type-
constraints are greater than the similarities.

Similarities:

(1) In both cases, 'tail' ends up as a list.

Differences:

(1) Compiler enforces the constraint that the identifier 'tail' is always 
associated with a list and will prevent any attempt to associate 'tail' 
with some value that is not a list. Python does nothing like this: the 
identifier 'tail' has no type restrictions at all, and can be used for 
any object.

(2) Errors are detectable at compile-time with an error that halts 
compilation; Python detects errors at runtime with an exception that can 
be caught and by-passed without halting execution.

(3) "Constraint" has the conventional meaning of a restriction, not a 
result; a type-constraint refers to a variable being prevented from being 
set to some other type, not that it merely becomes set to that type. For 
example, one doesn't refer to 'x = 1' as a type-constraint merely because 
x gets set to an int value; why do you insist that 'head, *tail = 
sequence' is a type-constraint merely because tail gets set to a list?



>> > rather,
>> > the semantics of what will go on at right hand side is coerced by the
>> > constraint placed on 'tail'.
>>
>> But it isn't a constraint placed on 'tail'. It is a consequence of the
>> definition of assignment in Python 3. 'tail' becomes bound to a list
>> because that is what the assignment statement is defined to do in that
>> circumstance, not because the identifier (symbol) 'tail' is constrained
>> to only accept lists. 'tail' may not even exist before hand, so talking
>> about constraints on 'tail' is an abuse of language, AS YOU AGREED
>> ABOVE.
> 
> 'tail' is (re)declared on the spot as a brand-new identifier (type
> constraint included); whether it exists before has no significance
> whatsoever, since python allows rebinding of identifiers.

Given the following:

tail = "beautiful red plumage"
head, *tail = [1, 2, 3, 4]
tail = 42

is it your option that all three instantiations of the *identifier* 
'tail' (one in each line) are *different* identifiers? Otherwise, your 
statement about "brand new identifier" is simply wrong.

If you allow that they are the same identifier with three different 
values (and three different types), then this completely destroys your 
argument that there is a constraint on the identifier 'tail'. First 
'tail' is a string, then it is a list, then it is an int. If that is a 
type constraint (restriction) on 'tail', then constraint has no meaning.

If you state that they are three different identifiers that just happen 
to be identical in every way that can be detected, then you are engaged 
in counting angels dancing on the head of pins. Given this idea that the 
middle 'tail' identifier is a different identifier from the other two, 
then I might accept that there *could* be a type-constraint on middle 
'tail', at least in some implementation of Python that hasn't yet been 
created. But what a pointless argument to make.


>> > *(I call that a 'type constraint', because that is what it literally
>> > is;
>>
>> No. It is literally a name binding of a dynamically typed,
>> unconstrained name to an object which happens to be a list.
> 
> Let me take a step back and reflect on the form of the argument we are
> having. I claim the object in front of us is a 'cube'. You deny this
> claim, by countering that it is 'just a particular configuration of
> atoms'.

No no no, you have utterly misunderstood my argument. I'm not calling it 
a configuration of atoms. I'm calling it a pyramid, and pointing out that 
no matter how many times you state it is a cube, it actually has FOUR 
sides, not six, and NONE of the sides are square, so it can't possibly be 
a cube.



> 'look at the definition!', you say; 'its just a list of coordinates, no
> mention of cubes whatsoever.'.
> 
> 'Look at the definition of a cube', I counter. 'This particular list of
> coordinates happens to fit the definition, whether the BDFT intended to
> or not'.

But you are wrong. It does not fit the definition of a type-constraint, 
except in the vacuous sense where you declare that there is some 
invisible distinction between the identifier 'tail' in one statement and 
the identical identifier 'tail' in another statement.


> You are correct, in the sense that I do not disagree that it is a
> particular configuration of atoms. But if you had a better understanding
> of cubes, youd realize it meets the definition;

I might not share your deep and expert understanding of cubes, but I 
understand what "type-constraint" means, and I know what Python does in 
iterator unpacking, and I know that there is no sensible definition of 
type-constraint that applies to iterator unpacking in Python.


> the fact that people
> might not make a habit of pausing at this fact (there is generally
> speaking not much of a point in doing so in this case), does not
> diminish the validity of this perspective. But again, if this
> perspective does not offer you anything useful, feel free not to share
> in it.

Quite frankly, I should just let you witter on about type-constraints, 
because (again, in my opinion) it destroys your credibility and will 
reduce even further the already minuscule chance that your proposal will 
be accepted. Since I'm against the idea, I should encourage you to 
describe Python's behaviour in terms that will all but guarantee others 
will dismiss you as knowing little or nothing about Python.

[...]
> Whatever the virtues of your critique of my proposal, you might end up
> wasting less time doing so by reading a book or two on compiler theory.

Perhaps you should spend more time considering the reality of how Python 
works and less time trying to force the triangular peg of Python concepts 
into the square hole of your ideas about theoretical compilers.



-- 
Steven



More information about the Python-list mailing list