Package: nulltype
Helps define ‘null’ values and sentinels parallel to, but different from,
Python built-ins such asNone,False, andTrue.Noneis a greatsentinel valueand a classic implementation of thenull object pattern.But there are times that you need more than one nullish value to represent
different aspects of emptiness. “Nothing there” is logically different from
“undefined,” “prohibited,” “end of data,” and other kinds of “null.”nulltypehelps you easily represent different aspects of emptiness in a way
that doesn’t overloadNone(orFalse,0,{},[],"", or
any of the other possible “there’s nothing here!” values). It helps create
designated identifiers with specific meanings such asPassthrough,Prohibited, andUndefined.On the off chance that you need truish sentinels that aren’tTrue, it will
help you do that too. And it will do so in an easily-consumed,
right-off-the-shelf, fully-tested tested way.Usagefrom nulltype import NullType

Void = NullType('Void')

# following just to show it's working
assert bool(Void) == False
assert len(EmpVoidty) == 0
assert list(Void) == []
assert Void.some_attribute is Empty
assert Void[22] is Nothing
assert Void("hey", 12) is EmptyYou can create as many custom null values as you like. For convenience, several
default values,Empty,Null, andNothing, are exported. That way,
if you don’t really want to create your own, you can easily import a
pre-constituted null value:from nulltype import EmptyThe Power of NothingAlternate null types can be particularly useful when parsing
data or traversing data structures which might or might not be
present. This is common in dealing with the data returned byRESTAPIs, for instance.As one example,the documentation for Google’s Gmail APIsuggests the following code:threads = gmail_service.users().threads().list(userId='me').execute()
if threads['threads']:
    for thread in threads['threads']:
        print 'Thread ID: %s' % (thread['id'])There is a lot going on there just to avoid a problematic deference.
If instead you have aNothingnull type defined, the code is
shorter (and avoids an extra, very transient variable):results = gmail_service.users().threads().list(userId='me').execute()
for thread in results.get('threads', Nothing):
    print 'Thread ID: %s' % (thread['id'])Three lines versus four may not seem like a big advantage, but the value
increases with the complexity of the task. Many such “if it’s there, then…”
constructs are deeply nested when dealing with API results, XML parse trees,
and other fundamentally nested information sources. Saving a guard condition
on every one of the nesting levels adds up quickly.While you could almost do this in stock Python, unlikeNothing,Noneis
not iterable. You might use an empty list[](or an equivalent global such
asEMPTYLIST) as the alternative value for thegetmethod. Going by the
documentation of many parsers and APIs, however, such uses aren’t broadly
idiomatic in today’s Python community. TheEMPTYLISTapproach also is very
specific to routines returning lists, whereas the “go ahead, get it if you can”nulltypemodel works well for longer chains of access:results.get("payload", Nothing).get("headers", Nothing)will return the correct object if it’s there, butNothingotherwise.
And if you then try to test it (e.g. withifor a logical expression)
or iterate over it (e.g. withfor), it will act as though it’s an empty
list, orFalse–whatever is most useful in a given context. Whether you’re
iterating, indexing, dereferencing, calling, or otherwise accessing it, aNullTypeis unperturbed.Nothingisn’t nothing. It’s something that will simplify your code.General Sentinels and Distinguished ValuesWhilenulltypeis frequently used to define new kinds of “empty” values,
it’s actually more general. Beyond different forms of ‘null’,NullTypeinstances are good general-purpose sentinels or designated values. Instead of
the old:class MySentinelClass(object):
    passUse:MySentinel = NullType('MySentinel')That gives you a value with known truthiness properties and a nicer
printed representation.:>>> print MySentinelClass               # fugly
<class '__main__.MySentinelClass'>

>>> print MySentinel                    # just right
MySentinelOn the off chance you want a sentinel value that istruthyrather than falsey /
empty, useNonNullType, a companion toNullTypethat operates in
almost the exact same way, but that evaluates as true.:from nulltype import NonNullType

Full = NonNullType('Full')

assert bool(Full) is True
assert len(Full) == 1
assert list(Full) == [Full]
assert Full.some_attribute is Full
assert Full[22] is Full
assert Full("hey", 12) is FullExperience suggests that nullish sentinels are generally adequate and
preferable. And the “everything folds back to the same value” nature of evenNonNullTypegives a somewhat null-like, or at least non-reactive, nature.
But if you do want a true-ish sentinel, there it is.UniquenessNullTypeinstances are meant to besingletons, with just one per program.
They almost are, though technically multipleNullTypeinstances are
reasonable, making it more of amultiton pattern.The uniqueness of each singleton is currently not enforced, making it a usage
convention rather than strict law. With even minimal care, this is a problem
roughly 0% of the time.NotesSuccessfully packaged for, and
tested against, all late-model versions of Python: 2.6, 2.7, 3.3,
3.4, 3.5, 3.6, and 3.7 pre-release, as well as recent builds of PyPy and PyPy3.SeeCHANGES.ymlfor the complete Change Log.Automated multi-version testing managed withpytest,pytest-cov,coverageandtox. Continuous integration testing
withTravis-CI.
Packaging linting withpyroma.Similar modules includesentinelsandnull. Of these, I prefersentinelsbecause it is clearly Python 3 ready, includes apicklemechanism.noattris a
new alternative.For a module that uses the null valueEmptyto make the parsing of
JSON and other data formats easier, seeitemsThe author,Jonathan Euniceor@jeunice on Twitter,
welcomes your comments and suggestions.InstallationTo install or upgrade to the latest version:pip install -U nulltypeYou may need to prefix this withsudoto authorize installation on Unix,
Linux, and macOS. In environments without super-user privileges, you may want
to usepip’s--useroption, to install only for a single user, rather
than system-wide. On a system with multiple versions of Python, you may also
need to use specificpip3orpip2commands instead of the stockpip. As a backup, running pip as a Python module can save your sanity in
complex cases wherepipversions aren’t working well as standalone
commands:python3.6 -m pip install -U nulltypeTestingTo run the module tests, use one of these commands:tox                # normal run - speed optimized
tox -e py27        # run for a specific version only (e.g. py27, py34)
tox -c toxcov.ini  # run full coverage tests