<andr...@r3dsolutions.com> wrote:
>> Q: What about other mutable objects like sets or dicts?
>> A: No, the elements are never copied.
> They aren't list multiplication compatible in any event! It's a total
> nonsense objection.
> If these are inconsistent in my idea -- OBVIOUSLY -- they are inconsistent
> in Python's present implementation. You can't even reference duplicate them
> NOW.
>>>> { 1:'a', 2:'b', 3:'c' } * 2
> Traceback (most recent call last):
> File "<stdin>", line 1, in <module>
> TypeError: unsupported operand type(s) for *: 'dict' and 'int'
The objection is not nonsense; you've merely misconstrued it. If
[[1,2,3]] * 4 is expected to create a mutable matrix of 1s, 2s, and
3s, then one would expect [[{}]] * 4 to create a mutable matrix of
dicts. If the dicts are not copied, then this fails for the same
reason
>> Q: How about if I use delegation to proxy a list?
>> A: Oh no, they definitely won't be copied.
> Give an example usage of why someone would want to do this. Then we can
> discuss it.
Seriously? Read a book on design patterns. You might start at SO:
>> Losing consistency in favour of saving a few characters for something as
>> uncommon as list multiplication is a poor tradeoff. That's why this
>> proposal has been rejected again and again and again every time it has
>> been suggested.
> Please link to the objection being proposed to the developers, and their
> reasoning for rejecting it.
> I think you are exaggerating.
>From Google:
Note that in two out of these four cases, the reporter was trying to
multiply lists of dicts, not just lists of lists.
> Besides, 2D arrays are *not* rare and people *have* to copy internals of
> them very often.
> The copy speed will be the same or *faster*, and the typing less -- and the
> psychological mistakes *less*, the elegance more.
List multiplication is not potentially useful for copying 2D lists,
only for initializing them. For copying an existing nested list,
you're still stuck with either copy.deepcopy() or a list
comprehension.
> It's hardly going to confuse anyone to say that lists are copied with list
> multiplication, but the elements are not.
> Every time someone passes a list to a function, they *know* that the list is
> passed by value -- and the elements are passed by reference. People in
> Python are USED to lists being "the" way to weird behavior that other
> languages don't do.
Incorrect. Python uses what is commonly known as call-by-object, not
call-by-value or call-by-reference. Passing the list by value would
imply that the list is copied, and that appends or removes to the list
inside the function would not affect the original list. This is not
what Python does; the list inside the function and the list passed in
are the same list. At the same time, the function does not have
access to the original reference to the list and cannot reassign it by
reassigning its own reference, so it is not call-by-reference
semantics either.
> On Tue, Nov 6, 2012 at 3:41 PM, Andrew Robinson
> <andr...@r3dsolutions.com> wrote:
>>> Q: What about other mutable objects like sets or dicts?
>>> A: No, the elements are never copied.
>> They aren't list multiplication compatible in any event! It's a total
>> nonsense objection.
>> If these are inconsistent in my idea -- OBVIOUSLY -- they are inconsistent
>> in Python's present implementation. You can't even reference duplicate them
>> NOW.
>>>>> { 1:'a', 2:'b', 3:'c' } * 2
>> Traceback (most recent call last):
>> File "<stdin>", line 1, in <module>
>> TypeError: unsupported operand type(s) for *: 'dict' and 'int'
> The objection is not nonsense; you've merely misconstrued it. If
> [[1,2,3]] * 4 is expected to create a mutable matrix of 1s, 2s, and
> 3s, then one would expect [[{}]] * 4 to create a mutable matrix of
> dicts. If the dicts are not copied, then this fails for the same
> reason
>>> Q: How about if I use delegation to proxy a list?
>>> A: Oh no, they definitely won't be copied.
>> Give an example usage of why someone would want to do this. Then we can
>> discuss it.
> Seriously? Read a book on design patterns. You might start at SO:
>>> Losing consistency in favour of saving a few characters for something as
>>> uncommon as list multiplication is a poor tradeoff. That's why this
>>> proposal has been rejected again and again and again every time it has
>>> been suggested.
>> Please link to the objection being proposed to the developers, and their
>> reasoning for rejecting it.
>> I think you are exaggerating.
> Note that in two out of these four cases, the reporter was trying to
> multiply lists of dicts, not just lists of lists.
>> Besides, 2D arrays are *not* rare and people *have* to copy internals of
>> them very often.
>> The copy speed will be the same or *faster*, and the typing less -- and the
>> psychological mistakes *less*, the elegance more.
> List multiplication is not potentially useful for copying 2D lists,
> only for initializing them. For copying an existing nested list,
> you're still stuck with either copy.deepcopy() or a list
> comprehension.
>> It's hardly going to confuse anyone to say that lists are copied with list
>> multiplication, but the elements are not.
>> Every time someone passes a list to a function, they *know* that the list is
>> passed by value -- and the elements are passed by reference. People in
>> Python are USED to lists being "the" way to weird behavior that other
>> languages don't do.
> Incorrect. Python uses what is commonly known as call-by-object, not
> call-by-value or call-by-reference. Passing the list by value would
> imply that the list is copied, and that appends or removes to the list
> inside the function would not affect the original list. This is not
> what Python does; the list inside the function and the list passed in
> are the same list. At the same time, the function does not have
> access to the original reference to the list and cannot reassign it by
> reassigning its own reference, so it is not call-by-reference
> semantics either.
On Tue, 06 Nov 2012 14:41:24 -0800, Andrew Robinson wrote:
> Yes. But this isn't going to cost any more time than figuring out
> whether or not the list multiplication is going to cause quirks, itself.
> Human psychology *tends* (it's a FAQ!) to automatically assume the
> purpose of the list multiplication is to pre-allocate memory for the
> equivalent (using lists) of a multi-dimensional array. Note the OP even
> said "4d array".
I'm not entirely sure what your point is here. The OP screwed up -- he didn't generate a 4-dimensional array. He generated a 2-dimensional array. If his intuition about the number of dimensions is so poor, why should his intuition about list multiplication be treated as sacrosanct?
As they say, the only truly intuitive interface is the nipple. There are many places where people's intuition about programming fail. And many places where Fred's intuition is the opposite of Barney's intuition.
Even more exciting, there are places where people's intuition is *inconsistent*, where they expect a line of code to behave differently depending on their intention, rather than on the code. And intuition is often sub-optimal: e.g. isn't it intuitively obvious that "42" + 1 should give 43? (Unless it is intuitively obvious that it should give 421.)
So while I prefer intuitively obvious behaviour where possible, it is not the holy grail, and I am quite happy to give it up.
> The OP's original construction was simple, elegant, easy to read and
> very commonly done by newbies learning the language because it's
> *intuitive*. His second try was still intuitive, but less easy to read,
> and not as elegant.
Yes. And list multiplication is one of those areas where intuition is suboptimal -- it produces a worse outcome overall, even if one minor use-
case gets a better outcome.
I'm not disputing that [[0]*n]*m is intuitively obvious and easy. I'm disputing that this matters. Python would be worse off if list multiplication behaved intuitively.
An analogy: the intuitively obvious thing to do with a screw is to bang it in with a hammer. It's long, thin, has a point at the end, and a flat head that just screams "hit me". But if you do the intuitive thing, your carpentry will be *much worse* than the alternatives -- a hammered in screw holds much less strongly than either a nail or a screwed in screw. The surface area available for gripping is about 2% compared to a nail and about 0.01% compared to a screw used correctly.
Having list multiplication copy has consequences beyond 2D arrays. Those consequences make the intuitive behaviour you are requesting a negative rather than a positive. If that means that newbie programmers have to learn not to hammer screws in, so be it. It might be harder, slower, and less elegant to drill a pilot hole and then screw the screw in, but the overall result is better.
>> * Consistency of semantics is better than a plethora of special
>> cases. Python has a very simple and useful rule: objects should not
>> be copied unless explicitly requested to be copied. This is much
>> better than having to remember whether this operation or that
>> operation makes a copy. The answer is consistent:
> Bull. Even in the last thread I noted the range() object produces
> special cases.
> >>> range(0,5)[1]
> 1
> >>> range(0,5)[1:3]
> range(1, 3)
What's the special case here? What do you think is copied?
You take a slice of a tuple, you get a new tuple.
You take a slice of a list, you get a new list.
You take a slice of a range object, you get a new range object.
I'm honestly not getting what you think is inconsistent about this.
> The principle involved is that it gives you what you *usually* want;
Who is the "you" that decides what "you" usually want? And how do they know what is "usual"?
Two-dimensional arrays in Python using lists are quite rare. Anyone who is doing serious numeric work where they need 2D arrays is using numpy, not lists. There are millions of people using Python, so it's hardly surprising that once or twice a year some newbie trips over this. But it's not something that people tend to trip over again and again and again, like C's "assignment is an expression" misfeature.
> I read some of the documentation on why Python 3 chose to implement it
> this way.
What documentation is this? Because this is a design decision that goes all the way back to at least Python 1.5:
[steve@ando ~]$ python1.5
Python 1.5.2 (#1, Aug 27 2012, 09:09:18) [GCC 4.1.2 20080704 (Red Hat 4.1.2-52)] on linux2
Copyright 1991-1995 Stichting Mathematisch Centrum, Amsterdam
So I expect the design decision for Python 3 was "we made the right decision before, there's no need to change it".
>> (pardon me for belabouring the point here)
>> Q: Does [0]*10 make ten copies of the integer object? A: No, list
>> multiplication doesn't make copies of elements.
> Neither would my idea for the vast majority of things on your first
> list.
Um, yes? The point is that "vast majority" is not "everything". Hence, your suggested behaviour is inconsistent.
> Q: What about [[]]*10?
> A: No, the elements are never copied.
> YES! For the obvious reason that such a construction is making mutable
> lists that the user wants to populate later. If they *didn't* want to
> populate them later, they ought to have used tuples -- which take less
> overhead. Who even does this thing you are suggesting?!
Who knows? Who cares? Nobody does:
n -= n
instead of just n=0, but that doesn't mean that we should give it some sort of special meaning different from n -= m. If it turns out that the definition of list multiplication is such that NOBODY, EVER, uses [[]]*n, that is *still* not a good reason for special-casing it. All it means is that this will be a less-obscure example of the billions of things which can be done in Python but nobody wants to.
You have quoted from the Zen of Python a few times in this post. Perhaps you missed one of the most critical ones?
Special cases aren't special enough to break the rules.
There are perfectly good ways to generate a 2D array out of lists, and even better reasons not to use lists for that in the first place. (Numpy arrays are much better suited for serious work.)
>> Q: What about other mutable objects like sets or dicts? A: No, the
>> elements are never copied.
> They aren't list multiplication compatible in any event! It's a total
> nonsense objection.
I'm afraid you've just lost an awful lot of credibility there.
py> x = [{}]*5
py> x
[{}, {}, {}, {}, {}]
py> x[0]['key'] = 1
py> x
[{'key': 1}, {'key': 1}, {'key': 1}, {'key': 1}, {'key': 1}]
And similarly for any other mutable object.
If you don't understand that lists can contain other mutable objects apart from lists, then you really shouldn't be discussing this issue.
>> Your proposal throws away consistency for a trivial benefit on a rare
>> use- case, and replaces it with a bunch of special cases:
> RARE!!!! You are NUTS!!!!
Yes, rare. I base that on about 15 years of Python coding and many thousands (tens of thousands?) of hours on Python forums like this one. What's your opinion based on?
List multiplication is rare enough, but when it is used, it is usually used to generate a 1D array like this:
values = [None]*n # or 0 is another popular starting value
Using it twice to generate a 2D array is even rarer.
>> Q: How about if I use delegation to proxy a list? A: Oh no, they
>> definitely won't be copied.
> Give an example usage of why someone would want to do this. Then we can
> discuss it.
Proxying objects is hardly a rare scenario. Delegation is less common since you can subclass built-ins, but it is still used. It is a standard design pattern.
>> Losing consistency in favour of saving a few characters for something
>> as uncommon as list multiplication is a poor tradeoff. That's why this
>> proposal has been rejected again and again and again every time it has
>> been suggested.
> Please link to the objection being proposed to the developers, and their
> reasoning for rejecting it.
> I think you are exaggerating.
Python is a twenty year old language. Do you really think this is the first time somebody has noticed it?
It's hard to search for discussions on the dev list, because the obvious search terms bring up many false positives. But here are a couple of bug reports closed as "won't fix":
I suspect it is long past time for a PEP so this can be rejected once and for all.
>> List multiplication [x]*n is conceptually equivalent to: <snip>
>> This is nice and simple and efficient.
> No it isn't efficient. It's *slow* when done as in your example.
Well of course it is slow*er* when you move it from low-level C to high level Python, but it is still fast.
>> Copying other objects is slow and inefficient. Keeping list
>> multiplication consistent, and fast, is MUCH more important than making
>> it work as expected for the rare case of 2D arrays:
> I don't think so -- again, look at range(); it was made to work
> inconsistent for a "common" case.
You mentioned range before, but it isn't clear to me what you think is inconsistent about it.
> Besides, 2D arrays are *not* rare and people *have* to copy internals of
> them very often.
So you say.
> The copy speed will be the same or *faster*, and the typing less -- and
> the psychological mistakes *less*, the elegance more.
You think that it is *faster* to copy a list than to make
...
> (Obviously, I could have just hardcoded the initialization, but I'm too lazy to type all that out ;))
> The behaviour I encountered seems a little contradictory to me. [None] * 4 creates four distinct elements in a single array while [[None] * 4] * 4 creates one distinct array of four distinct elements, with three references to it:
> >>> a = [None] * 4
> >>> a[0] = 'a'
> >>> a
> ['a', None, None, None]
> >>> m = [[None] * 4] * 4
> >>> m[0][0] = 'm'
> >>> m
> Is this expected behaviour and if so, why? In my mind either result makes sense, but the inconsistency is what throws me off.
m=[[None] * 2] * 3
is the same as
m=[[None]*2, [None]*2, [None]*2]
until one starts doing things like
m[0][0] = 'm'
So dont do it!
And to get python to help you by saying the same that I am saying do
m=((None) * 2) * 3
(well almost... its a bit more messy in practice)
m=(((None,) * 2),)*3
After that try assigning to m[0][0] and python will kindly say NO!
tl;dr version:
reference semantics is ok
assignment is ok (well up to a point)
assignment + reference semantics is not
On Wed, 07 Nov 2012 00:23:44 +0000, MRAB wrote:
>> Incorrect. Python uses what is commonly known as call-by-object, not
>> call-by-value or call-by-reference. Passing the list by value would
>> imply that the list is copied, and that appends or removes to the list
>> inside the function would not affect the original list. This is not
>> what Python does; the list inside the function and the list passed in
>> are the same list. At the same time, the function does not have access
>> to the original reference to the list and cannot reassign it by
>> reassigning its own reference, so it is not call-by-reference semantics
>> either.
> I prefer the term "reference semantics".
Oh good, because what the world needs is yet another name for the same behaviour.
- call by sharing
- call by object sharing
- call by object reference
- call by object
- call by value, where "values" are references (according to the Java community)
- call by reference, where "references" refer to objects, not variables
(according to the Ruby community)
- reference semantics
> >> Incorrect. Python uses what is commonly known as call-by-object, not
> >> call-by-value or call-by-reference. Passing the list by value would
> >> imply that the list is copied, and that appends or removes to the list
> >> inside the function would not affect the original list. This is not
> >> what Python does; the list inside the function and the list passed in
> >> are the same list. At the same time, the function does not have access
> >> to the original reference to the list and cannot reassign it by
> >> reassigning its own reference, so it is not call-by-reference semantics
> >> either.
> > I prefer the term "reference semantics".
> Oh good, because what the world needs is yet another name for the same > behaviour.
> - call by sharing
> - call by object sharing
> - call by object reference
> - call by object
> - call by value, where "values" are references > (according to the Java community)
> - call by reference, where "references" refer to objects, not variables
> (according to the Ruby community)
> - reference semantics
Roy Smith wrote:
> Call by social network? The called function likes the object.
> Depending on how it feels, it can also comment on some of the object's > attributes.
And then finds that it has inadvertently shared all its
private data with other functions accessing the object.
On 2012-11-06, at 5:55 PM, Steven D'Aprano <steve+comp.lang.pyt...@pearwood.info> wrote:
> I'm not entirely sure what your point is here. The OP screwed up -- he > didn't generate a 4-dimensional array. He generated a 2-dimensional > array. If his intuition about the number of dimensions is so poor, why > should his intuition about list multiplication be treated as sacrosanct?
Yep, I may have mis-worded the explanation a bit (although I *did* express that it was a 4D matrix in the OP). I was using a 2D list to represent a 4D matrix in order to easily iterate over 90 degree rotations with zip(*matrix[::-1]). It wasn't for production code (otherwise I *would* be using numpy), it was for an online programming challenge in which external libs are not supported.
> As they say, the only truly intuitive interface is the nipple. There are > many places where people's intuition about programming fail. And many > places where Fred's intuition is the opposite of Barney's intuition.
I couldn't agree more with this. My question was *not* based on what I perceive to be intuitive (although most of this thread has now seemed to devolve into that and become more of a philosophical debate), but was based on what I thought may have been inconsistent behaviour (which was quickly cleared up with None being immutable and causing it to *seem* that the behaviour was inconsistent to the forgetful mind). As you touch on here, "intuition" is entirely subjective. If you're coming from a C/C++ background, I'd think that your intuition would be that everything's passed by value unless explicitly stated. Someone coming from another background (Lua perhaps?) would likely have entirely different intuition.
> So while I prefer intuitively obvious behaviour where possible, it is not > the holy grail, and I am quite happy to give it up.
I fail to see where there has been any giving up on intuitiveness in the context of this particular topic. In my mind, intuitiveness is generally born of repetitiveness and consistency. As everything in Python is a reference, it would seem to me to be inconsistent to treat expressions such as [[obj]*4]*4 un-semantically (Pythonically speaking) and making it *less* intuitive. I agree that Python would definitely be worse off.
Steven D'Aprano writes:
> On Wed, 07 Nov 2012 00:23:44 +0000, MRAB wrote:
> > I prefer the term "reference semantics".
> Oh good, because what the world needs is yet another name for the
> same behaviour.
> - call by sharing
> - call by object sharing
> - call by object reference
> - call by object
> - call by value, where "values" are references > (according to the Java community)
> - call by reference, where "references" refer to objects, not variables
> (according to the Ruby community)
> - reference semantics
There's a call-by-* versus pass-by-* distinction, where the call-by-*
would be rather different from any of the above:
- call-by-value is what most languages now use: argument expressions
are reduced to values before they are passed to the function /
procedure / method / whatever.
- call-by-name was something Algol 60 had by default: something like
evaluating the argument expression every time its value is needed
- call-by-need: argument expression is reduced to a value the first
time its value is needed (if ever)
- call-by-lazy (increasingly silly terminology, and I don't quite have
an idea what it means in contrast to call-by-need)
The modern confusions would then be mostly over the pass-by-* family,
invariably using call-by-value in the above sense. The terminology for
these tends to produce more heat than light, but I think the relevant
distinctions are mostly just these:
- can one modify the argument effectively [Python: yes]
- can one modify the parameter with abandon [Python: don't]
- can one swap [Python: no]
- possibly: is it expensive to pass large objects? [Python: no]
The actual rule in Scheme, Java, and Python is the same simple and
sane rule: what are passed are values (argument expressions are fully
evaluated before the actual call takes place), parameter passing does
not involve any (observable) copying, and the arguments are bound to
fresh variables (no aliasing of variables).
Different communities use different words. Sometimes they use the same
words about different things. Resulting in more heat than light :(
(I'd have a few more things in the something-else department, but this
is already much longer than I thought. Ends.)
> On 7 November 2012 11:11, Oscar Benjamin <oscar.j.benja...@gmail.com> wrote:
>> A more modest addition for the limited case described in this thread could
>> be to use exponentiation:
>> >>> [0] ** (2, 3)
>> [[0, 0, 0], [0, 0, 0]]
> Hold on: why not just use multiplication?
>>>> [0] * (2, 3)
> is an error now, and it makes total sense. Additionally, it's not breaking
> the "no copy -- _ever_" rule because none of the lists existed before. The
> values inside the list would be by reference, as before, so lst * (x,) would
> be the same as lst * x if x is an integer.
The problem is that this operation is asymmetric. Currently int/list
multiplication is commutative so that:
['a', 'b'] * 2 == 2 * ['a', 'b']
If you use this kind of multiplication what happens to the other
cases? e.g. what do you give for:
and so on. Although Python does not guarantee commutativity of
multiplication in general I think that since for lists it has always
been commutative it would be bad to change that.
Exponentiation is expected to be asymmetric and is currently unused so
there is no ambiguity. The problem is if someone has already
subclassed list and added an exponentiation method.
After this post the only credibility you have left (with me, anyway) is that you seem to be willing to learn. So learn the way Python works before you try to reimplement it.
Oscar Benjamin wrote:
> On Nov 7, 2012 5:41 AM, "Gregory Ewing" <greg.ew...@canterbury.ac.nz > <mailto:greg.ew...@canterbury.ac.nz>> wrote:
> > If anything is to be done in this area, it would be better
> > as an extension of list comprehensions, e.g.
> > [[None times 5] times 10]
> > which would be equivalent to
> > [[None for _i in xrange(5)] for _j in xrange(10)]
> I think you're right that the meaning of list-int multiplication > can't/shouldn't be changed if this way.
> A multidimensional list comprehension would be useful even for people > who are using numpy as it's common to use a list comprehension to > initialise a numpy array.
> A more modest addition for the limited case described in this thread > could be to use exponentiation:
> Roy Smith wrote:
> > Call by social network? The called function likes the object.
> > Depending on how it feels, it can also comment on some of the object's
> > attributes.
> And then finds that it has inadvertently shared all its
> private data with other functions accessing the object.
And this is where Dihedral 8888 (or whatever the bot is called)
tells you that Python has no "private" variables. :)
~Ramit
This email is confidential and subject to important disclaimers and
conditions including on offers for the purchase or sale of
securities, accuracy and completeness of information, viruses,
confidentiality, legal privilege, and legal entity disclaimers,
available at http://www.jpmorgan.com/pages/disclosures/email.
>>> Incorrect. Python uses what is commonly known as call-by-object, not
>>> call-by-value or call-by-reference. Passing the list by value would
>>> imply that the list is copied, and that appends or removes to the list
>>> inside the function would not affect the original list. This is not
>>> what Python does; the list inside the function and the list passed in
>>> are the same list. At the same time, the function does not have access
>>> to the original reference to the list and cannot reassign it by
>>> reassigning its own reference, so it is not call-by-reference semantics
>>> either.
>> I prefer the term "reference semantics".
> Oh good, because what the world needs is yet another name for the same
> behaviour.
> - call by sharing
> - call by object sharing
> - call by object reference
> - call by object
> - call by value, where "values" are references
> (according to the Java community)
> - call by reference, where "references" refer to objects, not variables
> (according to the Ruby community)
> - reference semantics
<andr...@r3dsolutions.com> wrote:
> Interesting, you avoided the main point "lists are copied with list
> multiplication".
It seems that each post is longer than the last. If we each responded
to every point made, this thread would fill a book.
Anyway, your point was to suggest that people would not be confused by
having list multiplication copy lists but not other objects, because
passing lists into functions as parameters works in basically the same
way. Except that it does not work the same way, because when lists
are passed into functions, they are not copied at all. Nor are are
any of their contents copied, lists or not. So actually I did address
this point with the "call-by-object" tangent; I just did not
explicitly link it back to your thesis.
> But, in any event:
> Pass by value (not call by value) is a term stretching back 30 years; eg:
> when I learned the meaning of the words. Rewording it as "Call by value" is
> something that happened later, and the nuance is lost on those without a
> very wide programming knowledge *and* age.
Potayto, potahto. The distinction that you're describing is between
"strict" versus "non-strict" evaluation strategies. Hinging the
distinction on the non-descriptive words "call" and "pass" is lazy
terminology that should never have been introduced in the first place.
> In any event:
> All objects in Python are based on pointers; all parameters passed to
> functions, etc, are *copies* of those pointers; (by pointer value).
No, all parameters passed to functions are *objects*. Python itself
has no concept of pointers. What you describe is true as an
implementation detail for CPython but not necessarily true for other
implementations, and not true at all for an abstract
(implementation-independent) view of the language.
> I made the distinction between contents of the list and the list object
> itself for that reason; I gave an explicit correction to the pass by "value"
> generalization by saying: ("the elements are passed by reference").
The elements are not passed anywhere. Only the list object is passed
to the function, which is completely agnostic of the fact that the
list object happens to contain other objects.
On Wed, 07 Nov 2012 17:17:02 +0000, MRAB wrote:
> The disadvantage of calling it "call by ..." is that it suggests that
> you're just talking about calling functions.
*shrug*
There are already two synonyms for this, "call by ..." and "pass by ...". They are old, venerable terms dating back to Algol and possibly even older. All the way back to Fortran perhaps?
> What about binding in general, eg "x = y"? Does it make sense to still
> call it "call by ..."?
Sure, why not? The person who prepares beef tartare or sushimi is still called the cook.
> On 7 November 2012 14:00, Oscar Benjamin <oscar.j.benja...@gmail.com> wrote:
>> On 7 November 2012 13:39, Joshua Landau <joshua.landau...@gmail.com>
>> wrote:
>> > On 7 November 2012 11:11, Oscar Benjamin <oscar.j.benja...@gmail.com>
>> > wrote:
>> >> A more modest addition for the limited case described in this thread
>> >> could
>> >> be to use exponentiation:
>> Exponentiation is expected to be asymmetric and is currently unused so
>> there is no ambiguity. The problem is if someone has already
>> subclassed list and added an exponentiation method.
> How is that a problem? They just wont get the functionality.
This is absolutely contrived but:
Library A defines a subclass of list that adds an exponentiation
operator thinking that it's okay to still use these objects as lists.
Library B has an API that expects a list and tries to use the list
copy-exponentiation on its input. A user passes a list type object
from library A into library B and hopefully gets an error but possibly
gets a subtle bug that is hard to track down.
It doesn't sound plausible to me but at least in principle there is a
backward compatibility problem.
> That said, losing:
> [0] * (2, 3) == [0] * [2, 3]
> would mean losing duck-typing in general. *Thus*, I fully agree with your
> choice of exponentiation.
Also there's no reason why tuples couldn't have the same
exponentiation operator (although for them it would be no different
from repeated multiplication).
<andr...@r3dsolutions.com> wrote:
> Draw up some use cases for the multiplication operator (I'm calling on your
> experience, let's not trust mine, right?); What are all the Typical ways
> people *Do* to use it now?
> If those use cases do not *primarily* center around *wanting* an effect
> explicitly caused by reference duplication -- then it may be better to
> abolish list multiplication all together; and rather, improve the list
> comprehensions to overcome the memory, clarity, and speed pitfalls in the
> most common case of initializing a list.
Why? Just to get rid of an FAQ?
Here's one of the more interesting uses from my own code:
values = zip(samples, times * num_groups)
if len(values) < len(times) * num_groups:
# raise an error
Converting that multiplication to a generator expression would look like this:
values = zip(samples, (t for _ in range(num_groups) for t in times))
That's not particularly hairy, but I do assert that it is
substantially less readable, and more so because it loses the symmetry
with the following if condition.
The recipes in the itertools docs also include this example, which
notably depends on the list containing multiple references to the same
iterator:
def grouper(n, iterable, fillvalue=None):
"Collect data into fixed-length chunks or blocks"
# grouper(3, 'ABCDEFG', 'x') --> ABC DEF Gxx
args = [iter(iterable)] * n
return izip_longest(fillvalue=fillvalue, *args)
Replacing the list multiplication in that function with a list
comprehension would be awkward, as the obvious replacement of
[iter(iterable) for _ in range(n)] would produce different results.
> For example, in initialization use cases; often the variable of a for loop
> isn't needed and all the initializers have parameters which only need to be
> evaluated *once* (no side effects).
> Hence, there is an opportunity for speed and memory gains,while maintaining
> clarity and *consistency*.
> Some ideas of use cases:
> [ (0) in xrange(10) ] # The function to create a tuple cache's the
> parameter '0', makes 10 (0)'s
> [ dict.__new__(dict) in xrange(10) ] # dict.__new__, The dict parameter is
> cached -- makes 10 dicts.
> [ lambda x:(0) in xrange(10) ] # lambda caches (0), returns a *reference* to
> it multiple times.
How exactly do you propose to indicate to the compiler which parts of
the expressions are meant to be cached, and which are not?
>>> Bull. Even in the last thread I noted the range() object produces
>>> special cases.
>>> >>> range(0,5)[1]
>>> 1
>>> >>> range(0,5)[1:3]
>>> range(1, 3)
>> What's the special case here? What do you think is copied?
>> You take a slice of a range object, you get a new range object.
> You were'nt paying attention, OCCASIONALLY, get an integer, or a list.
>>>> range(3)[2]
> 2
> LOOOOK! That's not a range object, that's an integer. Use Python 3.2 and
> try it.
Of course you got an integer. You took an index of the range object,
not a slice. The rule is that taking an index of a sequence returns
an element; taking a slice of a sequence returns a sub-sequence. You
still have not shown any inconsistency here.
> Game programmers routinely use 2D lists to represent the screen layout;
> For example, they might use 'b' to represent a brick tile, and 'w' to
> represent a water tile.
In many cases it may be simpler to use a plain list of strings:
screen = [
"sssss",
"ssbss",
"sbbbs",
"bbbbb",
]
> py> x = [{}]*5
> py> x
> [{}, {}, {}, {}, {}]
> No, I showed what happed when you do {}*3;
> That *DOESN'T* work; You aren't multiplying the dictionary, you are
> multiplying the LIST of dictionaries. Very different things.
> You were complaining that my method doesn't multiply them -- well, gee --
> either mine DOES or python DOESN'T. Double standards are *crap*.
No, he wasn't. He was talking about multiplying lists of dicts, and
whether the dicts are then copied or not, just like every other Q&A
item in that dialogue was concerning whether item X in a list should
expect to be copied when the containing list is multiplied.
You are the only one talking about applying the multiplication
operator to dicts.
> Huh?
> I'm not yelling any more than you are. Are ???YOU??? yelling?
Perhaps you're not aware that on the Internet, TYPING IN ALL CAPS is
commonly construed as SHOUTING.
Andrew, it appears that your posts are being eaten or rejected by my ISP's news server, because they aren't showing up for me. Possibly a side-
effect of your dates being in the distant past? So if you have replied to any of my posts, I haven't seen them.
In any case, I wanted to ask a question:
On Wed, 07 Nov 2012 14:01:19 -0700, Ian Kelly wrote:
> On Wed, Nov 7, 2012 at 12:51 PM, Andrew Robinson
> <andr...@r3dsolutions.com> wrote:
[...]
>> But, in any event:
>> Pass by value (not call by value) is a term stretching back 30 years;
>> eg: when I learned the meaning of the words. Rewording it as "Call by
>> value" is something that happened later, and the nuance is lost on
>> those without a very wide programming knowledge *and* age.
Every now and again I come across somebody who tries to distinguish between "call by foo" and "pass by foo", but nobody has been able to explain the difference (if any) to me. When you CALL a function, you PASS values to it. Hence the two terms are effectively synonyms, and both refer to the evaluation strategy when binding arguments to parameters.
If you believe that is incorrect, can you point me to something explaining the difference?
> On Wed, Nov 7, 2012 at 12:51 PM, Andrew Robinson
> <andr...@r3dsolutions.com> wrote:
>> Interesting, you avoided the main point "lists are copied with list
>> multiplication".
> It seems that each post is longer than the last. If we each responded
> to every point made, this thread would fill a book.
It already is :)
> Anyway, your point was to suggest that people would not be confused by
> having list multiplication copy lists but not other objects, because
> passing lists into functions as parameters works in basically the same
> way.
Not quite; Although I wasn't clear; The variable passed in is by *value* in contradistinction to the list which is by reference. Python does NOT always default copy by reference *when it could*; that's the point.
Hence the programmer has to remember in foo( x,y ), the names x and y when assigned to -- *DONT* affect the variables from which they came.
But any object internals do affect the objects everywhere.
A single exception exists; My thesis is for a single exception as well -- I think Python allows that kind of thinking.
> So actually I did address
> this point with the "call-by-object" tangent; I just did not
> explicitly link it back to your thesis.
My apology for not proof reading my statements for clarity. It was definitely time for a nap back then.
> Potayto, potahto. The distinction that you're describing is between > "strict" versus "non-strict" evaluation strategies. Hinging the > distinction on the non-descriptive words "call" and "pass" is lazy > terminology that should never have been introduced in the first place.
I would do it again. Other's have already begun to discuss terminology with you -- I won't double team you.
<steve+comp.lang.pyt...@pearwood.info> wrote:
> Andrew, it appears that your posts are being eaten or rejected by my
> ISP's news server, because they aren't showing up for me. Possibly a side-
> effect of your dates being in the distant past? So if you have replied to
> any of my posts, I haven't seen them.
> In any case, I wanted to ask a question:
> On Wed, 07 Nov 2012 14:01:19 -0700, Ian Kelly wrote:
>> On Wed, Nov 7, 2012 at 12:51 PM, Andrew Robinson
>> <andr...@r3dsolutions.com> wrote:
> [...]
>>> But, in any event:
>>> Pass by value (not call by value) is a term stretching back 30 years;
>>> eg: when I learned the meaning of the words. Rewording it as "Call by
>>> value" is something that happened later, and the nuance is lost on
>>> those without a very wide programming knowledge *and* age.
> Every now and again I come across somebody who tries to distinguish
> between "call by foo" and "pass by foo", but nobody has been able to
> explain the difference (if any) to me. When you CALL a function, you PASS
> values to it. Hence the two terms are effectively synonyms, and both
> refer to the evaluation strategy when binding arguments to parameters.
> If you believe that is incorrect, can you point me to something
> explaining the difference?
Did you also miss MRAB's post above? It made sense to me.
> On 7 November 2012 22:16, Joshua Landau<joshua.landau...@gmail.com> wrote:
>> That said, losing:
>> [0] * (2, 3) == [0] * [2, 3]
>> would mean losing duck-typing in general.
There are precedents for this kind of thing; the
string % operator treats tuples specially, for
example.
I don't think it's all that bad if you regard
the tuple as effectively part of the syntax.