An example Maybe pattern implementation for Python.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 | #!/usr/bin/env python
# -*- coding: utf-8 -*-
NOVALUE = object()
class Maybe(object):
_has_value = False
_value = None
def __init__(self, value):
if value is not NOVALUE:
self._has_value = True
self._value = value
def __nonzero__(self):
return self.has_value
@property
def has_value(self):
return self._has_value
@property
def value(self):
return self._value
# optional sugar factories
def Some(value):
return Maybe(value)
def NoValue():
return Maybe(NOVALUE)
if __name__ == "__main__":
class MaybeSupportingRepository(object):
def __init__(self, *args):
self._d = dict(args)
def get(self, key):
return Maybe(self._d.get(key, NOVALUE))
repo = MaybeSupportingRepository( ("a", 1), ("b", 2), ("c", 3) )
key = "x"
maybe_v = repo.get(key)
if maybe_v:
print "There's a value for %s: %s" % (key, maybe_v.value)
else:
print "There's no value for %s" % key
key = "a"
maybe_v = repo.get(key)
if maybe_v.has_value:
print "There's a value for %s: %s" % (key, maybe_v.value)
else:
print "There's no value for %s" % key
|
Sometimes you need a way to tell a library's client that an object that was requested is unavailable. There're many ways to achieve this; the most common, in Python, is just returning None; other languages, especially statically typed ones, often return null. Otherwise, an exception can be raised, or the Null Object pattern can be employed, or you might just return a tuple holding something like (has_value, value). Python dictionaries offer even more solutions: just specify a fallback value when calling the get() method, and you're done.
The choice, of course, is up to you and depends on your needs; this solution is an improvement over simply returning None, because such behaviour can often slip through unnoticed, ending up with a "NoneType object has no attribute XXX" exception somewhere else in the application code, or valid values, such as empty collections, might be considered no-values because of a quick check like:
v = container.get("key", None)
if v:
# do something
else:
# no value found
This recipe:
- forces return value checking, because the return value is NOT the final value the client needs.
- allows the check above to work in all cases, because the object will evaluate to False only if its value is None. Empty collections will evaluate as True.
Syntactic-sugar factories are provided as well, in order to make your code more readable whenever applicable.
EDIT:
Somebody let me notice that None may actually be a valid value that we want to be returned. Hence it's better to create a new NOVALUE constant.
Further references:
Hmmm, Wouldn't the normal python idiom be to raise an appropriate exception and leave it up to the caller to deal with, or not? How does your solution win over this?
Sorry for the late response, I was not notified of your comment.
It's a matter of taste. I find try...except clauses cause eccessive "code stuttering", while the Maybe approach produces more readable code.