473,396 Members | 1,846 Online
Bytes | Software Development & Data Engineering Community
Post Job

Home Posts Topics Members FAQ

Join Bytes to post your question to a community of 473,396 software developers and data experts.

Sentinel values for special cases

Howdy all,

Ned Batchelder blogged[0] about a debate over checking function
parameters, and to what extent dynamic typing should be relied upon.

I was one of many who commented, but I wrote what purports to be a
comprehensive checklist when considering special-case inputs for
functions. I thought the best way to puncture my ego would be to post
that list here and see how well it survives :-) Here goes:
If you have a special case, there are a few steps.

First, do you really need a special case, or are you just being
paranoid about type safety? Let the caller take care of whether they
mean what they say, and make your function do *one* job clearly and
simply. (This supports the 'insert_ids(get_global_ids())' idea
earlier.)

Second, do you *really* need a special case, or are you making your
function too complex? Be very suspicious of functions that are written
to do two different things depending on their input, and split them so
that both are simple and the caller can be explicit about what they
want. This doesn't preclude factoring out the code that's common to
both of them, of course.

Third, if you actually need a special case, can it be None? This is
the idiomatic Python "sentinel value", and it looks like the code
posted by 'sri' [reproduced below].

def insert_ids(ids=None):
if ids is None:
ids = get_global_ids()

Note that if you're squeamish about using None,
but don't have a specific reason not to use it, use it; other
programmers will thank you for following convention.

Fourth, if you have decided that a magic sentinel value is called for
but None is already taken for some other purpose, don't use a
string. Use a unique do-nothing object, defined at the module level so
callers can easily get at it, like 'Dmitry Vasiliev' showed
[reproduced below].

GLOBAL = object()

def insert_ids(ids=GLOBAL):
if ids is GLOBAL:
ids = get_global_ids()

You won't accidentally use it, because it's defined only in one place
(you're comparing by 'is', remember) and it's not used for anything
except indicating the special case.

Fifth, there is no fifth. If you've come to the end and think it's too
complex, it probably is. Start at the top again.

[0]: <URL:http://www.nedbatchelder.com/blog/200610.html#e20061022T192641>

--
\ "Immorality: The morality of those who are having a better |
`\ time." -- Henry L. Mencken |
_o__) |
Ben Finney

Oct 27 '06 #1
7 3708
Ben Finney wrote:
Howdy all,

Ned Batchelder blogged[0] about a debate over checking function
parameters, and to what extent dynamic typing should be relied upon.

I was one of many who commented, but I wrote what purports to be a
comprehensive checklist when considering special-case inputs for
functions. I thought the best way to puncture my ego would be to post
that list here and see how well it survives :-) Here goes:

[0]: <URL:http://www.nedbatchelder.com/blog/200610.html#e20061022T192641>
Referring to the blog entry, if I had a function that could take
several items of data, where data items were 'expensive' to gather, but
might not all be needed due to other data values, then I can see the
need for using sentinel values for some of the data items and only
computing their true value when necessary.

In Python, as others stated, the sentinel value of choice for all the
data values should be None, or, some module level global constant,
guaranteed not to be part of the data.

None is what Python readers would expect as a sentinel value, but if
any of your data fields could have None as a valid value then you may
have to switch to a module level constant.
Be wary of using sentinel values which are strings, if your data could
itself be a string - make sure the sentinel value is not valid data,
and always use the sentinel name and not its value from then on. it is
very wrong to do this sort of thing:
NO_DATA = '::NO_DATA::'

def xyz(a,b,c):
if a == '::NO_DATA::':
# blah blah blah
You should use the name NO_DATA for the comparison.

If you are having difficulty working out what to use as a sentinel
value for your data then you could declare a Sentinel class and use
that:
class _Sentinel(object):
' Initial data value when true data has not been fetched/computed
yet'
pass

NO_DATA = _Sentinel

def xyz(a,b,c):
if a == NO_DATA:
# go get a
(Hmm, should that be double underscores on _Sentinel ...).

- Paddy.

Oct 27 '06 #2
"Paddy" <pa*******@netscape.netwrites:
None is what Python readers would expect as a sentinel value, but if
any of your data fields could have None as a valid value then you may
have to switch to a module level constant.
Be wary of using sentinel values which are strings, if your data could
itself be a string - make sure the sentinel value is not valid data,
and always use the sentinel name and not its value from then on.
I don't think "be wary" is appropriate; I think a blanket "don't use
strings for sentinel values" is fine. In your example below:
it is very wrong to do this sort of thing:

NO_DATA = '::NO_DATA::'

def xyz(a,b,c):
if a == '::NO_DATA::':
# blah blah blah
You should not use a string for that constant at all. Its only purpose
is to be a sentinel value, so just make it a unique object instance:

NO_DATA = object()

def xyz(a,b,c):
if a is NO_DATA:
# blah blah blah

The sentinel value for the 'a' parameter to 'foo.xyz()' is then
'foo.NO_DATA', nothing else. We're now checking by 'is', not '==', so
there's no way to pass the sentinel value to the function unless it's
that specific object. It's impossible to get that particular value any
other way, so you avoid the possibility of accidentally getting the
sentinel as part of the data.

I don't see any advantage a string would have to make it worth using
over the above type of sentinel.
If you are having difficulty working out what to use as a sentinel
value for your data then you could declare a Sentinel class and use
that:
Just use the plain 'object' class, since the instance name will make
its meaning clear, and other instances don't need to share any
behaviour or semantics.

--
\ "There is no reason anyone would want a computer in their |
`\ home." -- Ken Olson, president, chairman and founder of |
_o__) Digital Equipment Corp., 1977 |
Ben Finney

Oct 27 '06 #3
In article <ma***************************************@python. org>,
Ben Finney <be*@benfinney.id.auwrote:
>
Fourth, if you have decided that a magic sentinel value is called for
but None is already taken for some other purpose, don't use a
string. Use a unique do-nothing object, defined at the module level so
callers can easily get at it, like 'Dmitry Vasiliev' showed
[reproduced below].

GLOBAL = object()

def insert_ids(ids=GLOBAL):
if ids is GLOBAL:
ids = get_global_ids()
The one disadvantage of this approach is that it complicates pickling
if/when you store the stentinel in an instance. There are ways of
working around that, but none are pleasant.
--
Aahz (aa**@pythoncraft.com) <* http://www.pythoncraft.com/

"If you don't know what your program is supposed to do, you'd better not
start writing it." --Dijkstra
Oct 27 '06 #4
At Friday 27/10/2006 11:40, Aahz wrote:
GLOBAL = object()

def insert_ids(ids=GLOBAL):
if ids is GLOBAL:
ids = get_global_ids()

The one disadvantage of this approach is that it complicates pickling
if/when you store the stentinel in an instance. There are ways of
working around that, but none are pleasant.
But why should you store the sentinel in an instance? It's only
purpose is to detect a special case in the parameter, when None is
not appropiate.
Even if you were assigning instance attributes, you can use the
sentinel as a default class attribute (which are not pickled).
>>GLOBAL = object()
class A(object):
.... x = GLOBAL
.... def __init__(self, x=GLOBAL):
.... if x is not GLOBAL:
.... self.x = x
....
>>a = A(1)
a.x
1
>>vars(a)
{'x': 1}
>>b = A()
b.x
<object object at 0x00B8B438>
>>vars(b)
{}
>>>
from cPickle import loads,dumps
s=dumps(b)
b2=loads(s)
b2.x
<object object at 0x00B8B438>
>>vars(b2)
{}

--
Gabriel Genellina
Softlab SRL

__________________________________________________
Correo Yahoo!
Espacio para todos tus mensajes, antivirus y antispam ¡gratis!
¡Abrí tu cuenta ya! - http://correo.yahoo.com.ar
Oct 27 '06 #5
aa**@pythoncraft.com (Aahz) writes:
Ben Finney <be*@benfinney.id.auwrote:
Use a unique do-nothing object, defined at the module level so
callers can easily get at it [...]

GLOBAL = object()

def insert_ids(ids=GLOBAL):
if ids is GLOBAL:
ids = get_global_ids()

The one disadvantage of this approach is that it complicates
pickling if/when you store the stentinel in an instance. There are
ways of working around that, but none are pleasant.
Hmm, and any other kind of serialisation would be similarly affected I
suppose.

What's an alternative?

--
\ "Think for yourselves and let others enjoy the privilege to do |
`\ so too." -- Voltaire, _Essay On Tolerance_ |
_o__) |
Ben Finney

Oct 27 '06 #6
Gabriel Genellina <ga******@yahoo.com.arwrites:
At Friday 27/10/2006 11:40, Aahz wrote:
Ben Finney wrote:
GLOBAL = object()
>
def insert_ids(ids=GLOBAL):
if ids is GLOBAL:
ids = get_global_ids()
The one disadvantage of this approach is that it complicates
pickling if/when you store the stentinel in an instance. There are
ways of working around that, but none are pleasant.

But why should you store the sentinel in an instance? It's only
purpose is to detect a special case in the parameter, when None is
not appropiate.
You might be storing values that will later become parameters to the
function.

ids_to_be_processed_later = {
'chin': [14, 7, 9],
'bin': foo.GLOBAL,
'fin': [74, 98, 12],
}
serialise_stuff()

--
\ "The only tyrant I accept in this world is the still voice |
`\ within." -- Mahatma Gandhi |
_o__) |
Ben Finney

Oct 27 '06 #7
At Friday 27/10/2006 21:09, Ben Finney wrote:
GLOBAL = object()

def insert_ids(ids=GLOBAL):
if ids is GLOBAL:
ids = get_global_ids()
>
>The one disadvantage of this approach is that it complicates
>pickling if/when you store the stentinel in an instance. There are
>ways of working around that, but none are pleasant.
But why should you store the sentinel in an instance? It's only
purpose is to detect a special case in the parameter, when None is
not appropiate.

You might be storing values that will later become parameters to the
function.

ids_to_be_processed_later = {
'chin': [14, 7, 9],
'bin': foo.GLOBAL,
'fin': [74, 98, 12],
}
serialise_stuff()
Well... just don't do that! :)
Instead of a dict, use another class instance with a class attribute
(like on my earlier post).
--
Gabriel Genellina
Softlab SRL

__________________________________________________
Correo Yahoo!
Espacio para todos tus mensajes, antivirus y antispam ¡gratis!
¡Abrí tu cuenta ya! - http://correo.yahoo.com.ar
Oct 28 '06 #8

This thread has been closed and replies have been disabled. Please start a new discussion.

Similar topics

2
by: rzed | last post by:
I am working with PythonCard in one of my apps. For its purposes, it uses an .ini file that is passed to ConfigParser. For my app, I also need configuration information, but for various reasons,...
125
by: Sarah Tanembaum | last post by:
Beside its an opensource and supported by community, what's the fundamental differences between PostgreSQL and those high-price commercial database (and some are bloated such as Oracle) from...
10
by: Charles A. Lackman | last post by:
Hello, I have been working with an application that sends a dataset to other forms that are called from individual DLL's. I have noticed that, a dataset that is passed to another form, when...
10
by: joshb | last post by:
Hey guys, I have a length converter which converts mm, cm, m, km, ft, yd, in and mi. If I convert 5mm to miles, I get 0.00000031068559649... miles. I'm looking to convert all my values to two...
3
by: Mike | last post by:
I am writing this program that first asks the user to type today's exchange rate between U.S. dollars and Japanese yen, then reads U.S. dollar value and converts each. I am attemtping to use 0 or...
13
by: shan | last post by:
Hi to everybody here is my simple doubt What is meant by sentinel control loops ?
27
by: Ben Finney | last post by:
Antoon Pardon wrote: > I just downloaded your enum module for python > and played a bit with it. IMO some of the behaviour makes it less > usefull. Feedback is appreciated. I'm hoping to...
23
by: Antoon Pardon | last post by:
I know this PEP is rejected. However I have a problem that would benefit from having extreme values and the sample implementation that is given in the PEP is unsatifactory for my purpose. I had...
1
by: Sergiu Ignat | last post by:
Hello, I try to deploy an application through ClickOnce. I use .NET Framework 3.5SP1. In Publish/Prerequisites I require the entire framework (not only the client profile) and checked "Download...
0
by: emmanuelkatto | last post by:
Hi All, I am Emmanuel katto from Uganda. I want to ask what challenges you've faced while migrating a website to cloud. Please let me know. Thanks! Emmanuel
0
BarryA
by: BarryA | last post by:
What are the essential steps and strategies outlined in the Data Structures and Algorithms (DSA) roadmap for aspiring data scientists? How can individuals effectively utilize this roadmap to progress...
1
by: nemocccc | last post by:
hello, everyone, I want to develop a software for my android phone for daily needs, any suggestions?
1
by: Sonnysonu | last post by:
This is the data of csv file 1 2 3 1 2 3 1 2 3 1 2 3 2 3 2 3 3 the lengths should be different i have to store the data by column-wise with in the specific length. suppose the i have to...
0
by: Hystou | last post by:
There are some requirements for setting up RAID: 1. The motherboard and BIOS support RAID configuration. 2. The motherboard has 2 or more available SATA protocol SSD/HDD slots (including MSATA, M.2...
0
marktang
by: marktang | last post by:
ONU (Optical Network Unit) is one of the key components for providing high-speed Internet services. Its primary function is to act as an endpoint device located at the user's premises. However,...
0
Oralloy
by: Oralloy | last post by:
Hello folks, I am unable to find appropriate documentation on the type promotion of bit-fields when using the generalised comparison operator "<=>". The problem is that using the GNU compilers,...
0
tracyyun
by: tracyyun | last post by:
Dear forum friends, With the development of smart home technology, a variety of wireless communication protocols have appeared on the market, such as Zigbee, Z-Wave, Wi-Fi, Bluetooth, etc. Each...
0
agi2029
by: agi2029 | last post by:
Let's talk about the concept of autonomous AI software engineers and no-code agents. These AIs are designed to manage the entire lifecycle of a software development project—planning, coding, testing,...

By using Bytes.com and it's services, you agree to our Privacy Policy and Terms of Use.

To disable or enable advertisements and analytics tracking please visit the manage ads & tracking page.