Velocity Reviews - Computer Hardware Reviews

Velocity Reviews > Newsgroups > Programming > Python > code review

Reply
Thread Tools

code review

 
 
Chris Angelico
Guest
Posts: n/a
 
      06-30-2012
On Sun, Jul 1, 2012 at 8:05 AM, Thomas Jollans <> wrote:
> Yes. My sole point, really, is that "normally", one would expect these
> two expressions to be equivalent:
>
> a < b < c
> (a < b) < c
>
> This is clearly not true.


Python has quite a few things like that, actually. The most noticeable
for C programmers is:

a = b = c = d = e = 0

which in C is identical to

a = (b = (c = (d = (e = 0))))

because assignment is an expression, but in Python is equivalent to
nothing because assignment is simply allowed to do multiple. Downside:
*Only* simple assignment can be chained. Augmented assignment cannot:

>>> a+=10 # That's fine.
>>> a+=b+=10

File "<stdin>", line 1
a+=b+=10
^
SyntaxError: invalid syntax
>>> a=b+=10

File "<stdin>", line 1
a=b+=10
^
SyntaxError: invalid syntax
>>> a+=b=10

File "<stdin>", line 1
a+=b=10
^
SyntaxError: invalid syntax


In C, these are all well-defined and valid, and are evaluated
right-to-left, and do what you would expect. (And yes, it's handy at
times to do this sort of thing.)

So it's not a special case for the comparison operators; it's a more
general case that Python handles certain chains of operators as single
entities, rather than breaking everything down into "OPERAND OPERATOR
OPERAND" and evaluating one at a time. Is it better than/worse than C?
Not really. It's a design choice and we code within it. (My personal
preference is for the C style, as it makes for a more expressive
language with less mental glitching, but as I say, that's personal
preference.)

ChrisA
 
Reply With Quote
 
 
 
 
Thomas Jollans
Guest
Posts: n/a
 
      06-30-2012
On 07/01/2012 01:25 AM, Chris Angelico wrote:
> On Sun, Jul 1, 2012 at 8:05 AM, Thomas Jollans <> wrote:
>> Yes. My sole point, really, is that "normally", one would expect these
>> two expressions to be equivalent:
>>
>> a < b < c
>> (a < b) < c
>>
>> This is clearly not true.

>
> Python has quite a few things like that, actually. The most noticeable
> for C programmers is:
>
> a = b = c = d = e = 0
>
> which in C is identical to
>
> a = (b = (c = (d = (e = 0))))
>
> because assignment is an expression, but in Python is equivalent to
> nothing because assignment is simply allowed to do multiple. Downside:
> *Only* simple assignment can be chained. Augmented assignment cannot:
>
>>>> a+=10 # That's fine.
>>>> a+=b+=10

> File "<stdin>", line 1
> a+=b+=10
> ^
> SyntaxError: invalid syntax
>>>> a=b+=10

> File "<stdin>", line 1
> a=b+=10
> ^
> SyntaxError: invalid syntax
>>>> a+=b=10

> File "<stdin>", line 1
> a+=b=10
> ^
> SyntaxError: invalid syntax
>
>
> In C, these are all well-defined and valid, and are evaluated
> right-to-left, and do what you would expect. (And yes, it's handy at
> times to do this sort of thing.)
>
> So it's not a special case for the comparison operators; it's a more
> general case that Python handles certain chains of operators as single
> entities, rather than breaking everything down into "OPERAND OPERATOR
> OPERAND" and evaluating one at a time. Is it better than/worse than C?
> Not really. It's a design choice and we code within it. (My personal
> preference is for the C style, as it makes for a more expressive
> language with less mental glitching, but as I say, that's personal
> preference.)


Nicely put. Of course it's not catastrophic, it's just a feature of
Python that I'm not particularly fond of.

Another operator with special chaining behaviour that occurred to me is
the tuple-building "," operator. This is a particularly interesting one
since the "," symbol is also used in other contexts where it is not an
operator, and the symbol can be considered an operator in the way it can
be in Python only in very few (if any) other programming languages.
 
Reply With Quote
 
 
 
 
Chris Angelico
Guest
Posts: n/a
 
      07-01-2012
On Sun, Jul 1, 2012 at 10:08 AM, Ben Finney <ben+> wrote:
> Thomas Jollans <> writes:
>
>> My sole point, really, is that "normally", one would expect these two
>> expressions to be equivalent:
>>
>> a < b < c
>> (a < b) < c

>
> What norm gives you that expectation? That's not how those operators
> work in mathematical notation. I know of no programming language that
> would give a newcomer to Python that expectation. So where is the norm
> you're referring to?


C, SQL, REXX, and many other languages.

ChrisA
 
Reply With Quote
 
Steven D'Aprano
Guest
Posts: n/a
 
      07-01-2012
On Sun, 01 Jul 2012 00:05:26 +0200, Thomas Jollans wrote:

> Yes. My sole point, really, is that "normally", one would expect these
> two expressions to be equivalent:
>
> a < b < c
> (a < b) < c


Good grief. Why would you expect that?

You can't just arbitrarily stick parentheses around parts of expressions
and expect the result to remain unchanged. Order of evaluation matters:

2**3**4 != (2**3)**4

Comparisons are no different. You can't just toss in some arbitrary
brackets and expect the result to be the same. In the second case, the
parentheses explicitly turn the comparison into the equivalent of this:

temporary_flag = a < b
temporary_flag < c

which is not the same as a < b < c.

This has nothing to do with chained comparisons. You can write the same
thing without chaining:

a < b and b < c
(a < b) < c

Is it not obvious that the second case is completely different from the
first? Chaining is irrelevant here.


> This is clearly not true. That's the inconsistency here with the rest of
> the language.


Nonsense. Your expectation that parentheses should not affect the order
of evaluation is at odds with the entire Python language, to say nothing
of almost every programming language in existence.


> As soon as you read it as a ternary operator,


Well that's your problem. Why are you reading it as a ternary operator?
It isn't one. It is a pair of chained binary operator.

How can you tell that it is a pair of binary operators, rather than a
single ternary operator?

1) There are TWO operators, hence a pair, not a single ternary operator;

2) each operator takes TWO arguments, not three.



Chained comparisons is a standard mathematical notation with an obvious
meaning that is familiar to anyone with even a passing familiarity to
mathematics. They have straight-forward and simple semantics. If other
languages don't allow them, so much the worse for other languages.

You seem to be inventing arguments to support an irrational dislike to
the feature, but the arguments don't stand up. By all means say that you
don't like chained comparisons, that is your right, but your attempts to
rationalise that dislike simply do not work.


--
Steven
 
Reply With Quote
 
Chris Angelico
Guest
Posts: n/a
 
      07-01-2012
On Sun, Jul 1, 2012 at 12:06 PM, Steven D'Aprano
<steve+> wrote:
> You can't just arbitrarily stick parentheses around parts of expressions
> and expect the result to remain unchanged. Order of evaluation matters:
>
> 2**3**4 != (2**3)**4


But that's because ** binds right to left. It _is_ valid to say:

2**3**4 = 2**(3**4)

That's the usual way of depicting order of evaluation: putting in the
implicit parentheses.

1+2*3 = 1+(2*3)

Everyone who knows algebra knows that the parens are optional, but
nobody would expect them to change the evaluation of the expression.
It's like adding whitespace:

1+2*3 = 1 + 2 * 3 = 1 + 2*3

where the latter is another way of showing order of evaluation (the
asterisk "binds more tightly" than the plus). With comparisons, it's
not the same.

(a<b)<=(c<d)

is, incidentally, a valid expression, as long as you accept that False
is less than True. So if (c<d) is replaced by a boolean variable, you
can conceivably have an expression like:

(len(x)<minimum)<require_minimum

where 'minimum' is an integer and 'require_minimum' is a boolean that,
if set to False, overrides the check. Sure, there's other ways to do
this, but this is at least plausible. And it's completely different
from:

len(x)<minimum<require_minimum

as Python interprets it.

ChrisA
 
Reply With Quote
 
rusi
Guest
Posts: n/a
 
      07-01-2012
On Jul 1, 3:05*am, Thomas Jollans <t...@jollybox.de> wrote:
> Yes. My sole point, really, is that "normally", one would expect these
> two expressions to be equivalent:
>
> a < b < c
> (a < b) < c
>
> This is clearly not true. That's the inconsistency here....


I dont see the inconsistency with the specific example as you've
given. However if we consider the argument in general, there is
something to be said for being (more) careful to distinguish
associative and conjunctive interpretation of operators. IOW for an
arbitrary operator * (not standard multiply):

If * : t x t -> t, the only meaningful semantics of a*b*c is (a*b)*c
or a*(b*c)
If * : t x t -> Bool the only meaningful semantics of a*b*c is a*b +
b*c
where the most reasonable instance of '+' is 'and'

What happens when t = Bool?

Both cases match. And there is something to be said for notationally
allowing for both cases
Dijkstra/Scholten and David Gries books on logic in computer science
expand on this.

A short net-reachable paper is http://wwwhome.ewi.utwente.nl/~fokkinga/mmf2001a.pdf
 
Reply With Quote
 
Steven D'Aprano
Guest
Posts: n/a
 
      07-01-2012
On Sun, 01 Jul 2012 10:37:05 +1000, Chris Angelico wrote:

> On Sun, Jul 1, 2012 at 10:08 AM, Ben Finney <ben+>
> wrote:
>> Thomas Jollans <> writes:
>>
>>> My sole point, really, is that "normally", one would expect these two
>>> expressions to be equivalent:
>>>
>>> a < b < c
>>> (a < b) < c

>>
>> What norm gives you that expectation? That's not how those operators
>> work in mathematical notation. I know of no programming language that
>> would give a newcomer to Python that expectation. So where is the norm
>> you're referring to?

>
> C, SQL, REXX, and many other languages.


All the worse for those languages, since they violate the semantics of
mathematical notation.

The more I learn about C, the less I want to know about C. What sort of
crazy language designer thought that having

2 == 2 == 2

return 0 (false) was a good idea? At least Pascal gives an error, since
you can't compare bools with longints, and forces you to write:

(2 = 2) and (2 = 2)

Sheer craziness for C to abuse mathematical notation like that. But what
is one to expect from a language where

(unsigned)-1 == -1

apparently is true.

http://nitoprograms.blogspot.com.au/...-and-unsigned-
comparisons-in-c-c.html


--
Steven
 
Reply With Quote
 
Chris Angelico
Guest
Posts: n/a
 
      07-01-2012
On Sun, Jul 1, 2012 at 1:23 PM, Steven D'Aprano
<steve+> wrote:
> All the worse for those languages, since they violate the semantics of
> mathematical notation.


Not so. It simply means that booleans are nothing special. In REXX,
there are no data types at all, and "1" and "0" are your booleans. In
C, boolean and comparison operations return integers, either 1 or 0.
Same was true of Python early on, if I understand correctly. It's not
shameful.

> The more I learn about C, the less I want to know about C. What sort of
> crazy language designer thought that having
>
> 2 == 2 == 2
>
> return 0 (false) was a good idea?


It makes perfect sense though; your comparisons simply return
integers, so you can legally index using them. A simple binary tree
can work thus:

node child[2];

next_node = child[search_value>current_value];

> Sheer craziness for C to abuse mathematical notation like that. But what
> is one to expect from a language where
>
> (unsigned)-1 == -1
>
> apparently is true.


Of course it's true. When you compare an unsigned value to a signed
one, the signed value is automatically up-cast to unsigned, in the
same way that comparing 32-bit and 64-bit integers will do. So
whatever rule the compiler uses to cast your first -1 to unsigned will
be used to cast the second to unsigned, and they'll be equal.

As to "2 == 2 == 2": I don't see it as a problem that:

x = (2 == 2)
y = (x == 2)
z = (2 == 2 == 2)

leave y and z as the same. There are different ways of interpreting
the z statement, but having it identical to y is certainly a plausible
one.

ChrisA
 
Reply With Quote
 
rusi
Guest
Posts: n/a
 
      07-01-2012
On Jul 1, 8:23*am, Steven D'Aprano <steve
+comp.lang.pyt...@pearwood.info> wrote:
> On Sun, 01 Jul 2012 10:37:05 +1000, Chris Angelico wrote:
> > On Sun, Jul 1, 2012 at 10:08 AM, Ben Finney <ben+pyt...@benfinney.id.au>
> > wrote:
> >> Thomas Jollans <t...@jollybox.de> writes:

>
> >>> My sole point, really, is that "normally", one would expect these two
> >>> expressions to be equivalent:

>
> >>> a < b < c
> >>> (a < b) < c

>
> >> What norm gives you that expectation? That's not how those operators
> >> work in mathematical notation. I know of no programming language that
> >> would give a newcomer to Python that expectation. So where is the norm
> >> you're referring to?

>
> > C, SQL, REXX, and many other languages.

>
> All the worse for those languages, since they violate the semantics of
> mathematical notation.
>
> The more I learn about C, the less I want to know about C. What sort of
> crazy language designer thought that having
>
> 2 == 2 == 2
>
> return 0 (false) was a good idea?


Kernighan and Ritchie admit they made a design mistake with their
operator precedences:

http://en.wikipedia.org/wiki/C_%28pr...e%29#Criticism

That those mistakes are repeated and replicated is more unfortunate.
The second bullet above to be read along with
http://en.wikipedia.org/wiki/Assignm...ersus_equality
 
Reply With Quote
 
Steven D'Aprano
Guest
Posts: n/a
 
      07-01-2012
On Sun, 01 Jul 2012 12:20:52 +1000, Chris Angelico wrote:

> On Sun, Jul 1, 2012 at 12:06 PM, Steven D'Aprano
> <steve+> wrote:
>> You can't just arbitrarily stick parentheses around parts of
>> expressions and expect the result to remain unchanged. Order of
>> evaluation matters:
>>
>> 2**3**4 != (2**3)**4

>
> But that's because ** binds right to left. It _is_ valid to say:
>
> 2**3**4 = 2**(3**4)


Yes, but my point is that you can't just add parentheses arbitrarily
without understanding what you're doing.

If you're used to some feeble $2 calculator that doesn't implement
operator precedence, you might expect that you could do this too:

1+2*3
(1+2)*3

But you can't. It's your expectations that are skewed, not Python.

If you (generic you) are used to some feeble $2 language that doesn't
implement chained comparisons, it is your expectations that are skewed,
not Python.

Comparisons were invented long before C, or even computers, and they have
had chained semantics long before Python. Languages like C force you to
unlearn the standard semantics of comparisons, and substitute something
that is less expressive, less powerful, less efficient, and more likely
to contain a bug.

This is almost always wrong in languages without chained comparisons:

a < func(x) < b


This is inefficient, and still wrong, if x has side-effects or isn't
reentrant:

a < func(x) and func(x) < b


This is too verbose for such a simple comparison:

tmp = func(x)
(a < tmp) and (tmp < b)


[...]
> Everyone who knows algebra knows that the parens are optional, but
> nobody would expect them to change the evaluation of the expression.


Nonsense. Of course parens change the evaluation of the expression.
That's what parens are for!

There may be some specific cases where they don't, because you have
happened to put them in a specially crafted position where they don't
actually change the order of evaluation. E.g. putting parens around the
entire expression, or putting them around a single atom, or around
another pair of parentheses:

2*(3+4)
=> (2*(3+4)) # unchanged
=> (2)*(3+4)
=> 2*((3+4))


but just because there are *some* places you can add redundant parens
doesn't mean that you can add them *anywhere*. You have to understand the
semantics of expression, including the precedence rules. If you don't
understand them, you might as well be just adding parens in random
positions, in which case you should expect the semantics to change.


[...]
> (a<b)<=(c<d)
>
> is, incidentally, a valid expression, as long as you accept that False
> is less than True.


Right. I'm not saying that there's never any need to evaluate a
comparison, and then compare it to the result of another comparison.
That's a perfectly valid thing to do.

And you know what? You can do it, using parens, exactly as shown.

Chained comparisons is the common case, it should have the simple syntax.
That's why mathematicians write {x : a < x < b} instead of
{x: a < x} ∩ {x: x < b}. The uncommon case, treating a bool as a value to
be compared to another value, should be possible, which it is.



--
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
What is code review? (Java code review) www Java 51 05-15-2007 01:10 PM
Secure Python code - volunteers for code review? andrew blah Python 6 10-17-2004 01:17 AM
Re: Secure Python code - volunteers for code review? Josiah Carlson Python 1 10-13-2004 03:05 PM
Code write \ code review productivity Volodymyr Sadovyy Java 8 04-25-2004 03:30 AM
Code review of cross platform code sample Otto Wyss C++ 5 09-07-2003 02:06 PM



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