Jeroen,
given the context, I say that there are two reasons for using āis Noneā as is presented in that PEP: efficiency, and the āwhen you really mean āif x is not Noneāā. In the ROOT comparison code, you donāt really mean āis Noneā. After all, the return is a TObject*, which is never the singleton None, even if it is 0.
What I have to deal with is inconsistencies between C++ and python, and then get something that has a natural feel to it, while minimizing surprises. This approach is completely open to discussion. Below is my recollection of history and decisions. Feel free to add ideas.
The specific problem for None, is that in python it is both the not-object (i.e. C++ NULL) as well as nothing (i.e. C++ void). In staying strict with C++, I originally preferred to distinguish between (long)0 and None, when (void*)0 was the C++ result. However, that turned out to be awkward for users, who did not want to notice the difference (isnāt always possible, though). But so, it all became None.
Then, there is the following. If the result of a C++ function is a pointer, and it is compared against something, pointer-comparison is used in C++. However, using pointer comparison in python is awkward, because everything is a pointer. Hence, object comparison is always used, even when comparing a TObject** to a TObject*. That then, however, leads to differences in which comparison was selected, depending on the type of the object returned, because 1) PyROOT does automatic down-casting of pointers to the most derived known type, 2) python will select the comparison operator of the most object-like object, and 3) users can provide any kind of operator==() behavior they like.
Compare this, old root (and note that TFile::Get() returns a TObject*, just like FindObject() does; the comparison problem is with the right-hand TLorentzVector if None is returned):
[code]>>> from ROOT import *
f = TFile(āaap.rootā, ārecreateā )
a = TLorentzVector( 1, 2, 3, 4 )
a.Write()
156
del a
f.Get(āTLorentzVectorā) == TLorentzVector( 1, 2, 3, 4 )
1
f.Get(āTSpellErrorLorentzVectorā) == TLorentzVector( 1, 2, 3, 4 )
Traceback (most recent call last):
File āā, line 1, in
TypeError: Bool_t TLorentzVector::operator==(const TLorentzVector& q) =>
could not convert argument 1
f.Get(āTLorentzVectorā) == None
Traceback (most recent call last):
File āā, line 1, in
TypeError: Bool_t TLorentzVector::operator==(const TLorentzVector& q) =>
could not convert argument 1
f.Get(āTSpellErrorLorentzVectorā) == None
True
[/code]versus new root:>>> from ROOT import *
f = TFile('aap.root', 'recreate' )
a = TLorentzVector( 1, 2, 3, 4 )
a.Write()
156
del a
f.Get('TLorentzVector') == TLorentzVector( 1, 2, 3, 4 )
1
f.Get('TSpellErrorLorentzVector') == TLorentzVector( 1, 2, 3, 4 )
False
f.Get('TLorentzVector') == None
False
f.Get('TSpellErrorLorentzVector') == None
True
Iow., with a typed pointer return, the usage of comparison can remain consistent, even with a user-provided operator==(), and even if that operator is overloaded on multiple types.
Internally, the objects use the python C-API ārich compareā interface, so all that has to be done is special-case None and map operator==() and friends to richcompare as appropriate.
Cheers,
Wim