5
$\begingroup$

I want to teach students about default function arguments.

I have a bad habit of cooking-up contrived examples.

To prove that I am not a student cheating on their homework, I shall provide you with the following code. I really can write an example of a function without default arguments on my own, it is simply too contrived:

class SandwhichMaker:

    FISH = """
              /"*._         _
          .-*'`    `*-.._.-'/
        < * ))     ,       ( 
          `*-._`._(__.--*"`.\ 
        
    """

    @classmethod
    def publicly_make_sandwhich(klass, bread, veggie, meat = "fish", sauce = None):
        if (sauce == None):
            sauce = klass.meat_to_sauce(meat)
        bread  = str(bread)
        veggie = str(veggie)
        meat   = str(meat).lower()
        if meat == "fish":
            meat = SandwhichMaker.FISH
        sandwhich = klass.privatly_make_sandwhich(bread, veggie, meat, sauce)
        return sandwhich

    @classmethod
    def meat_to_sauce(cls, meat):
        if meat.lower() == "fish":
            return "tartar sauce"

    @classmethod
    def privatly_make_sandwhich(cls, bread, veggie, meat, sauce):
        """
        PRECONDITION: all input parameters are strings
        """
        seperator = "\n" + 40 * "-" + "\n"
        sandwhich = seperator.join([
            "\n\n",
            bread,
            veggie,
            meat,
            sauce,
            bread
        ])
        return sandwhich


sw = SandwhichMaker()

sandwhich = sw.publicly_make_sandwhich("whole wheat", "cucumber", "fish")

print(sandwhich)

Console output:

----------------------------------------
whole wheat
----------------------------------------
cucumber
----------------------------------------

              /"*._         _
          .-*'`    `*-.._.-'/
        < * ))     ,       ( 
          `*-._`._(__.--*"`.\ 
        
    
----------------------------------------
tartar sauce
----------------------------------------
whole wheat

So, my example function with default arguments is as follows:

publicly_make_sandwhich(klass, bread, veggie, meat = "fish", sauce = None):

The default meat is "fish"

I really want students to see how default arguments can be useful.

I thought about downloading a few open source code repositories.
I could then search all text-files for a regular expression (regex) to find functions having default arguments.

\(\s*\w*\s*\w+\s*=[^),][^)]*\)
  1. Exactly one left parenthesis ... [1, 1] of (
  2. Zero or more of any white-space character ([0, +inf] of \s)
  3. optional data-type such as float or int. zero or more letters, numbers or underscores. [1, +inf] of [a-zA-Z0-9_]
  4. zero or more white-space characters
  5. one or more letters, numbers or underscores.
  6. zero or more white-space chars
  7. exactly one equals sign
  8. exactly one character except right-paren or comma
  9. zero or more character other than right-paren
  10. exactly one right-parenthesis

It would be easier just to ask a human being for a good example.

$\endgroup$
1
  • 1
    $\begingroup$ My most common use pattern on that is debug=false $\endgroup$ Dec 29 '20 at 15:01
4
$\begingroup$

I recently used default arguments to write an API method for a mail sending library: The method needs a subject, a body and a "To" recipient, "Cc" and "Bcc" are optional:

def send(subject, body, to, cc=None, bcc=None):
    """
    Send a message with the given data using the earlier configured SMTP server and the earlier configured from address.
    :param subject: The subject (one-line string)
    :param body: The contents (multiline string)
    :param to: Recipient address (string)
    :param cc: CC address (string or None)
    :param bcc: BCC address (string or None)
    """
    # The following to helper functions are omitted for brevity
    msg = _create_message(subject, body, to, cc, bcc)
    _send_message(msg)

I also quite often use this to specify default constructor arguments for machine learning setups, so that I can change the parameters when experimenting, but have a default variant during development:

import random

class MulticlassPerceptron:

   def __init__(self, epochs=100, learning_rate=0.9, random_seed=15):
      self.epochs = epochs
      self.learning_rate = learning_rate
      random.seed(random_seed)
      ...

   def train(self, training_data):
      for epoch in range(self.epochs):
         for datapoint in training_data:
            ...

train_dataset = ...
m = MulticlassPerceptron()
m.train(train_dataset)

Generally, I make quite heavy use of default arguments whenever I write scripts for myself, because for me they are a perfect compromise between those two objectives:

  1. No one else than me will use the scripts, and my configuration is pretty constant, so there is no need for complex Config objects etc.
  2. It is bad practice to hardcode things that are technically changeable parameters.

So what I usually do is write methods that have only default arguments which store my default configuration: This is an easy way to store a configuration (satisfies objective 1) but could be changed if the need ever arises (satisfies objective 2).

Does this help you? If not, I'm happy to add more examples.

$\endgroup$
4
$\begingroup$

Probably the most basic example is Python's print function, which has keyword arguments sep and end.

$\endgroup$
3
$\begingroup$

I like TuringTux's answer quite a lot. If you're looking for a simpler example for the classroom just to illustrate, you might consider printing an array with an optional delimiter.

public void printArray(IList<T> array, string delimiter = ", ") 
{ 
    foreach (T in array)
    {
        Console.Write(T + delimiter);
    }
    Console.WriteLine();
}

This way, I can call

int[] arr = {1, 2, 3};

printArray(arr);
printArray(arr, "");
printArray(arr, "-->");

and wind up with the output

1, 2, 3, 
123 
1-->2-->3-->

(Apologies if any mistakes slipped through into the code -- I didn't actually try it out. The idea should be clear enough, though.)

The advantage here is that the example isn't purely silly, so the utility is clear enough, but the entire thing is simple enough to also allow mid-to-low level students to understand what they're seeing.

$\endgroup$
1
  • 1
    $\begingroup$ This is a real-world and self-contained example. I like it very much. $\endgroup$
    – TuringTux
    Dec 18 '20 at 17:38

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.