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

A way to allow functions with 0, 1, and -1 arguments! For those times you want to differentiate a no value argument from a non-value argument.

Python, 18 lines
class Thing(object):
    """A thing, does stuff."""
    def __init__(self):
        self.special = "My special value!"

    def process(self, default=True):
        """Accept any argument with no special processing (except True)."""
        if default is True: # Notice I'm checking identity, not truth or equality
            default = self.special
        elif not default: # Optional check for False values
            print "Non-value given!"
        print default

if __name__ == "__main__":
    t = Thing()
    t.process()            # Prints t's special value
    t.process("something") # Prints 'something'
    t.process(None)        # Prints the False value warning

Say you have a method of some object, and as the default argument you want it to use an instance variable. Suppose also that this argument can also be something else, or even None (to indicate no action should be taken). How do you make a default argument that allows all these at once?

In my case I am passing messages using queues. I have a method that wraps all my data in a tuple, and puts it on a queue. The last part of the message is another queue for receiving return messages. By default it uses the class instance's input queue, but you could also get results into a special separate queue, or you could not care about results and just pass None.

I would like to just say def foo(self, result=self.in_queue): but default args are bound at definition, and there exists neither a self nor an in_queue when the method is defined. The other trick I would use is def foo(self, result=None): then if not result set result = self.in_queue. But I wanted to leave the possibility of passing an empty value to disable returns.

I guess you could use the result=None trick and just have some other special value (like False?) signal that no results are required. I think it is nicer (and more explicit) to handle the default this way though, rather than forcing the caller to use some special value (secret handshake).


Chris Phillips 14 years ago  # | flag

Building on your idea, instead of using True for your token, create a global- or class- variable and set it to something mutable like a list. For example:

class Thing(object):
    USE_DEFAULT = []

    def process(self, default = USE_DEFAULT):
        if default is self.USE_DEFAULT:
            pass # default behavior
            pass # non-default behavior

This allows you to True as a valid value and additionally (by using a well-named variable) is "self-documenting".

Gabriel Genellina 14 years ago  # | flag

The usual idiom is like Chris Phillips described it, but with USE_DEFAULT = object() to make clear it is not to be used as an actual list.

Ed Blake (author) 14 years ago  # | flag

Yeah, basically any unique identity will work.

As for my intended use of this trick: I RTFM and multiprocessing doesn't let you pass queue pointers across processes... duh. Not too hard to re-factor to a GUID though!

Created by Ed Blake on Wed, 14 Apr 2010 (MIT)
Python recipes (4591)
Ed Blake's recipes (1)

Required Modules

  • (none specified)

Other Information and Tasks