A class like this is useful although I've called it NamedObject (by analogy to NamedTuple). It's useful in contexts other than as a sentinel, for example to represent deleted values, but the fundamental aspects are that it's a unique object that has a name.

I've done two different things in my case:

(1) add __setattr__ so you can't set values on this object (to prevent someone that gets the object from operating on it as if it's some other object).

(2) printing str(descr) not repr(descr). Since the standard use case is passing a string to the constructor, it's not valuable for it to print quotes around it. I also use <> instead of NamedObject() but I don't care about that distinction.

>>> a = NamedObject('DELETED'), NamedObject('DELETED')
>>> a
(<DELETED>, <DELETED>)
>>> a[0] == a[1]
False

Note the last line: this is different from how unittest.mock.sentinel works. I find both cases useful (and particularly find the unittest.mock.sentinel version more useful in unittests).

Writing this up I suppose UniqueObject might be a better name for this than NamedObject.

--- Bruce
Learn how hackers think: http://j.mp/gruyere-security


On Sat, Feb 15, 2014 at 3:56 AM, Chris Angelico <rosuav@gmail.com> wrote:
A common pattern, both in the stdlib and in user code, is the
dedicated sentinel object to recognize an omitted argument:

no_timeout = object()
def get_data(source, timeout=no_timeout):
    if timeout is not no_timeout:
        source.set_alarm(timeout)
    return source.pop()

This is a bit unclear in the function signature, as seen in help():

"""
Help on function get_data in module __main__:

get_data(source, timeout=<object object at 0x7f579fe53070>)
"""

The stdlib does this:
"""
Help on function create_connection in module socket:

create_connection(address, timeout=<object object at 0x7fe52900b080>,
source_address=None)
""" # chomp docstring

"""
Help on function urlopen in module urllib.request:

urlopen(url, data=None, timeout=<object object at 0x7fe52900b080>, *,
cafile=None, capath=None, cadefault=False)
""" # no docstring to chomp, actually

It's not particularly useful to get the object's address. Proposal: A
self-documenting Sentinel class which serves the exact same purpose.

class Sentinel:
    def __init__(self, desc):
        self.desc = desc
    def __repr__(self):
        return "Sentinel(" + repr(self.desc) + ")"

This can then be used just like object(), only it retains something
for the benefit of its repr:

no_timeout = Sentinel("No timeout")
# Same function definition
"""
Help on function get_data in module __main__:

get_data(source, timeout=Sentinel('No timeout'))
"""

I don't know how this interacts with Argument Clinic and C-written
functions. If there's something that's being done for those that would
make sentinels come out a particular way in their help() info, ideally
this should be displayed the same way (or at least similarly).

Thoughts?

ChrisA
_______________________________________________
Python-ideas mailing list
Python-ideas@python.org
https://mail.python.org/mailman/listinfo/python-ideas
Code of Conduct: http://python.org/psf/codeofconduct/