Consider the following terribly contrived example:
class QueryThing: handle = None def __init__(self, db): if db and hasattr(db, 'get_handle'): self.handle = db.get_handle() >>> query = QueryThing(some_db) >>> query.handle NoneClearly, something has gone wrong here. Either
some_dbdoesn't have a
get_handlemethod, or it does and it failed to return one. But which was it? Returning a value representing the successful result or otherwise
Noneis a very common pattern in Python; a function without an explicit return always has an implicit
return Noneat its end.
So the problem here is there's no distinction between an unset property and an improperly set one. Or even worse: between an unset property and one for which
Noneis a legitimate value.
Python 1.4 added a built-in object -
Ellipsis- to provide an extending slicing syntax, primarily used by the 3rd party extension numpy. Nothing within Python handles this slicing syntax natively but any user-written classes can provide their own
__getitem__method which is
Ellipsisaware. But that's not what we're interested in here.
In Python 3.x, you can replace any reference to
Ellipsiswith that of its token:
...So, with that in mind, the above example could be re-written as:
class QueryThing: handle = ... def __init__(self, db): if db and hasattr(db, 'get_handle'): self.handle = db.get_handle() >>> query = QueryThing(some_db) >>> query.handle EllipsisHere we can see that there's obviously a problem with the db object itself, and no attempt was ever made to write to the .handle propery.
I like the visual "empty slot"-ness the
Ellipsisdenotes in the class definition. It's a simple pattern, sure, and it isn't meant to replace actual error handling. However, I find it to be a nice way to provide a semantic distinction that an overuse of