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_db
doesn't have a get_handle
method, or it does and it failed to return one. But which was it? Returning a value representing the successful result or otherwise None
is a very common pattern in Python; a function without an explicit return always has an implicit return None
at 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
None
is 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 Ellipsis
aware. But that's not what we're interested in here.In Python 3.x, you can replace any reference to
Ellipsis
with 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
Ellipsis
denotes 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 None
doesn't.
No comments:
Post a Comment