Welcome, guest | Sign In | My Account | Store | Cart

An example Maybe pattern implementation for Python.

Python, 54 lines
 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:

2 comments

Paddy McCarthy 13 years, 10 months ago  # | flag

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?

Alan Franzoni (author) 13 years, 9 months ago  # | flag

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.

Created by Alan Franzoni on Sat, 29 May 2010 (MIT)
Python recipes (4591)
Alan Franzoni's recipes (5)

Required Modules

  • (none specified)

Other Information and Tasks