Credit: Dinu C. Gherman
You want to reduce the need for conditional statements in your code, particularly the need to keep checking for special cases.
The usual marker for “there’s
nothing here” is None
, but we may
be able to do better than that:
class Null: """ Null objects always and reliably "do nothing." """ def _ _init_ _(self, *args, **kwargs): pass def _ _call_ _(self, *args, **kwargs): return self def _ _repr_ _(self): return "Null( )" def _ _nonzero_ _(self): return 0 def _ _getattr_ _(self, name): return self def _ _setattr_ _(self, name, value): return self def _ _delattr_ _(self, name): return self
An instance of the Null
class can replace the
primitive value None
. Using this class, you can
avoid many conditional statements in your code and can often express
algorithms with little or no checking for special values. This recipe
is a sample implementation of the Null Object design pattern (see
“The Null Object Pattern”, B.
Woolf, Pattern Languages of Programming, PLoP
96, September 1996).
This recipe’s Null
class ignores
all parameters passed when constructing or calling instances and any
attempt to set or delete attributes. Any call or attempt to access an
attribute (or a method, since Python does not distinguish between the
two and calls _ _getattr_ _
either way) returns
the same Null
instance (i.e.,
self
, since there’s no reason to
create a new one). For example, if you have a computation such as:
def compute(x, y): try: "lots of computation here to return some appropriate object" except SomeError: return None
and you use it like this:
for x in xs: for y in ys: obj = compute(x, y) if obj is not None: obj.somemethod(y, x)
you can usefully change the computation to:
def compute(x, y): try: "lots of computation here to return some appropriate object" except SomeError: return Null( )
and thus simplify it as:
for x in xs: for y in ys: compute(x, y).somemethod(y, x)
Thus, you don’t need to check whether
compute
has returned a real result or an instance
of Null
. Even in the latter case, you can safely
and innocuously call on it whatever method you want.
Python calls _ _getattr_ _
for special methods as
well. This means that you may have to take some care and customize
Null
to your application’s needs,
either directly in the class’s sources or by
subclassing it appropriately. For example, with this
recipe’s Null
, any comparison
between Null
instances, even
a==a
, returns a Null
instance
and evaluates as false. (Note that we’ve had to
explicitly define _ _nonzero_ _
for this purpose,
since _ _nonzero_ _
must return an
int
.) If this is a problem for your purposes, you
must define _ _eq_ _
(in Null
itself or in an appropriate subclass) and implement it appropriately.
Similar delicate considerations apply to several other special
methods.
The goal of Null
objects is to provide an
intelligent replacement for the often-used primitive value
None
in Python (Null
or null
pointers in other languages). These “nobody lives
here” markers are used for many purposes, including
the important case in which one member of a group of otherwise
similar elements is special. Usually, this usage results in
conditional statements to distinguish between ordinary elements and
the primitive null (e.g., None
) value, but
Null
objects help you avoid that.
Among the advantages of using Null
objects are the
following:
Superfluous conditional statements can be avoided by providing a first-class object alternative for the primitive value
None
, thereby improving code readability.They can act as a placeholder for objects whose behavior is not yet implemented.
They can be used polymorphically with instances of any other class.
They are very predictable.
To cope with the disadvantage of creating large numbers of passive objects that do nothing but occupy memory space, the Null Object pattern is often combined with the Singleton pattern (see Recipe 5.22), but this recipe does not explore that combination.
“The Null Object Pattern”, B. Woolf, Pattern Languages of Programming, PLoP 96, September 1996, http://www.cs.wustl.edu/~schmidt/PLoP-96/woolf1.ps.gz.
Get Python Cookbook now with the O’Reilly learning platform.
O’Reilly members experience books, live events, courses curated by job role, and more from O’Reilly and nearly 200 top publishers.