Groups | Search | Server Info | Keyboard shortcuts | Login | Register [http] [https] [nntp] [nntps]


Groups > comp.lang.python > #17049 > unrolled thread

Re: Verbose and flexible args and kwargs syntax

Started byTerry Reedy <tjreedy@udel.edu>
First post2011-12-12 10:52 -0500
Last post2011-12-13 12:14 -0800
Articles 14 — 6 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.


Contents

  Re: Verbose and flexible args and kwargs syntax Terry Reedy <tjreedy@udel.edu> - 2011-12-12 10:52 -0500
    Re: Verbose and flexible args and kwargs syntax Eelco <hoogendoorn.eelco@gmail.com> - 2011-12-12 09:12 -0800
      Re: Verbose and flexible args and kwargs syntax Terry Reedy <tjreedy@udel.edu> - 2011-12-12 16:58 -0500
        Re: Verbose and flexible args and kwargs syntax Eelco <hoogendoorn.eelco@gmail.com> - 2011-12-12 15:40 -0800
          Re: Verbose and flexible args and kwargs syntax Ian Kelly <ian.g.kelly@gmail.com> - 2011-12-12 18:41 -0700
            Re: Verbose and flexible args and kwargs syntax Eelco <hoogendoorn.eelco@gmail.com> - 2011-12-13 00:44 -0800
              Re: Verbose and flexible args and kwargs syntax Eelco <hoogendoorn.eelco@gmail.com> - 2011-12-13 01:26 -0800
              Re: Verbose and flexible args and kwargs syntax Ian Kelly <ian.g.kelly@gmail.com> - 2011-12-13 11:15 -0700
                Re: Verbose and flexible args and kwargs syntax Eelco <hoogendoorn.eelco@gmail.com> - 2011-12-13 12:32 -0800
        Re: Verbose and flexible args and kwargs syntax Steven D'Aprano <steve+comp.lang.python@pearwood.info> - 2011-12-13 01:34 +0000
      Re: Verbose and flexible args and kwargs syntax alex23 <wuwei23@gmail.com> - 2011-12-12 16:27 -0800
        Re: Verbose and flexible args and kwargs syntax Eelco <hoogendoorn.eelco@gmail.com> - 2011-12-13 00:30 -0800
          Re: Verbose and flexible args and kwargs syntax Cameron Simpson <cs@zip.com.au> - 2011-12-14 06:11 +1100
            Re: Verbose and flexible args and kwargs syntax Eelco <hoogendoorn.eelco@gmail.com> - 2011-12-13 12:14 -0800

#17049 — Re: Verbose and flexible args and kwargs syntax

FromTerry Reedy <tjreedy@udel.edu>
Date2011-12-12 10:52 -0500
SubjectRe: Verbose and flexible args and kwargs syntax
Message-ID<mailman.3550.1323705162.27778.python-list@python.org>
On 12/12/2011 4:12 AM, Eelco Hoogendoorn wrote:
>> The above examples are seldom needed in Python because we have one
>> general method to repeatedly split a sequence into head and tail.
>
>> it = iter(iterable) # 'it' now represents the sequenced iterable
>> head = next(it) # 'it' now represents the tail after removing the head
>
>> In other words, next(it) encompasses all of your examples and many more.
>> Because 'it' is mutated to represent the tail, it does not need to be
>> rebound and therefore is not.
>
>
> The question in language design is never 'could we do these things
> before'. The answer is obvious: yes our CPUs are turing complete; we can
> do anything. The question is; how would we like to do them?
>
> So do you think the new head/tail unpacking features in python 3 are
> entirely uncalled for?

No, *target unpacking (singular) is quite useful in specialized cases. 
But it is not specifically head/tail unpacking.

 >>> a,*b,c = 1,2,3,4,5,6
 >>> a,b,c
(1, [2, 3, 4, 5], 6)
 >>> *a,b,c = 1,2,3,4,5
 >>> a,b,c
([1, 2, 3], 4, 5)

> I personally quite like them, but I would like them to be more general.

It already is. The *target can be anywhere in the sequence.

-- 
Terry Jan Reedy

[toc] | [next] | [standalone]


#17065

FromEelco <hoogendoorn.eelco@gmail.com>
Date2011-12-12 09:12 -0800
Message-ID<e31c5737-5514-4031-acdd-4882fe11b867@y7g2000vbe.googlegroups.com>
In reply to#17049
> > I personally quite like them, but I would like them to be more general.
>
> It already is. The *target can be anywhere in the sequence.

Im not sure if this is a genuine understanding, or trollish
obtuseness.

Yes, the target can be anywhere in the sequence. And yes, the
resulting list can contain objects of any type, so its very flexible
in that regard too.

But to relate it to the topic of this thread: no, the syntax does not
allow one to select the type of the resulting sequence. It always
constructs a list.

Yes, we can cast the list to be whatever we want on the next line, but
the question is whether this language design can be improved upon. The
choice of a list feels arbitrary, adding another line to cast it to
something else would be even more verbose, and whats more, there would
be serious performance implications if one should seek to apply this
pattern to a deque/linkedlist; it would make taking off the head/tail
of the list from a constant to a linear operation.

That is:

>>> head, deque(tail) = somedeque

Is better in every way I can think of (readability, consistence,
performance) than:

>>> head, *tail = somedeque
>>> tail = deque(tail)

[toc] | [prev] | [next] | [standalone]


#17085

FromTerry Reedy <tjreedy@udel.edu>
Date2011-12-12 16:58 -0500
Message-ID<mailman.3571.1323727128.27778.python-list@python.org>
In reply to#17065
On 12/12/2011 12:12 PM, Eelco wrote:

> Im not sure if this is a genuine understanding, or trollish
> obtuseness.

If you are referring to what I write, it is based on genuine 
understanding of Python.

> Yes, the target can be anywhere in the sequence. And yes, the
> resulting list can contain objects of any type, so its very flexible
> in that regard too.
>
> But to relate it to the topic of this thread: no, the syntax does not
> allow one to select the type of the resulting sequence. It always
> constructs a list.

One use case of *target is to ignore the stuff collected in the target 
because one only wants a few end values from the iterable. Another is to 
pull stuff out because one wants to iterate through the rest. For both 
uses, a list is as good as anything.

> Yes, we can cast the list to be whatever we want on the next line,

Convert. For the very few cases one wants to do this, it is quite adequate.

 > but the question is whether this language design can be improved upon.

Not easily.

> The choice of a list feels arbitrary,

On the contrary, a list is precisely what is needed to collect an 
indefinite number of leftovers.

 > adding another line to cast it to
> something else would be even more verbose, and whats more, there would
> be serious performance implications if one should seek to apply this
> pattern to a deque/linkedlist; it would make taking off the head/tail
> of the list from a constant to a linear operation.

For a linked list, no *target and no copying is needed:

head, tail = llist

>>>> head, deque(tail) = somedeque
>
> Is better in every way I can think of (readability, consistence,
> performance) than:

>>>> head, *tail = somedeque
>>>> tail = deque(tail)

But your suggestion is much worse in each way than

head = somedeque.popleft()

To repeat, there is no reason to copy even once. If one does not want to 
mutate the deque, then one mutates an iterator view of the deque.

-- 
Terry Jan Reedy

[toc] | [prev] | [next] | [standalone]


#17090

FromEelco <hoogendoorn.eelco@gmail.com>
Date2011-12-12 15:40 -0800
Message-ID<bdd08297-8fdd-47a2-a435-b12627bd6482@i6g2000vbh.googlegroups.com>
In reply to#17085
> > Im not sure if this is a genuine understanding, or trollish
> > obtuseness.
>
> If you are referring to what I write, it is based on genuine
> understanding of Python.

This is getting 'interesting'. In a way. I meant to write
'misunderstanding', as I think the context made quite clear. So again
this adds another layer of doubt as to whether you are being obtuse
for its own sake, or if there is yet another layer of misunderstanding
stacked on top of the others. Either way, lets continue with the
benefit of the doubt.

> One use case of *target is to ignore the stuff collected in the target
> because one only wants a few end values from the iterable. Another is to
> pull stuff out because one wants to iterate through the rest. For both
> uses, a list is as good as anything.

So what is the point of having different sequence types, if a list can
do anything? I would argue that the different performance
characteristics and differences in semantics give each sequence type
ample reason for existence.

> Convert. For the very few cases one wants to do this, it is quite adequate.

Or so you opine. As you may recall, the original idea was motivated by
args/kwargs syntactic clarity and flexibility; I merely shifted focus
to this use case of collection unpacking since it is somewhat simpler
and easier to discuss. Keep that in mind, should you seek to build a
case for that assertion in the future.

>  > but the question is whether this language design can be improved upon.
>
> Not easily.

Again, so you opine. Care to give a refutation of my proposed double
colon syntax? Ignoring for a moment whether or not it is useful; does
it clash with anything? I havnt been able to think of anything in the
past few hours, but im not taking that to mean much; there are
probably some subtleties I am overlooking. I think it dos not clash
with slicing syntax for instance, but im not sure.

> For a linked list, no *target and no copying is needed:
>
> head, tail = llist

I have no idea what this means.

> >>>> head, deque(tail) = somedeque
>
> > Is better in every way I can think of (readability, consistence,
> > performance) than:
> >>>> head, *tail = somedeque
> >>>> tail = deque(tail)
>
> But your suggestion is much worse in each way than
>
> head = somedeque.popleft()

No its not. First of all, its not semantically equivalent; popleft
mutates a collection type, and collection unpacking does not; it
creates a set of disjoint views of the collection's data. Whether its
worse in terms of readability is debatable, but in terms of the other
two stated metrics, your claim of it being any kind of worse is
objectively false.

Furthermore, this brings us back again to the point I raised several
times before. Yes, obviously you can already DO these things, but NOT
within the same uniform collection unpacking syntactic construct.
Again, you have failed to point out what is wrong with supplying a
type constrain to python and let it do the right thing based on that;
to reiterate:

head, tail::deque = deque

No need to mutate anything; python can create the view of the
linkedlist internally. A single unambigious syntax, and single
unambigious semantics, for all sequence types. Whats not to like?

If you dont like the extra characters you have to type; there is of
course such a thing as defaults. You can choose:
head, tail:: = deque; if the type constraint is omitted, we could make
tail a list by default, or my preferred solution, infer it from the
right hand side type. In case of the former, all you had to do was
type :: instead of *, and your python universe would otherwise be
completely unchanged. If thats 'not easily', I dont know what is.

> To repeat, there is no reason to copy even once. If one does not want to
> mutate the deque, then one mutates an iterator view of the deque.

And arrive at yet another construction to do the same thing.

[toc] | [prev] | [next] | [standalone]


#17099

FromIan Kelly <ian.g.kelly@gmail.com>
Date2011-12-12 18:41 -0700
Message-ID<mailman.3574.1323740526.27778.python-list@python.org>
In reply to#17090
On Mon, Dec 12, 2011 at 4:40 PM, Eelco <hoogendoorn.eelco@gmail.com> wrote:
>> For a linked list, no *target and no copying is needed:
>>
>> head, tail = llist
>
> I have no idea what this means.

Each node of a linked list consists of a data member and a "next"
member, that holds the next node in the list.  The natural container
for this and the way it is usually implemented in Python is a
2-element tuple.  For example:

llist = ('one', ('two', ('three', ('four', None))))

would be a linked list as typically constructed in Python, and it can
be used in pretty much the same way that lists are used in Lisp.  The
result of the operation:

head, tail = llist

is:

head = 'one'
tail = ('two', ('three', ('four', None)))

and as Terry pointed out, no copying is required to do this.  I
believe deque is also implemented as a doubly-linked list, which is
probably why you keep referring to it as such, but that is an
implementation detail.  When you say "linked list" in relation to
Python, the preceding is what comes to mind.

>> >>>> head, deque(tail) = somedeque
>>
>> > Is better in every way I can think of (readability, consistence,
>> > performance) than:
>> >>>> head, *tail = somedeque
>> >>>> tail = deque(tail)
>>
>> But your suggestion is much worse in each way than
>>
>> head = somedeque.popleft()
>
> No its not. First of all, its not semantically equivalent; popleft
> mutates a collection type, and collection unpacking does not; it
> creates a set of disjoint views of the collection's data. Whether its
> worse in terms of readability is debatable, but in terms of the other
> two stated metrics, your claim of it being any kind of worse is
> objectively false.

I definitely disagree on readability.  Skimming this thread as I have
been, it took me a while to get that your proposed syntax would create
a second deque sharing internal state with the original, rather than
creating a simple copy of the deque, which is what it looks like it
should do.

Incidentally, while that change is not really central to your syntax
proposal, I think it would be very messy.  For example, how do you
propose keeping the length elements in sync?  Inserting an item in one
may or may not affect the length of the other.  If I append an item to
the end of one deque, should the other automatically be extended as
well?  What if the tail node of the second deque occurs after the tail
node of the deque being appended?  Does the appended element then get
inserted into the middle of the second deque (I think it would have to
be)?  If I insert an element into the longer (second) deque that just
happens to be immediately after the tail of the shorter deque, does
*that* cause the shorter deque to be automatically extended?  And
likewise for operations at the head of the deque.

None of these questions have obvious answers as to the "right" way to
it, and for that reason I think this is probably best left to the user
to implement a custom deque view class with whatever semantics they
prefer.

> Furthermore, this brings us back again to the point I raised several
> times before. Yes, obviously you can already DO these things, but NOT
> within the same uniform collection unpacking syntactic construct.
> Again, you have failed to point out what is wrong with supplying a
> type constrain to python and let it do the right thing based on that;
> to reiterate:
>
> head, tail::deque = deque

Python users generally follow the rule "explicit is better than
implicit".  Setting a general constraint and letting the language "do
the right thing" is a kind of black magic that feels off because it
tends to break that rule.  But that's not to say that black magic
never wins -- just look at super() and the MRO.

> If you dont like the extra characters you have to type; there is of
> course such a thing as defaults. You can choose:
> head, tail:: = deque; if the type constraint is omitted, we could make
> tail a list by default, or my preferred solution, infer it from the
> right hand side type.

I prefer the former.  If it always creates a list by default, then the
programmer reading this code three years later knows exactly what the
type of tail is.  If it instead infers it from the sequence being
unpacked, then the programmer is forced to infer it as well, and it
won't necessarily be obvious or even consistent.

Cheers,
Ian

[toc] | [prev] | [next] | [standalone]


#17109

FromEelco <hoogendoorn.eelco@gmail.com>
Date2011-12-13 00:44 -0800
Message-ID<2ff41b2e-90e3-4fae-9951-67bb257ed6ed@m10g2000vbc.googlegroups.com>
In reply to#17099
On Dec 13, 2:41 am, Ian Kelly <ian.g.ke...@gmail.com> wrote:
> On Mon, Dec 12, 2011 at 4:40 PM, Eelco <hoogendoorn.ee...@gmail.com> wrote:
> >> For a linked list, no *target and no copying is needed:
>
> >> head, tail = llist
>
> > I have no idea what this means.
>
> Each node of a linked list consists of a data member and a "next"
> member, that holds the next node in the list.  The natural container
> for this and the way it is usually implemented in Python is a
> 2-element tuple.  For example:
>
> llist = ('one', ('two', ('three', ('four', None))))
>
> would be a linked list as typically constructed in Python, and it can
> be used in pretty much the same way that lists are used in Lisp.  The
> result of the operation:
>
> head, tail = llist
>
> is:
>
> head = 'one'
> tail = ('two', ('three', ('four', None)))
>
> and as Terry pointed out, no copying is required to do this.  I
> believe deque is also implemented as a doubly-linked list, which is
> probably why you keep referring to it as such, but that is an
> implementation detail.  When you say "linked list" in relation to
> Python, the preceding is what comes to mind.

'for i in llist' is not quite going to fly is it? Thats probably the
reason noone ever uses that construct; its not a proper sequence type.


> >> >>>> head, deque(tail) = somedeque
>
> >> > Is better in every way I can think of (readability, consistence,
> >> > performance) than:
> >> >>>> head, *tail = somedeque
> >> >>>> tail = deque(tail)
>
> >> But your suggestion is much worse in each way than
>
> >> head = somedeque.popleft()
>
> > No its not. First of all, its not semantically equivalent; popleft
> > mutates a collection type, and collection unpacking does not; it
> > creates a set of disjoint views of the collection's data. Whether its
> > worse in terms of readability is debatable, but in terms of the other
> > two stated metrics, your claim of it being any kind of worse is
> > objectively false.
>
> I definitely disagree on readability.  Skimming this thread as I have
> been, it took me a while to get that your proposed syntax would create
> a second deque sharing internal state with the original, rather than
> creating a simple copy of the deque, which is what it looks like it
> should do.

Thats a matter of reading up on the semantics of collection unpacking.
To be honest I dont even know what they are for lists, since I dont
use python 3. But whatever is the case, the important thing is that
the semantics should be uniform regardless of the type to be unpacked.

> Incidentally, while that change is not really central to your syntax
> proposal, I think it would be very messy.  For example, how do you
> propose keeping the length elements in sync?  Inserting an item in one
> may or may not affect the length of the other.  If I append an item to
> the end of one deque, should the other automatically be extended as
> well?  What if the tail node of the second deque occurs after the tail
> node of the deque being appended?  Does the appended element then get
> inserted into the middle of the second deque (I think it would have to
> be)?  If I insert an element into the longer (second) deque that just
> happens to be immediately after the tail of the shorter deque, does
> *that* cause the shorter deque to be automatically extended?  And
> likewise for operations at the head of the deque.
>
> None of these questions have obvious answers as to the "right" way to
> it, and for that reason I think this is probably best left to the user
> to implement a custom deque view class with whatever semantics they
> prefer.

Good point. Copy-on-write semantics could be used, but really one
should have several linked list types reflecting the underlying
implementations. I would like to have an immutable singly linked list
builtin of the standard functional type, which you can only unpack
from one end and renders these issues moot, plus a builtin doubly
linked list with copy-on-write or copy-on-unpacking semantics.

> > Furthermore, this brings us back again to the point I raised several
> > times before. Yes, obviously you can already DO these things, but NOT
> > within the same uniform collection unpacking syntactic construct.
> > Again, you have failed to point out what is wrong with supplying a
> > type constrain to python and let it do the right thing based on that;
> > to reiterate:
>
> > head, tail::deque = deque
>
> Python users generally follow the rule "explicit is better than
> implicit".  Setting a general constraint and letting the language "do
> the right thing" is a kind of black magic that feels off because it
> tends to break that rule.  But that's not to say that black magic
> never wins -- just look at super() and the MRO.

We are not talking black magic here; we are talking about an EXPLICIT
type constraint provided on the very same line.

> > If you dont like the extra characters you have to type; there is of
> > course such a thing as defaults. You can choose:
> > head, tail:: = deque; if the type constraint is omitted, we could make
> > tail a list by default, or my preferred solution, infer it from the
> > right hand side type.
>
> I prefer the former.  If it always creates a list by default, then the
> programmer reading this code three years later knows exactly what the
> type of tail is.  If it instead infers it from the sequence being
> unpacked, then the programmer is forced to infer it as well, and it
> won't necessarily be obvious or even consistent.

Well perhaps, but not always knowing the type of your objects at write-
time is inherent to weakly typed languages; this happens all the time.
Not knowing the type of the sequence to be unpacked is in a sense an
asset; I can use this construct in a function, and unpack any sequence
type in a manner appropriate for it. About the result of the unpacking
I will know just as much as about the input to it; that they are the
same type.

[toc] | [prev] | [next] | [standalone]


#17112

FromEelco <hoogendoorn.eelco@gmail.com>
Date2011-12-13 01:26 -0800
Message-ID<824d74b4-5495-447a-a9cb-addc22092a44@da3g2000vbb.googlegroups.com>
In reply to#17109
> > Python users generally follow the rule "explicit is better than
> > implicit".  Setting a general constraint and letting the language "do
> > the right thing" is a kind of black magic that feels off because it
> > tends to break that rule.  But that's not to say that black magic
> > never wins -- just look at super() and the MRO.
>
> We are not talking black magic here; we are talking about an EXPLICIT
> type constraint provided on the very same line.

To hammer that point home: would you say the following C# code
performs 'black magic'?

float x = 3;

After all, exactly the same thing is going on; C# will 'do the right
thing'; convert the integer literal 3 to a floating point value, based
on the type constraint placed on x.

[toc] | [prev] | [next] | [standalone]


#17159

FromIan Kelly <ian.g.kelly@gmail.com>
Date2011-12-13 11:15 -0700
Message-ID<mailman.3610.1323800154.27778.python-list@python.org>
In reply to#17109
On Tue, Dec 13, 2011 at 1:44 AM, Eelco <hoogendoorn.eelco@gmail.com> wrote:
> 'for i in llist' is not quite going to fly is it? Thats probably the
> reason noone ever uses that construct; its not a proper sequence type.

Not really a problem, because fortunately Python makes it super-easy
to create custom iterators.

def listiter(llist):
    while llist:
        head, llist = llist
        yield head

And you're done.  If you like, you could also wrap this up in a class
with all the sequence-related magic methods you want, although you
lose the simplicity of the literal syntax by doing so.  If this is
rarely used, it is more likely because custom containers are going to
be less efficient than built-ins, but if you want to do functional
programming and are not overly concerned with the speed of iteration,
this is not a bad way to do it.

> Good point. Copy-on-write semantics could be used, but really one
> should have several linked list types reflecting the underlying
> implementations. I would like to have an immutable singly linked list
> builtin of the standard functional type, which you can only unpack
> from one end and renders these issues moot, plus a builtin doubly
> linked list with copy-on-write or copy-on-unpacking semantics.

Copy-on-write could be implemented with any type.  You don't need a
doubly linked list for that.

> We are not talking black magic here; we are talking about an EXPLICIT
> type constraint provided on the very same line.

An explicit type constraint with very different semantics depending on
what particular type you specify and what particular type you're
unpacking from, as I had understood it before.  Now you seem to be
saying that it would always be a copy, but sharing state with
copy-on-write possible, which is a different situation.

> Well perhaps, but not always knowing the type of your objects at write-
> time is inherent to weakly typed languages; this happens all the time.
> Not knowing the type of the sequence to be unpacked is in a sense an
> asset; I can use this construct in a function, and unpack any sequence
> type in a manner appropriate for it. About the result of the unpacking
> I will know just as much as about the input to it; that they are the
> same type.

Just because the issue is inherent doesn't mean we should contribute
to it.  Knowing that an object is an arbitrary sequence is fine if all
you want to do is iterate and index it.  If you want to do anything
else, then it's important to know the type.  The copy-on-write
suggestion does make the type-matching approach a bit more attractive.
 On the other hand, it's also more fragile (what if the type being
unpacked can't be constructed from an iterable?  For example, a
database cursor), so that approach potentially needs additional
error-handling.

Anyway, the more I think about it, that concern is really more of an
issue for straight copying.  One of my pet peeves is that I prefer
list(x) for copying sequences rather than the more common x[::].  The
latter is fine if all I need is an immutable sequence of uncertain
type to iterate and index over -- but then why did I need to make a
copy?  Unpacking implies different use cases, though, and maybe a good
argument can be made for it to match type.

[toc] | [prev] | [next] | [standalone]


#17171

FromEelco <hoogendoorn.eelco@gmail.com>
Date2011-12-13 12:32 -0800
Message-ID<26b97534-1624-4afb-8869-f04afc800e22@4g2000yqu.googlegroups.com>
In reply to#17159
On Dec 13, 7:15 pm, Ian Kelly <ian.g.ke...@gmail.com> wrote:
> On Tue, Dec 13, 2011 at 1:44 AM, Eelco <hoogendoorn.ee...@gmail.com> wrote:
> > 'for i in llist' is not quite going to fly is it? Thats probably the
> > reason noone ever uses that construct; its not a proper sequence type.
>
> Not really a problem, because fortunately Python makes it super-easy
> to create custom iterators.
>
> def listiter(llist):
>     while llist:
>         head, llist = llist
>         yield head
>
> And you're done.  If you like, you could also wrap this up in a class
> with all the sequence-related magic methods you want, although you
> lose the simplicity of the literal syntax by doing so.  If this is
> rarely used, it is more likely because custom containers are going to
> be less efficient than built-ins, but if you want to do functional
> programming and are not overly concerned with the speed of iteration,
> this is not a bad way to do it.

Fair enough. Still, I dont readily see myself using the construct, and
I do feel myself longing for something like that to be included as a
builtin collection type. Im not sure why python is so stingy with the
collections it provides.

> > Good point. Copy-on-write semantics could be used, but really one
> > should have several linked list types reflecting the underlying
> > implementations. I would like to have an immutable singly linked list
> > builtin of the standard functional type, which you can only unpack
> > from one end and renders these issues moot, plus a builtin doubly
> > linked list with copy-on-write or copy-on-unpacking semantics.
>
> Copy-on-write could be implemented with any type.  You don't need a
> doubly linked list for that.

True

> > We are not talking black magic here; we are talking about an EXPLICIT
> > type constraint provided on the very same line.
>
> An explicit type constraint with very different semantics depending on
> what particular type you specify and what particular type you're
> unpacking from, as I had understood it before.  Now you seem to be
> saying that it would always be a copy, but sharing state with
> copy-on-write possible, which is a different situation.

Yes, I think consistent semantics over all sequence types is very
important. Although, returning a view for immutable collections like
tuples and 'functional lists' (for lack of a better term), and always
returning a copy for mutable container types (lists and doubly-linked-
lists / deques) is not so bad either, imo. Just one extra simple rule
that is clear and obvious enough.

> > Well perhaps, but not always knowing the type of your objects at write-
> > time is inherent to weakly typed languages; this happens all the time.
> > Not knowing the type of the sequence to be unpacked is in a sense an
> > asset; I can use this construct in a function, and unpack any sequence
> > type in a manner appropriate for it. About the result of the unpacking
> > I will know just as much as about the input to it; that they are the
> > same type.
>
> Just because the issue is inherent doesn't mean we should contribute
> to it.

How are we contributing to it? If we dont know the type of the RHS, we
dont know the type of the LHS either, but its not like we lost any
information. If we do know the type of the RHS, then we do know the
type of the LHS as well; its conserved.

> Anyway, the more I think about it, that concern is really more of an
> issue for straight copying.  One of my pet peeves is that I prefer
> list(x) for copying sequences rather than the more common x[::].  The
> latter is fine if all I need is an immutable sequence of uncertain
> type to iterate and index over -- but then why did I need to make a
> copy?  Unpacking implies different use cases, though, and maybe a good
> argument can be made for it to match type.

Thanks for the constructive feedback; something to think about.

[toc] | [prev] | [next] | [standalone]


#17097

FromSteven D'Aprano <steve+comp.lang.python@pearwood.info>
Date2011-12-13 01:34 +0000
Message-ID<4ee6abac$0$11091$c3e8da3@news.astraweb.com>
In reply to#17085
On Mon, 12 Dec 2011 16:58:33 -0500, Terry Reedy wrote:

> On 12/12/2011 12:12 PM, Eelco wrote:

>> Yes, we can cast the list to be whatever we want on the next line,
> 
> Convert. For the very few cases one wants to do this, it is quite
> adequate.

+1

with the small proviso that there's not much you can usefully do with a 
dict. If you convert to an ordered dict, the initial order is lost by the 
time you see it; if you want to allow duplicates, duplicates are likewise 
lost (or in a function call, an exception is raised).

In Python 4000, I'd like to see some way to pass arbitrary parameters to 
functions, including duplicates. That is, emulating the richness of 
command line argument handling in functions.

In Python 3.x, I'd like to see OrderedDict become a built-in, and then 
**kwargs can collect named arguments in an ordered dict without losing 
the order.



-- 
Steven

[toc] | [prev] | [next] | [standalone]


#17091

Fromalex23 <wuwei23@gmail.com>
Date2011-12-12 16:27 -0800
Message-ID<e8c21dc4-e971-410c-a9ec-55f4fb3d1db6@e8g2000prb.googlegroups.com>
In reply to#17065
On Dec 13, 3:12 am, Eelco <hoogendoorn.ee...@gmail.com> wrote:
> But to relate it to the topic of this thread: no, the syntax does not
> allow one to select the type of the resulting sequence. It always
> constructs a list.

So by this argument, _every_ function that returns a list should take
an optional argument to specify an alternative form of sequence.

What, exactly, is so onerous about coercing your list to _whatever_
type you want? You know, like everybody else has been.

What does this _gain_ you other than one less line of code?

[toc] | [prev] | [next] | [standalone]


#17106

FromEelco <hoogendoorn.eelco@gmail.com>
Date2011-12-13 00:30 -0800
Message-ID<d6a73ede-4ad9-4380-b9c1-5da30850e764@o1g2000vbe.googlegroups.com>
In reply to#17091
On Dec 13, 1:27 am, alex23 <wuwe...@gmail.com> wrote:
> On Dec 13, 3:12 am, Eelco <hoogendoorn.ee...@gmail.com> wrote:
>
> > But to relate it to the topic of this thread: no, the syntax does not
> > allow one to select the type of the resulting sequence. It always
> > constructs a list.
>
> So by this argument, _every_ function that returns a list should take
> an optional argument to specify an alternative form of sequence.
>
> What, exactly, is so onerous about coercing your list to _whatever_
> type you want? You know, like everybody else has been.
>
> What does this _gain_ you other than one less line of code?

1) Turning two lines of code into a single more readable one is
nothing to scoff at
2) After-the-fact conversion is O(n), getting the view you want right
away is O(1)

Not every function needs this flexibility; many specialized functions
could not care less. But collection unpacking is quite a general
thing, and for the record; slicing a tuple returns a tuple. Would you
rather have that return a list too?

[toc] | [prev] | [next] | [standalone]


#17162

FromCameron Simpson <cs@zip.com.au>
Date2011-12-14 06:11 +1100
Message-ID<mailman.3614.1323803498.27778.python-list@python.org>
In reply to#17106
On 13Dec2011 00:30, Eelco <hoogendoorn.eelco@gmail.com> wrote:
| On Dec 13, 1:27 am, alex23 <wuwe...@gmail.com> wrote:
| > On Dec 13, 3:12 am, Eelco <hoogendoorn.ee...@gmail.com> wrote:
| > > But to relate it to the topic of this thread: no, the syntax does not
| > > allow one to select the type of the resulting sequence. It always
| > > constructs a list.
| >
| > So by this argument, _every_ function that returns a list should take
| > an optional argument to specify an alternative form of sequence.
| >
| > What, exactly, is so onerous about coercing your list to _whatever_
| > type you want? You know, like everybody else has been.
| >
| > What does this _gain_ you other than one less line of code?
| 
| 1) Turning two lines of code into a single more readable one is
| nothing to scoff at
| 2) After-the-fact conversion is O(n), getting the view you want right
| away is O(1)

Regarding (2), it has already cost you O(n) to get there. So your O(1)
is a little ingenuous.
-- 
Cameron Simpson <cs@zip.com.au> DoD#743
http://www.cskk.ezoshosting.com/cs/

I'm a volunteer EMT-I on our local ambulance service; one of our Paramedics
calls squid style motorcycle accidents "ZoomSplats", as in "we had a good
ZoomSplat the other night". ;-)
        - Scott <traurig@rapnet.sanders.lockheed.com>

[toc] | [prev] | [next] | [standalone]


#17169

FromEelco <hoogendoorn.eelco@gmail.com>
Date2011-12-13 12:14 -0800
Message-ID<0c87865e-d8a2-41b6-91b4-09278ee2ffcf@cs7g2000vbb.googlegroups.com>
In reply to#17162
On Dec 13, 8:11 pm, Cameron Simpson <c...@zip.com.au> wrote:
> On 13Dec2011 00:30, Eelco <hoogendoorn.ee...@gmail.com> wrote:
> | On Dec 13, 1:27 am, alex23 <wuwe...@gmail.com> wrote:
> | > On Dec 13, 3:12 am, Eelco <hoogendoorn.ee...@gmail.com> wrote:
> | > > But to relate it to the topic of this thread: no, the syntax does not
> | > > allow one to select the type of the resulting sequence. It always
> | > > constructs a list.
> | >
> | > So by this argument, _every_ function that returns a list should take
> | > an optional argument to specify an alternative form of sequence.
> | >
> | > What, exactly, is so onerous about coercing your list to _whatever_
> | > type you want? You know, like everybody else has been.
> | >
> | > What does this _gain_ you other than one less line of code?
> |
> | 1) Turning two lines of code into a single more readable one is
> | nothing to scoff at
> | 2) After-the-fact conversion is O(n), getting the view you want right
> | away is O(1)
>
> Regarding (2), it has already cost you O(n) to get there. So your O(1)
> is a little ingenuous.

Well, yes, but if one takes a given sequence as input (at least O(n)
complexity to obtain it in the first place, indeed), and then wants
to, say, recursively unwind it, the cost of the total operation is
O(n) versus O(n^2)

And besides, O(n) < 2*O(n); perhaps of lesser concern than different
orders, but still.

[toc] | [prev] | [standalone]


Back to top | Article view | comp.lang.python


csiph-web