Detecting whether a value was passed for a parameter (was: skipping __init__ and using exploiting a class member instead)

Ben Finney ben+python at benfinney.id.au
Sun Oct 20 07:50:57 EDT 2013


Peter Cacioppi <peter.cacioppi at gmail.com> writes:

> I was laboring under some misconception that there was Python magic
> that allowed __init__ and only __init__ to add class attributes by
> setting their values. Good to know this piece of magic isn't part of
> Python, and thus lazy eval can be handled more cleanly than I
> originally thought.

Rather, the magic is that ‘__init__’ will be called automatically by the
class creation protocol, to initialise any new instance of that class.

So the method to write if you want instance-specific initialisation is
still ‘__init__’.

> Think about it this way. None here really means "not yet initialized".
> It is a value that cannot occur naturally and thus functions as a
> not-initialized flag.

As you point out, though, that's not a menaing of None agreed on
everywhere. You can't expect that the caller will not be passing a value
of None for that parameter.

> So this, somewhat arbitrary, context sensitive value should be isolated
> as much as possible. You don't want it popping up hither and yon, you
> want to type as infrequently as possible and localized it to as few
> methods as possible.

Right. The pattern you're looking for is called a “sentinel value”
<URL:https://en.wikipedia.org/wiki/Sentinel_value>.

Usually, the None singleton is good enough for a sentinel value, but
often it has a meaning already in your context, so is not available as a
sentinel value.

In cases where None is not available, make a specific value that has no
other meaning, and use that specifically for your parameter's sentinel
value::

    class Foo(object):
        _default_for_x = object()

        def __init__(self, x=_default_for_object):
            """ Initialise a new instance. """
            if x is self._default_for_object:
                # No value for ‘x’ was passed, because ‘_default_for_x’
                # would not be passed by accident.
                x = self._get_value_for_x()
            self.x = x

        def _get_default_value_for_x(self):
            """" Calculate the default ‘x’ value for this instance. """
            return some_computation(self)

-- 
 \       “When a well-packaged web of lies has been sold to the masses |
  `\    over generations, the truth will seem utterly preposterous and |
_o__)                    its speaker a raving lunatic.” —Dresden James |
Ben Finney




More information about the Python-list mailing list