Velocity Reviews - Computer Hardware Reviews

Velocity Reviews > Newsgroups > Programming > Python > Pythonification of the asterisk-based collection packing/unpacking syntax

Reply
Thread Tools

Pythonification of the asterisk-based collection packing/unpacking syntax

 
 
Steven D'Aprano
Guest
Posts: n/a
 
      12-18-2011
On Sat, 17 Dec 2011 23:33:27 -0600, Evan Driscoll wrote:


> I do have one more thing to point out, which is that currently the
> Python vararg syntax is very difficult to Google for.


You're coming in late to the conversation, but that was literally the
second thing pointed out by the original poster:

On Sun, 11 Dec 2011 11:49:23 +0100, Eelco Hoogendoorn wrote:
> Throwing an idea for a PEP out there:
>
> It strikes me that the def func(*args, **kwargs) syntax is rather
> unpytonic. It certainly did not have that 'for line in file'
> pythonic obviousness for me as a beginner. Plus, asterikses are
> impossible to google for, so finding out what exactly they do more
> or less forces you to write a forum post about it.


And rebutted. Modesty[1] prevents me from quoting myself, but here are
some links to searches:

http://duckduckgo.com/?q=python+asterisk
http://duckduckgo.com/?q=python+*

My normal first place to look for something is Wikipedia. Enjoy it before
SOPA kills it.

http://en.wikipedia.org/wiki/Asteris...ming_languages



> In the first pages
> of the four searches matching "python (function)? (star | asterisk)",


Your search looks overly complicated to me.

I'm not an expert on Google's syntax, but if you search for "python,
optionally with function", isn't that the same as just searching for
"python" since it will return hits either with or without "function"?


> I certainly remember having a small amount of difficulty figuring out
> what the heck * and ** did the first time I encountered them.


Answering that sort of question is what the interactive interpreter
excels at. Suppose you see a function declared with *args as an argument,
and you have no idea what it means. Try it and find out!

py> def spam(*args):
.... print(args, type(args))
....
py> spam(42)
((42,), <type 'tuple'>)
py> spam(42, 23)
((42, 23), <type 'tuple'>)

Never underestimate the power of Python's introspection tools, especially
the two simplest ones: print and type. Often you will learn more in 10
minutes experimentation than in an hour googling.



[1] Eh, who am I fooling?

--
Steven
 
Reply With Quote
 
 
 
 
Steven D'Aprano
Guest
Posts: n/a
 
      12-18-2011
On Sat, 17 Dec 2011 23:33:27 -0600, Evan Driscoll wrote:

> This would suggest perhaps some keywords might be called for instead of
> operators.


The barrier to new keywords in Python is very high. Not going to happen
for something that already has perfectly good syntax already familiar to
Python and Ruby programmers. Might else well try to get C and Java to
stop using "..." (ellipses).


> In the grand scheme of things the argument packing and
> unpacking are not *all* that common, so I don't think the syntactic
> burden would be immense.


The burden is that you complicate the compiler and reduce the pool of
useful names available to the programmer. To be useful, the keywords need
to be short, meaningful and memorable -- which means they're already used
in code, which you will break needlessly.

With operators that otherwise would be illegal, the programmer doesn't
need to learn about varargs until they need to. With keywords, the
programmer needs to learn "you can't use varargs or kwargs as variable
names" practically from day 1.


> But then you could also say
> def foo(varargs(list) l, kwargs(dict) d)


So you're not just adding keywords, you're adding new syntax: something
which looks like a function call, but isn't a function call. Another
feature which the programmer has to learn just to be able to read Python
source code -- and one which almost certainly is going to clash with
function annotations as people start using them.

I'm going to pose the same question to you I have already posed to Eelco:
in your proposal, what happens if the caller has shadowed the list built-
in? Does argument l become a built-in list, or does it become whatever
type list is currently bound to?

Some more questions:

Can varargs accepted arbitrary callables, or only a fixed set of known
types?

How does this differ from what can be done with annotations?


--
Steven
 
Reply With Quote
 
 
 
 
Chris Angelico
Guest
Posts: n/a
 
      12-18-2011
On Sun, Dec 18, 2011 at 6:31 PM, Steven D'Aprano
<steve+> wrote:
> My normal first place to look for something is Wikipedia. Enjoy it before
> SOPA kills it.
>
> http://en.wikipedia.org/wiki/Asteris...ming_languages


I would never expect to find this sort of thing in the article on the
asterisk; maybe I'd look on the language's article, but more likely I
would be looking for info on the language's own web site.

> On Sat, 17 Dec 2011 23:33:27 -0600, Evan Driscoll wrote:
>> In the first pages
>> of the four searches matching "python (function)? (star | asterisk)",

>
> Your search looks overly complicated to me.


I understand this to mean that he did four searches:
* python star
* python function star
* python asterisk
* python function asterisk

> Never underestimate the power of Python's introspection tools, especially
> the two simplest ones: print and type. Often you will learn more in 10
> minutes experimentation than in an hour googling.


+1 QOTW. I refer to this as "IIDPIO debugging" (Eyed Pio) - If In
Doubt, Print It Out.

ChrisA
 
Reply With Quote
 
Steven D'Aprano
Guest
Posts: n/a
 
      12-18-2011
On Sat, 17 Dec 2011 21:03:01 -0600, Evan Driscoll wrote:

> Something like ML or Haskell, which does not even allow integer to
> double promotions, is very strong typing. Something like Java, which
> allows some arithmetic conversion and also automatic stringification (a
> la "1" + 1) is somewhere in the middle of the spectrum. Personally I'd
> put Python even weaker on account of things such as '[1,2]*2' and '1 <
> True' being allowed,


They are not examples of weak typing. The first is an example of operator
overloading, the second is a side-effect of a compromise made for
backwards compatibility.

If the first example were written:

[1, 2].repeat(2)

I expect you'd be perfectly happy with it as an unexceptional example of
a list method: it takes an integer count, and returns a new list
consisting of the elements of the original list repeated twice.

If it were written:

[1, 2].__mul__(2)

you'd probably raise an eyebrow at the ugliness of the method name, but
you would be okay with the concept.

Well, that's exactly what it is in Python: the list __mul__ method
performs sequence multiplication (repeating the contents), which
overloads the * operator. No automatic type conversions needed, so not an
example of weak typing.


As for comparisons between ints and True or False, that's not weak typing
either, because True and False *are* ints: bool is a subclass of int.
This was done purely for historical reasons: originally Python didn't
have a bool type, and you used 0 and 1 by convention for boolean values.
When it was decided to add a bool type, the least disruptive way to do
this was to define it as a subclass of int.


--
Steven
 
Reply With Quote
 
Roy Smith
Guest
Posts: n/a
 
      12-18-2011
In article <mailman.3787.1324197792.27778.python->,
Chris Angelico <> wrote:

> > Never underestimate the power of Python's introspection tools, especially
> > the two simplest ones: print and type. Often you will learn more in 10
> > minutes experimentation than in an hour googling.

>
> +1 QOTW. I refer to this as "IIDPIO debugging" (Eyed Pio) - If In
> Doubt, Print It Out.


I always knew it by the name "Ask the computer". As in, "WTF is foo?
Let's ask the computer!".

In addition to print and type, I'm a big fan of dir(). Often, I know an
object has a method to do what I want, but I can't remember the name.
For example, the other day, I was using a set (which I don't use very
often). I needed the method to remove an item from the set. Faster
than finding the right place in the docs, I just fired up an interpreter
and typed dir(set()) at it.
 
Reply With Quote
 
Chris Angelico
Guest
Posts: n/a
 
      12-18-2011
On Mon, Dec 19, 2011 at 12:58 AM, Roy Smith <> wrote:
> In addition to print and type, I'm a big fan of dir(). *Often, I know an
> object has a method to do what I want, but I can't remember the name.
> For example, the other day, I was using a set (which I don't use very
> often). *I needed the method to remove an item from the set. *Faster
> than finding the right place in the docs, I just fired up an interpreter
> and typed dir(set()) at it.


That's excellent when all you need is the name. I usually have IDLE
running (all the time - which sometimes produces oddities after I
experiment with monkey-patching some module or other, and then oddly
enough, things don't work properly!!), and will snap off "help(foo)"
for any foo. Unfortunately help() is at times unhelpful, and even at
its best it's very spammy. There's no one best solution; the
successful programmer will usually have a large toolset at his
disposal.

ChrisA
 
Reply With Quote
 
Eelco
Guest
Posts: n/a
 
      12-18-2011
On Dec 18, 1:59*am, Steven D'Aprano <steve
+comp.lang.pyt...@pearwood.info> wrote:
> On Sat, 17 Dec 2011 06:38:22 -0800, Eelco wrote:
> > Type constraints:

>
> > In case the asterisk is not used to signal unpacking, but rather to
> > signal packing, its semantics is essentially that of a type constraint.

>
> "Type constraint" normally refers to type restrictions on *input*: it is
> a restriction on what types are accepted. When it refers to output, it is
> not normally a restriction, therefore "constraint" is inappropriate.
> Instead it is normally described as a coercion, cast or conversion.
> Automatic type conversions are the opposite of a constraint: it is a
> loosening of restrictions. "I don't have to use a list, I can use any
> sequence or iterator".


Casts or conversions are a runtime concept; im talking about
declarations. That seems to be the source of your confusion.

> In iterator unpacking, it is the *output* which is a list, not a
> restriction on input: in the statement:
>
> head, *tail = sequence
>
> tail may not exist before the assignment, and so describing this as a
> constraint on the type of tail is completely inappropriate.


Yes, the variable tail is being (re)declared here. Thats exactly why I
call it a type constraint. Im not sure what the CS books have to say
on the matter, I guess this use of the term entered my lexicon through
the C# developer team. Either way, you seem to be the only one who
does not grok my intended meaning, so I suggest you try reading it
again.

> > The statement:

>
> > * * head, tail = sequence

>
> > Signifies regular unpacking. However, if we add an asterisk, as in:

>
> > * * head, *tail = sequence

>
> > We demand that tail not be just any python object, but rather a list.

>
> We don't demand anything, any more than when we say:
>
> for x in range(1, 100):
>
> we "demand" that x is not just any python object, but rather an int.
>
> Rather, we accept what we're given: in case of range and the for loop, we
> are given an int. In the case of extended tuple unpacking, we are given a
> list.


for x in range is syntactic sugar for a series of assignments to x; x
is an unconstrained variable that will indeed take anything it gets;
the semantics of what comes after 'x' does in no way depend on x
itself. head, tail = l and head, *tail = l mean something completely
different, and the only difference is a constraint placed on tail,
which forces the semantics to be different; the righthand side, or
what is to be assigned, is identical. Of course one can just regard it
as syntactic sugar for head, tail = unpackheadandtailaslist(l); but
the syntactic sugar achieves that same end through a type constraint
on tail. Really.

> You are jumping to conclusions about implementation details which aren't
> supported by the visible behaviour. What evidence do you have that
> iterator unpacking creates a tuple first and then converts it to a list?


You are jumping to conclusions about my opinion which aren't supported
by my visible behaviour. What evidence do you have that I ever even
said any such thing?

> > The aim of this PEP, is that this type-constraint syntax is expanded
> > upon. We should be careful here to distinguish with providing optional
> > type constraints throughout python as a whole; this is not our aim.

>
> Iterator unpacking is no more about type constraints than is len().


Because you wish to keep nitpicking about my usage of the term 'type
constraint' (even though you have not introduced an alternative term
yourself), or because you actually disagree with the content of my
message?
 
Reply With Quote
 
Eelco
Guest
Posts: n/a
 
      12-18-2011
On Dec 18, 5:52*am, buck <workithar...@gmail.com> wrote:
> I like the spirit of this. Let's look at your examples.


Glad to see an actual on-topic reply; thanks.

> > Examples of use:
> > * * head, tail::tuple = ::sequence
> > * * def foo(args::list, kwargs::dict): pass
> > * * foo(::args, ::kwargs)

>
> My initial reaction was "nonono!", but this is simply because of the ugliness. The double-colon is very visually busy.


I would have preferred the single colon, but its taken. But exactly
this syntax is used in other languages (for what that is worth...)

> I find that your second example is inconsistent with the others. If we say that the variable-name is always on the right-hand-side, we get:
>
> > * * def foo(list::args, dict::kwargs): pass

>
> This nicely mirrors other languages (such as in your C# example: *"float foo") as well as the old python behavior (prefixing variables with */** to modify the assignment).


The link in my OP has the BDFL discussing type constraints; he also
prefers identifier:type. Many modern languages have something similar.
How is my second example inconsistent?

> As for the separator, let's examine the available ascii punctuation. Excluding valid variable characters, whitespace, and operators, we have:
>
> ! -- ok.
> " -- can't use this. Would look like a string.
> # -- no. Would looks like a comment.
> $ -- ok.
> ' -- no. Would look like a string.
> ( -- no. Would look like a function.
> ) -- no. Would look like ... bad syntax.
> , -- no. Would indicate a separate item in the variable list.
> . -- no. Would look like an attribute.
> : -- ok, maybe. Seems confusing in a colon-terminated statement.
> ; -- no, just no.
> ? -- ok.
> @ -- ok.
> [ -- no. Would look like indexing.
> ] -- no.
> ` -- no. Would look like a string?
> { -- too strange} -- too strange
>
> ~ -- ok.
>
> That leaves these. Which one looks least strange?
>
> float ! x = 1
> float $ x = 1
> float ? x = 1
> float @ x = 1
>
> The last one looks decorator-ish, but maybe that's proper. The implementation of this would be quite decorator-like: take the "normal" value of x, pass it through the indicated function, assign that value back to x.


I dont think thats too proper an analogy. The type constraint does not
just coerce the content that is bound to the variable, it influences
the semantics of the whole statement; so insofar there is a suggested
relation with decorators, id rather not have it.


>
> Try these on for size.
>
> * * *head, @tuple tail = sequence
> * * *def foo(@list args, @dict kwargs): pass
> * * *foo(@args, @kwargs)
>
> For backward compatibility, we could say that the unary * is identical to@list and unary ** is identical to @dict.


Yes, maximum backwards compatibility would be great.
 
Reply With Quote
 
Eelco
Guest
Posts: n/a
 
      12-18-2011
On Dec 18, 6:33*am, Evan Driscoll <edrisc...@wisc.edu> wrote:
> On 12/17/2011 22:52, buck wrote:> Try these on for size.
>
> > * * *head, @tuple tail = sequence
> > * * *def foo(@list args, @dict kwargs): pass
> > * * *foo(@args, @kwargs)

>
> > For backward compatibility, we could say that the unary * is identical to @list and unary ** is identical to @dict.

>
> I like this idea much more than the original one. In addition to the
> arguments buck puts forth, which I find compelling, I have one more: you
> go to great length to say "this isn't really type checking in any sense"
> (which is true)... but then you go forth and pick a syntax that looks
> almost exactly like how you name types in many languages! (In fact,
> except for the fact that it's inline, the 'object :: type' syntax is
> *exactly* how you name types in Haskell.)


No, its not type *checking*, its type *declaration*. I tried to go to
great lengths to point that out, but it appears I did not do a very
good job . Type declaration is exactly what I want, and insofar this
syntax has already found adoptation elsewhere, ill consider that a
plus.

> I have a bigger objection with the general idea, however.It seems very
> strange that you should have to specify types to use it. If the */**
> syntax were removed, that would make the proposed syntax very very
> unusual for Python. I could be missing something, but I can think of any
> other place where you have to name a type except where the type is an
> integral part of what you're trying to do. (I would not say that
> choosing between tuples and lists are an integral part of dealing with
> vararg functions.) If */** were to stick around, I could see 99% of
> users continuing to use them. And then what has the new syntax achieved?


Good point; technically the askeriskes could be kept for backwards
compatibility, but that would break 'there should only be one way to
do it'. Indeed it would be unusual for python to have to explicitly
name a type, but implicitly ** does very much the same. Its just a
cryptic way of saying 'dict please'.

> You can fix this if you don't require the types and just allow the user
> to say "def foo(@args)" and "foo(@args)". Except... that's starting to
> look pretty familiar... (Not to mention if you just omit the type from
> the examples above you need another way to distinguish between args and
> kwargs.)


Yes, one could opt for a syntax where the collection type is optional
and a sensible default is chosen, But to me that would largely defeat
the point; I very much like the added verbosity and explicitness. args-
tuple and kwargs-dict; that just has a much better ring to it than
star-star-kwargs, or whatever other cryptic symbol you use.

> I have one more suggestion.
>
> I do have one more thing to point out, which is that currently the
> Python vararg syntax is very difficult to Google for. In the first pages
> of the four searches matching "python (function)? (star | asterisk)",
> there was just one relevant hit on python.org which wasn't a bug report.
> I certainly remember having a small amount of difficulty figuring out
> what the heck * and ** did the first time I encountered them.
>
> This would suggest perhaps some keywords might be called for instead of
> operators. In the grand scheme of things the argument packing and
> unpacking are not *all* that common, so I don't think the syntactic
> burden would be immense. The bigger issue, of course, would be picking
> good words.
>
> This also helps with the issue above. Let's say we'll use 'varargs' and
> 'kwargs', though the latter too well-ingrained in code to steal. (I
> don't want to get too much into the debate over *what* word to choose.
> Also these don't match the 'head, *tail = l' syntax very well.) Then we
> could say:
> * def foo(varargs l, kwargs d):
> * * * bar(varargs l, kwargs d)
> and varargs would be equivalent to * and kwargs would be equivalent to
> **. But then you could also say
> * def foo(varargs(list) l, kwargs(dict) d)


I agree; I had the same experience. But according to others our
opinion is wrong, and we should just become better at using google.
 
Reply With Quote
 
Steven D'Aprano
Guest
Posts: n/a
 
      12-18-2011
On Sun, 18 Dec 2011 06:13:37 -0800, Eelco wrote:

> Casts or conversions are a runtime concept; im talking about
> declarations. That seems to be the source of your confusion.


Everything in Python happens at runtime, apart from compilation of source
code into byte code. Python doesn't have declarations. Even class and def
are statements which happen at runtime, not declarations which happen at
compile time.

Your proposal will be no different: if it happens, it will happen at
runtime.


[...]
> I guess this use of the term entered my lexicon through the
> C# developer team. Either way, you seem to be the only one who does not
> grok my intended meaning, so I suggest you try reading it again.


I grok your *intended* meaning. I also grok that you are using the wrong
words to explain your intended meaning.

"head, *tail = iterable" is not a declaration in Python. It is not a type
conversion, or a constraint, or a cast. It is an iterator unpacking
operation. It is syntactic sugar for something similar to this:

tmp = iter(iterable)
head = next(tmp)
tail = []
try:
while True:
tail.append(next(tmp))
except StopIteration:
pass
del tmp


The only constraint is on the *input* to the operation: the right hand
side object must obey the iterator protocol. The only conversion (if we
want to call it such) is that an iterator object is created by the right
hand side object.

[...]
> head, tail = l and head, *tail = l mean something completely different,


They are both iterator unpacking, and therefore by definition not
"completely different". The first one is syntactic sugar for something
like this:

tmp = iter(iterable)
head = next(tmp)
tail = next(tmp)
if tmp is not empty: raise exception
del tmp


and the second as shown earlier. Clearly they are quite similar: the only
difference is that in the first case, the right hand side must have
exactly two items, while in the second case, the right hand side can have
an arbitrary number of items.


> and the only difference is a constraint placed on tail, which forces the
> semantics to be different; the righthand side, or what is to be
> assigned, is identical. Of course one can just regard it as syntactic
> sugar for head, tail = unpackheadandtailaslist(l); but the syntactic
> sugar achieves that same end through a type constraint on tail. Really.


All the words are in English, but I have no understanding of what you are
trying to say here. What is unpackheadandtailaslist and how does it
differ from actual unpacking in Python?


>> You are jumping to conclusions about implementation details which
>> aren't supported by the visible behaviour. What evidence do you have
>> that iterator unpacking creates a tuple first and then converts it to a
>> list?

>
> You are jumping to conclusions about my opinion which aren't supported
> by my visible behaviour. What evidence do you have that I ever even said
> any such thing?


My evidence was your actual words, quoted in my post, which you deleted
in your response.

Here they are again:

[quote]
This changes the semantics from normal unpacking, to unpacking
and then repacking all but the head into a list.
[end quote]

In context, "this changes..." refers to extended iterator unpacking in
the form "head, *tail" contrasted with "head, tail =...", and that it
generates a list.

It may very well be that I have misunderstood you. If you do not intend
the meaning that extended tuple unpacking first unpacks to a tuple and
then re-packs to a list, then please explain what you did mean.


>> > The aim of this PEP, is that this type-constraint syntax is expanded
>> > upon. We should be careful here to distinguish with providing
>> > optional type constraints throughout python as a whole; this is not
>> > our aim.

>>
>> Iterator unpacking is no more about type constraints than is len().

>
> Because you wish to keep nitpicking about my usage of the term 'type
> constraint' (even though you have not introduced an alternative term
> yourself), or because you actually disagree with the content of my
> message?


I don't think much about your proposal. I think it is unnecessary, based
on a profound misunderstanding of how Python actually operates, badly
explained using inappropriate terms, and the syntax you propose is ugly.


--
Steven
 
Reply With Quote
 
 
 
Reply

Thread Tools

Posting Rules
You may not post new threads
You may not post replies
You may not post attachments
You may not edit your posts

BB code is On
Smilies are On
[IMG] code is On
HTML code is Off
Trackbacks are On
Pingbacks are On
Refbacks are Off


Similar Threads
Thread Thread Starter Forum Replies Last Post
create collection of collection Hemant ASP .Net 1 10-22-2009 03:04 PM
Collection problems (create Collection object, add data to collection, bind collection to datagrid) Øyvind Isaksen ASP .Net 1 05-18-2007 09:24 AM
Sorting the Collection(java.util.Collection) of Elements Pradeep Java 2 01-24-2007 02:33 PM
Adding to collection from controlDesigner only produces end tag for the collection. Don ASP .Net Building Controls 0 07-22-2005 09:31 PM
STL - an algorithm for finding a collection within a collection? Dylan C++ 5 03-22-2005 01:31 AM



Advertisments
 



1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57