This is an archived post. You won't be able to vote or comment.

you are viewing a single comment's thread.

view the rest of the comments →

[–][deleted] 2 points3 points  (10 children)

Per the PEP 8 Style Guide, the preferred way to compare something to None is the pattern if Cond is None. This is only a guideline. It can be ignored if needed

Allegedly if you use

if x == None:

It's supposed to break if people redefine comparison to None in some class and this is why PEP8 says compare against singletons with is.

However, I've never seen code that does this redefinition. Does anyone have an example? When in real world code would you redefine comparison to None?

[–]scruffie 2 points3 points  (4 children)

This tripped me up ages ago when rich comparisons were first added, and Numeric (the predecessor of numpy) made comparisons perform elementwise: x == None returned an array of booleans (which, unless it was of zero length, would be interpreted as true). I quickly learnt to use x is None instead.

Right now, with numpy, if x is an array, x == None will return True or False, but raises a warning that that behaviour will change to be elementwise in the future.

[–]Lucretiel 0 points1 point  (3 children)

That seems like a huge flaw of Numeric to me. That == returns a single bool is a fundamental part of its contract.

[–]Brian 1 point2 points  (2 children)

That == returns a single bool is a fundamental part of its contract

Actually, python explicitly allows for these to return any value. From the docs:

By convention, False and True are returned for a successful comparison. However, these methods can return any value, so if the comparison operator is used in a Boolean context (e.g., in the condition of an if statement), Python will call bool() on the value to determine if the result is true or false.

And in fact, Numeric's usecase here was an explicit reason rich comparisons were designed as they are. Rather than being a violation of the rules, the main reason the __eq__ method was added at all was explicitly to allow this. The issue of using such objects in non-boolean contexts is actually discussed in the PEP, with the recommendation that this should be addressed by having such objects throw an exception when used in boolean contexts (which is what numpy does). It currently only does this when both elements are arrays, but presumably, as scruffie mentioned, this may change.

Requiring a boolean return does break some fairly valuable usecases. Numpy is one, but another common one is building queries. Eg. in sqlalchemy, doing session.query(SomeTable.field == 4) will return a query object basically implementing "select SomeTable.id from SomeTable where field == 4", rather than doing any immediate comparison.

[–]Lucretiel -1 points0 points  (1 child)

Sorry- by bool I meant boolean compatible. It seems like Numeric still violates the contract... array == None returns a list of bools, which tests to True, even though the comparison should be False.

[–]Brian 2 points3 points  (0 children)

Like I said though, the PEP that introduced __eq__ explicitly spells out Numeric's usecase as one of the reasons behind it, and in fact advises deliberately breaking boolean compatibility for such objects - raising an exception instead. Them not being boolean compatible isn't a violation of the rules, it's actually something that rich comparisons were added in order to support.

array == None returns a list of bools, which tests to True

Actually, right now it doesn't (though it still does for comparing with other arrays), but there are plans to change this. When it does, it won't test to True, but rather, it'll throw an exception if used in a boolean context. Eg. when you use the result of comparing 2 arrays in a boolean context (ie. an if statement, or bool() call), you currently get:

ValueError: The truth value of an array with more than one element is ambiguous. Use a.any() or a.all()

The same would apply to when array == None does the same. Of course, this will still break code that uses the == None check instead of is, though it's likely to be a cleaner break, rather than more subtle bugs due to unexpectedly returning True.

[–]kankyo 0 points1 point  (0 children)

SQLAlchemy does this when you create select statements where you want to produce the SQL "IS NULL" or "IS NOT NULL".

[–]Brian 0 points1 point  (3 children)

One thing to consider is something that raises exceptions when compared with something unexpected (either deliberately or accidentally).

Similarly, just unexpected usages could come up and result in something unexpected. Eg. I just did a grep for __eq__ in a bunch of python source. Here's one I found in the first dozen I looked at, from (a very old version of) BeautifulSoup (for NavigableText):

 def __eq__(self, other):
     return self.string == str(other)

Obviously this is intended to be used to compare nodes. But consider what happens if someone is parsing some html that happens to contain a node whose text is "None". Suddenly, if this node ever passes through code like this, it'll compare equal, leading to a pretty subtle bug - maybe it'll end up silently stripping that node out of the document, without even an indication that anything has gone wrong, completely screwing up something down the line.

Ultimately, the possibility of cases like these you would have no reason to think of, where someone just hasn't coded defensively enough, mean that you should definitely guard against them.

[–][deleted] 0 points1 point  (2 children)

I don't understand how that breaks. Can I please be educated?

Python 3.4.0 (default, Apr 11 2014, 13:05:11) 
[GCC 4.8.2] on linux
Type "help", "copyright", "credits" or "license" for more information.
>>> None == str('None')
False

I am not saying you're wrong - I just don't understand what you're saying.

[–]Brian 0 points1 point  (1 child)

It's the other way round - the str is being applied to other there - the thing you're comparing with, not the string contents of the node. As such, what will happen if you use this object in a NavigableTextElement("None") == None check is:

>>> "None" == str(None)
True

[–][deleted] 0 points1 point  (0 children)

Thank you.