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


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

Proposal: === and !=== operators

Started bySteven D'Aprano <steve@pearwood.info>
First post2014-07-09 07:00 +0000
Last post2014-07-12 08:33 +0000
Articles 19 on this page of 39 — 16 participants

Back to article view | Back to comp.lang.python


Contents

  Proposal: === and !=== operators Steven D'Aprano <steve@pearwood.info> - 2014-07-09 07:00 +0000
    Re: Proposal: === and !=== operators Chris Angelico <rosuav@gmail.com> - 2014-07-09 17:21 +1000
      Re: Proposal: === and !=== operators Steven D'Aprano <steve@pearwood.info> - 2014-07-09 09:17 +0000
        Re: Proposal: === and !=== operators Rustom Mody <rustompmody@gmail.com> - 2014-07-09 09:20 -0700
        Re: Proposal: === and !=== operators Ian Kelly <ian.g.kelly@gmail.com> - 2014-07-09 11:50 -0600
        Re: Proposal: === and !=== operators Cameron Simpson <cs@zip.com.au> - 2014-07-10 09:16 +1000
        Re: Proposal: === and !=== operators Johannes Bauer <dfnsonfsduifb@gmx.de> - 2014-07-12 13:54 +0200
          Re: Proposal: === and !=== operators Steven D'Aprano <steve+comp.lang.python@pearwood.info> - 2014-07-12 16:35 +0000
            Re: Proposal: === and !=== operators Chris Angelico <rosuav@gmail.com> - 2014-07-13 02:54 +1000
              Re: Proposal: === and !=== operators Roy Smith <roy@panix.com> - 2014-07-12 16:39 -0400
            Re: Proposal: === and !=== operators Johannes Bauer <dfnsonfsduifb@gmx.de> - 2014-07-12 20:14 +0200
              Re: Proposal: === and !=== operators Chris Angelico <rosuav@gmail.com> - 2014-07-13 09:01 +1000
                Re: Proposal: === and !=== operators Roy Smith <roy@panix.com> - 2014-07-12 19:06 -0400
                  Re: Proposal: === and !=== operators Chris Angelico <rosuav@gmail.com> - 2014-07-13 09:15 +1000
              Re: Proposal: === and !=== operators Steven D'Aprano <steve+comp.lang.python@pearwood.info> - 2014-07-13 04:48 +0000
    Re: Proposal: === and !=== operators Cameron Simpson <cs@zip.com.au> - 2014-07-09 18:17 +1000
      Re: Proposal: === and !=== operators Steven D'Aprano <steve@pearwood.info> - 2014-07-09 09:02 +0000
        Re: Proposal: === and !=== operators Chris Angelico <rosuav@gmail.com> - 2014-07-09 19:23 +1000
    Re: Proposal: === and !=== operators Devin Jeanpierre <jeanpierreda@gmail.com> - 2014-07-09 05:01 -0700
    Re: Proposal: === and !=== operators Roy Smith <roy@panix.com> - 2014-07-09 08:27 -0400
      Re: Proposal: === and !=== operators Steven D'Aprano <steve+comp.lang.python@pearwood.info> - 2014-07-09 12:48 +0000
        Re: Proposal: === and !=== operators Tim Chase <python.list@tim.thechases.com> - 2014-07-09 13:05 -0500
          Re: Proposal: === and !=== operators Steven D'Aprano <steve+comp.lang.python@pearwood.info> - 2014-07-10 01:10 +0000
        Re: Proposal: === and !=== operators Ian Kelly <ian.g.kelly@gmail.com> - 2014-07-09 12:31 -0600
        Re: Proposal: === and !=== operators Roy Smith <roy@panix.com> - 2014-07-09 16:47 -0400
    Re: Proposal: === and !=== operators Ethan Furman <ethan@stoneleaf.us> - 2014-07-09 05:43 -0700
    Re: Proposal: === and !=== operators Robert Kern <robert.kern@gmail.com> - 2014-07-09 16:27 +0100
    Re: Proposal: === and !=== operators Alex Burke <alexjeffburke@gmail.com> - 2014-07-10 18:33 +0200
    Re: Proposal: === and !=== operators Steven D'Aprano <steve+comp.lang.python@pearwood.info> - 2014-07-12 03:30 +0000
      Re: Proposal: === and !=== operators Alan Bawden <alan@scooby-doo.csail.mit.edu> - 2014-07-12 01:07 -0400
        Re: Proposal: === and !=== operators Torsten Bronger <bronger@physik.rwth-aachen.de> - 2014-07-12 08:05 +0200
          Re: Proposal: === and !=== operators Torsten Bronger <bronger@physik.rwth-aachen.de> - 2014-07-12 08:14 +0200
        Re: Proposal: === and !=== operators Chris Angelico <rosuav@gmail.com> - 2014-07-12 16:06 +1000
        Re: Proposal: === and !=== operators Ethan Furman <ethan@stoneleaf.us> - 2014-07-11 23:11 -0700
        Re: Proposal: === and !=== operators Chris Angelico <rosuav@gmail.com> - 2014-07-12 16:39 +1000
          Re: Proposal: === and !=== operators Marko Rauhamaa <marko@pacujo.net> - 2014-07-12 10:06 +0300
        Re: Proposal: === and !=== operators Ethan Furman <ethan@stoneleaf.us> - 2014-07-11 23:53 -0700
        Re: Proposal: === and !=== operators Chris Angelico <rosuav@gmail.com> - 2014-07-12 17:25 +1000
        Re: Proposal: === and !=== operators Steven D'Aprano <steve+comp.lang.python@pearwood.info> - 2014-07-12 08:33 +0000

Page 2 of 2 — ← Prev page 1 [2]


#74250

FromSteven D'Aprano <steve+comp.lang.python@pearwood.info>
Date2014-07-09 12:48 +0000
Message-ID<53bd3a1d$0$29995$c3e8da3$5496439d@news.astraweb.com>
In reply to#74249
On Wed, 09 Jul 2014 08:27:28 -0400, Roy Smith wrote:

> We would have *three* ways to compare for equality (==, ===, and is).

`is` does not, never has, and never will, be a test for equality.

py> x = []
py> y = []
py> x is y
False



-- 
Steven

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


#74269

FromTim Chase <python.list@tim.thechases.com>
Date2014-07-09 13:05 -0500
Message-ID<mailman.11703.1404929192.18130.python-list@python.org>
In reply to#74250
On 2014-07-09 12:48, Steven D'Aprano wrote:
> On Wed, 09 Jul 2014 08:27:28 -0400, Roy Smith wrote:
> 
> > We would have *three* ways to compare for equality (==, ===, and
> > is).
> 
> `is` does not, never has, and never will, be a test for equality.
> 
> py> x = []
> py> y = []
> py> x is y
> False

I too am in the -1 category for adding an extra operator-suite
because of the confusion it will add (this is one of my biggest
pet peeves with PHP/JS).

If you want to compare things in a NaN-aware way, you can either
spell it out explicitly:

  if x == y or (math.isnan(x) and math.isnan(y)):
    do_stuff()

or create a wrapper function to do that for you:

 def equalish(x, y):
   return x == y or (math.isnan(x) and math.isnan(y))

 if equalish(w, z):
   print("yep")
 else:
   print("nope")

-tkc

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


#74289

FromSteven D'Aprano <steve+comp.lang.python@pearwood.info>
Date2014-07-10 01:10 +0000
Message-ID<53bde7ff$0$29995$c3e8da3$5496439d@news.astraweb.com>
In reply to#74269
On Wed, 09 Jul 2014 13:05:22 -0500, Tim Chase wrote:

> If you want to compare things in a NaN-aware way, you can either spell
> it out explicitly:
> 
>   if x == y or (math.isnan(x) and math.isnan(y)):
>     do_stuff()

But do we really want any arbitrary NAN to compare equal-ish with every 
other NAN? There are 9007199254740990 distinct NANs (slightly less than 
0.05% of the total number of floats). 

Much to my disappointment, frexp doesn't return the payload and exponent 
of NANs, at least not on Linux:

py> math.frexp(NAN)
(nan, 0)

so it can't be used to distinguish different NANs.


-- 
Steven

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


#74271

FromIan Kelly <ian.g.kelly@gmail.com>
Date2014-07-09 12:31 -0600
Message-ID<mailman.11705.1404930731.18130.python-list@python.org>
In reply to#74250
On Wed, Jul 9, 2014 at 12:05 PM, Tim Chase
<python.list@tim.thechases.com> wrote:
>  def equalish(x, y):
>    return x == y or (math.isnan(x) and math.isnan(y))

With more generality:

def nan_type(x):
    if isinstance(x, numbers.Complex):
        if cmath.isnan(x): return 'qnan'
    elif isinstance(x, decimal.Decimal):
        if x.is_qnan(): return 'qnan'
        if x.is_snan(): return 'snan'
    return None

def equalish(x, y):
    if x is y or x == y: return True
    x_nan = nan_type(x)
    return x_nan is not None and x_nan == nan_type(y)

Have I missed any cases?

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


#74279

FromRoy Smith <roy@panix.com>
Date2014-07-09 16:47 -0400
Message-ID<roy-9B49B8.16472209072014@news.panix.com>
In reply to#74250
In article <53bd3a1d$0$29995$c3e8da3$5496439d@news.astraweb.com>,
 Steven D'Aprano <steve+comp.lang.python@pearwood.info> wrote:

> On Wed, 09 Jul 2014 08:27:28 -0400, Roy Smith wrote:
> 
> > We would have *three* ways to compare for equality (==, ===, and is).
> 
> `is` does not, never has, and never will, be a test for equality.
> 
> py> x = []
> py> y = []
> py> x is y
> False

That is a very narrow legalistic way of looking at things.  If you don't 
like the word "equality", substitute a more generic word, such as 
"sameness"  We continually have threads about when to use "==" and when 
to use "is".  Clearly, there is confusion about them.  Adding another 
way to test for sameness will just increase the confusion.

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


#74251

FromEthan Furman <ethan@stoneleaf.us>
Date2014-07-09 05:43 -0700
Message-ID<mailman.11688.1404912787.18130.python-list@python.org>
In reply to#74232
On 07/09/2014 12:00 AM, Steven D'Aprano wrote:
>
> I propose:

[adding new operators]

-1

Too much added confusion, too little gain.

--
~Ethan~

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


#74259

FromRobert Kern <robert.kern@gmail.com>
Date2014-07-09 16:27 +0100
Message-ID<mailman.11694.1404919684.18130.python-list@python.org>
In reply to#74232
On 2014-07-09 08:00, Steven D'Aprano wrote:
> At the moment, Python has two (in)equality operators, == and != which
> call __eq__ and __ne__ methods. Some problems with those:
>
>
> * Many people expect == to always be reflexive (that is, x == x for
>    every x) but classes which customise __eq__ may not be.
>
> * The == operator requires __eq__ to return True or False
>    (or NotImplemented) and raises TypeError if it doesn't, which
>    makes it impossible to use == with (say) three-valued or fuzzy
>    logic.

No, it doesn't. It can return anything.

[~]
|1> x = np.arange(5)

[~]
|2> x == 3
array([False, False, False,  True, False], dtype=bool)

You can blame Numeric/numpy for that feature getting in. :-)

Now certainly, many uses of __eq__, like containment comparisons, do assume that 
the result is a bool(able).

-- 
Robert Kern

"I have come to believe that the whole world is an enigma, a harmless enigma
  that is made terrible by our own mad attempt to interpret it as though it had
  an underlying truth."
   -- Umberto Eco

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


#74313

FromAlex Burke <alexjeffburke@gmail.com>
Date2014-07-10 18:33 +0200
Message-ID<mailman.11730.1405010015.18130.python-list@python.org>
In reply to#74232
On 9 July 2014 09:00, Steven D'Aprano <steve@pearwood.info> wrote:
> At the moment, Python has two (in)equality operators, == and != which
> call __eq__ and __ne__ methods. Some problems with those:
>
>
> * Many people expect == to always be reflexive (that is, x == x for
>   every x) but classes which customise __eq__ may not be.
>
> * The == operator requires __eq__ to return True or False
>   (or NotImplemented) and raises TypeError if it doesn't, which
>   makes it impossible to use == with (say) three-valued or fuzzy
>   logic.
>
>
> I propose:
>
> * The == operator be redefined to *always* assume reflexivity, that
>   is, it first compares the two arguments using `is` before calling
>   the __eq__ methods.
>
> * That's a backwards-incompatible change, so you need to enable it
>   using "from __future__ import equals" in Python 3.5, and then to
>   become the default behaviour in 3.6.

(course I forgot to reply-all first first time I sent this)

Hi,

Aside from really reaching for Python for all my own projects (mostly
due to enjoying the language and it's design) the day job is mostly writing
server side code for Javascript on node.js. With that perspective I really
think those extra operators will cause confusion - granted the distinction
between == and === is for different reasons there but it's just complexity
to need to explain why there are different modes of comparison. With ==
and is I think when to use which is much more clear cut.

That being said though the point you raised that:
    different objects can implement __eq__ such that it may or may not be
    reflexive in different cases
could do with an answer. It's the implicitness and differing behaviour that
get me, and it seems to be one of the roots of the disagreement. When
should NaN compare equal, when not etc.

So, (this is likely a terrible idea) what if == was made always always reflexive
and a from __future__ to have any NaN being compared raise a new
NonReflexiveComparison exception? Advantages:
* no equality special cases to explain
* make potentially meaningless comparisons immediately clear
* if you really want to compare NaNs you can catch the case and
  return math.isnan(left) and math.isnan(right)

The obvious issue is possibility of exceptions from arbitrary == comparisons.

PS hoping these comments comments might prove useful :-)

Thanks, Alex J Burke.

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


#74365

FromSteven D'Aprano <steve+comp.lang.python@pearwood.info>
Date2014-07-12 03:30 +0000
Message-ID<53c0abe8$0$9505$c3e8da3$5496439d@news.astraweb.com>
In reply to#74232
Thanks to everyone who has commented. Some responses:

* I was completely mistaken about == enforcing the rule 
  that __eq__ returns True or False. I have no idea where
  I got that idea from, sorry for the noise.

* I disagree that having two equals operators, == and ===, 
  would be confusing. The Javascript/PHP model doesn't apply:

  - In Javascript and PHP, the shorter, more attractive
    version == does the wrong thing and is harmful, hence
    everyone needs to learn about === immediately.
  - In my proposal, the shorter, more attractive version 
    == does what everyone already assumes == will do. It's
    only specialists who need to learn about === .

* But having said that, I find myself trying to solve a problem
  reported by Anders which I do not understand, cannot replicate,
  and although I'm willing to give him the good faith benefit of 
  the doubt, I find myself less and less convinced that the 
  problem is what he thinks it is.

To be specific, the one concrete piece of code Anders has posted is a 
completely generic "detect changes" function which he claims is broken by 
the presence of NANs. That may be, but his code is not runnable (it uses 
undefined methods that could do anything) so cannot be tested, and I 
cannot replicate his problem.

NANs or no NANs, a generic "detect changes" function already fails to 
work in some circumstances:

py> data = [1, 2.0, 3.0, 4.0]
py> old = data[:]
py> old == data  # No changes made yet, should return True
True
py> data[0] = 1.0  # Change of type.
py> old == data  # Should return False
True


But perhaps we only care about changes in value, not type. NAN or no NAN, 
list equality works fine:

py> data = [1.0, 2.0, float('nan'), 4.0]
py> old = data[:]
py> old == data  # No changes made yet, should return True
True
py> data[3] = 104.0
py> old == data  # Should return False
False


The only time it may fail, for some definition of "fail", is when the NAN 
is replaced by another NAN of the same payload but different identity. 
But I still fail to understand Ander's use-case here, how a NAN might be 
replaced by another NAN, or why he wants to compare two different NANs as 
equal.

Since I do not understand Ander's problem, I cannot hope to solve it. So 
I withdraw my suggestion. Thanks to everyone who listened.



-- 
Steven

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


#74366

FromAlan Bawden <alan@scooby-doo.csail.mit.edu>
Date2014-07-12 01:07 -0400
Message-ID<w2dbnsvcghb.fsf@scooby-doo.csail.mit.edu>
In reply to#74365
Steven D'Aprano <steve+comp.lang.python@pearwood.info> writes:

> But perhaps we only care about changes in value, not type. NAN or no NAN, 
> list equality works fine:
>
> py> data = [1.0, 2.0, float('nan'), 4.0]
> py> old = data[:]
> py> old == data  # No changes made yet, should return True
> True

You lost me right here.  If list equality is determined by comparing
lists element-by-element, and the second element of old is _not_ equal
to the second element of data, then why should old and data be equal?

In fact, I find myself puzzled about exactly how list equality is
actually defined.  Consider:

  >>> a = float('nan')
  >>> x = [1, a, 9]
  >>> y = [1, a, 9.0]
  >>> x == y
  True

So is there some equality predicate where corresponding elements of x
and y are equal?  

  >>> map(operator.eq, x, y)
  [True, False, True]

It's not "==".

  >>> map(operator.is_, x, y)
  [True, True, False]

And it's not "is".

-- 
Alan Bawden

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


#74369

FromTorsten Bronger <bronger@physik.rwth-aachen.de>
Date2014-07-12 08:05 +0200
Message-ID<874mynjen4.fsf@physik.rwth-aachen.de>
In reply to#74366
Hallöchen!

Alan Bawden writes:

> [...]
>
> You lost me right here.  If list equality is determined by
> comparing lists element-by-element, and the second element of old
> is _not_ equal to the second element of data, then why should old
> and data be equal?

I think Python first tests for identity, and falls back to equality
(or the other way round).  This behaviour is questionable in my
opinion.

Tschö,
Torsten.

-- 
Torsten Bronger    Jabber ID: torsten.bronger@jabber.rwth-aachen.de
                                  or http://bronger-jmp.appspot.com

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


#74371

FromTorsten Bronger <bronger@physik.rwth-aachen.de>
Date2014-07-12 08:14 +0200
Message-ID<87zjgfhznj.fsf@physik.rwth-aachen.de>
In reply to#74369
Hallöchen!

Torsten Bronger writes:

> Alan Bawden writes:
>
>> [...]
>>
>> You lost me right here.  If list equality is determined by
>> comparing lists element-by-element, and the second element of old
>> is _not_ equal to the second element of data, then why should old
>> and data be equal?
>
> I think Python first tests for identity, and falls back to
> equality (or the other way round).  This behaviour is questionable
> in my opinion.

See also
https://mail.python.org/pipermail/python-bugs-list/2006-August/035010.html

Tschö,
Torsten.

-- 
Torsten Bronger    Jabber ID: torsten.bronger@jabber.rwth-aachen.de
                                  or http://bronger-jmp.appspot.com

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


#74370

FromChris Angelico <rosuav@gmail.com>
Date2014-07-12 16:06 +1000
Message-ID<mailman.11769.1405145213.18130.python-list@python.org>
In reply to#74366
On Sat, Jul 12, 2014 at 3:07 PM, Alan Bawden
<alan@scooby-doo.csail.mit.edu> wrote:
> Steven D'Aprano <steve+comp.lang.python@pearwood.info> writes:
>
>> But perhaps we only care about changes in value, not type. NAN or no NAN,
>> list equality works fine:
>>
>> py> data = [1.0, 2.0, float('nan'), 4.0]
>> py> old = data[:]
>> py> old == data  # No changes made yet, should return True
>> True
>
> You lost me right here.  If list equality is determined by comparing
> lists element-by-element, and the second element of old is _not_ equal
> to the second element of data, then why should old and data be equal?
>
> In fact, I find myself puzzled about exactly how list equality is
> actually defined.

In the Python 3 docs, it's made a bit clearer, at least as regards the
'in' and 'not in' operators: it's an identity check followed by an
equality check - "x is y or x == y". The same comparison is done for
equality, although it's not strictly described there (at least, I
couldn't find it). There are a lot of these sorts of corner cases that
aren't made really clear in the docs, because the verbosity would be
more of a problem than the omission - I consider them to be like "The
land continues to burn" on Obsidian Fireheart [1] or the reminder text
on Madness [2]; if you go to the Magic: The Gathering Comprehensive
Rulebook, you can find the exact "code-accurate" details on how either
one works, but that takes several pages of verbiage, and it's much
better to just say "you may cast it for its madness cost instead of
putting it into your graveyard".

For the most part, the identity check is a pure optimization. Most
Python objects compare equal to themselves. It only VERY occasionally
matters (like when there's a NaN in your list), and for those cases,
you have the interactive interpreter to try things at.

ChrisA

[1] http://gatherer.wizards.com/Pages/Card/Details.aspx?multiverseid=192224
[2] eg http://gatherer.wizards.com/Pages/Card/Details.aspx?multiverseid=118892

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


#74373

FromEthan Furman <ethan@stoneleaf.us>
Date2014-07-11 23:11 -0700
Message-ID<mailman.11771.1405146988.18130.python-list@python.org>
In reply to#74366
On 07/11/2014 10:07 PM, Alan Bawden wrote:
> Steven D'Aprano <steve+comp.lang.python@pearwood.info> writes:
>
>> But perhaps we only care about changes in value, not type. NAN or no NAN,
>> list equality works fine:
>>
>> py> data = [1.0, 2.0, float('nan'), 4.0]
>> py> old = data[:]
>> py> old == data  # No changes made yet, should return True
>> True
>
> You lost me right here.  If list equality is determined by comparing
> lists element-by-element, and the second element of old is _not_ equal
> to the second element of data, then why should old and data be equal?
>
> In fact, I find myself puzzled about exactly how list equality is
> actually defined.  Consider:
>
>    >>> a = float('nan')
>    >>> x = [1, a, 9]
>    >>> y = [1, a, 9.0]
>    >>> x == y
>    True
>
> So is there some equality predicate where corresponding elements of x
> and y are equal?
>
>    >>> map(operator.eq, x, y)
>    [True, False, True]
>
> It's not "==".
>
>    >>> map(operator.is_, x, y)
>    [True, True, False]
>
> And it's not "is".

class list:
     def __eq__(self, other):
         if len(self) != len(other):
             return False
         for this, that in zip(self, other):
              if this is that or this == that:
                  continue
              break
         else:
             return True
         return False

--
~Ethan~

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


#74374

FromChris Angelico <rosuav@gmail.com>
Date2014-07-12 16:39 +1000
Message-ID<mailman.11772.1405147177.18130.python-list@python.org>
In reply to#74366
On Sat, Jul 12, 2014 at 4:11 PM, Ethan Furman <ethan@stoneleaf.us> wrote:
> class list:
>     def __eq__(self, other):
>         if len(self) != len(other):
>             return False
>         for this, that in zip(self, other):
>              if this is that or this == that:
>                  continue
>              break
>         else:
>             return True
>         return False

Seems a little elaborate. Why not just return straight from the loop,
instead of breaking and using else? :)

ChrisA

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


#74375

FromMarko Rauhamaa <marko@pacujo.net>
Date2014-07-12 10:06 +0300
Message-ID<87mwcf6opo.fsf@elektro.pacujo.net>
In reply to#74374
Chris Angelico <rosuav@gmail.com>:

> On Sat, Jul 12, 2014 at 4:11 PM, Ethan Furman <ethan@stoneleaf.us> wrote:
>> class list:
>>     def __eq__(self, other):
>>         if len(self) != len(other):
>>             return False
>>         for this, that in zip(self, other):
>>              if this is that or this == that:
>>                  continue
>>              break
>>         else:
>>             return True
>>         return False
>
> Seems a little elaborate. Why not just return straight from the loop,
> instead of breaking and using else? :)

But look at that keyword density! That single function demonstrates 80%
of Python syntax making it a great educational tool.


Marko

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


#74376

FromEthan Furman <ethan@stoneleaf.us>
Date2014-07-11 23:53 -0700
Message-ID<mailman.11773.1405149423.18130.python-list@python.org>
In reply to#74366
On 07/11/2014 11:39 PM, Chris Angelico wrote:
> On Sat, Jul 12, 2014 at 4:11 PM, Ethan Furman <ethan@stoneleaf.us> wrote:
>> class list:
>>      def __eq__(self, other):
>>          if len(self) != len(other):
>>              return False
>>          for this, that in zip(self, other):
>>               if this is that or this == that:
>>                   continue
>>               break
>>          else:
>>              return True
>>          return False
>
> Seems a little elaborate. Why not just return straight from the loop,
> instead of breaking and using else? :)

Because I'm tired, and it seemed like a good excuse to show else with for, and because I'm tired.  :)

--
~Ethan~

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


#74377

FromChris Angelico <rosuav@gmail.com>
Date2014-07-12 17:25 +1000
Message-ID<mailman.11774.1405149942.18130.python-list@python.org>
In reply to#74366
On Sat, Jul 12, 2014 at 4:53 PM, Ethan Furman <ethan@stoneleaf.us> wrote:
> Because I'm tired, and it seemed like a good excuse to show else with for,
> and because I'm tired.  :)

Excellent justification. I withdraw the criticism. :)

ChrisA

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


#74379

FromSteven D'Aprano <steve+comp.lang.python@pearwood.info>
Date2014-07-12 08:33 +0000
Message-ID<53c0f2be$0$9505$c3e8da3$5496439d@news.astraweb.com>
In reply to#74366
On Sat, 12 Jul 2014 01:07:28 -0400, Alan Bawden wrote:

> Steven D'Aprano <steve+comp.lang.python@pearwood.info> writes:
> 
>> But perhaps we only care about changes in value, not type. NAN or no
>> NAN, list equality works fine:
>>
>> py> data = [1.0, 2.0, float('nan'), 4.0] 
>> py> old = data[:]
>> py> old == data  # No changes made yet, should return True True
> 
> You lost me right here.  If list equality is determined by comparing
> lists element-by-element, and the second element of old is _not_ equal
> to the second element of data, then why should old and data be equal?

Because practicality beats purity.

Outside of the specialist areas of IEEE-754 floating point NANs, and SQL 
NUL, it is a basic property of *nearly everything* that x equals itself, 
for any x. This property is called "reflexivity", and is considered by 
some (although not me) to be absolutely fundamental, never to be violated 
under any circumstances.

(To be clear, I'm talking about reflexivity of *equality* specifically, 
not of every operator. We wouldn't expect x > x, for example -- the 
"greater than" operator is not reflexive.)

While IEEE-754, and SQL, have good reasons for violating the reflexivity 
of equals, that upsets a lot of people. Given:

data = [1.0, 2.0, 3.0]
data == data

it would be surprising, and annoying, more often than useful if the 
equality test returned False. Replace any of the floats with a NAN, and 
the same surprise and annoyance applies.

In the case of Python, lists and other builtin containers partially 
violate the specialist rules of IEEE-754 NAN handling, by using "is" 
identity test as a shortcut for equality. Effectively, they assume that 
equality is always reflexive. This was introduced as an optimization, 
since == can be quite expensive in Python, whereas "is" requires only a 
fast pointer comparison and is very cheap.

I support this optimization, even if it violates the non-reflexivity of 
NANs. NANs are specialised values, and outside of the narrow confines of 
IEEE-754 arithmetic, their non-reflexivity is more of a nuisance than 
anything else.

(Some would argue that *even within* IEEE-754 arithmetic, non-reflexivity 
is a nuisance. For now, I prefer to remain neutral in that argument.)


> In fact, I find myself puzzled about exactly how list equality is
> actually defined.  

You'd have to check the C source code, but I would expect something like 
this (only written in C):

class list:
    def __eq__(self, other):
        if self is other:
            return True
        elif isinstance(other, list):
            if len(self) != len(other):
                return False
            for a, b in zip(self, other):
                if not (a is b or a == b):
                    return False
            return True
        else:
            return NotImplemented  # Let other decide.


-- 
Steven

[toc] | [prev] | [standalone]


Page 2 of 2 — ← Prev page 1 [2]

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


csiph-web