5

I'm developing a toolbox containing several python scripts. For several of them some arguments may be numeric values. Depending of the script some may require a value v to be between -1 and 1, or 0 and 1 or 1 and 10 or ... An example could be a page width from an output diagram which should be always positive.

I can check all the time if v is in the required range. I could also for each of these range define an Action or a type using argparse. An example is given using a new type:

def positive_num(a_value):
    """Check a numeric positive."""
    if not a_value > 0:
        raise argparse.ArgumentTypeError("Should be positive.")
    return a_value 

And add it later to the parser:

parser_grp.add_argument('-pw', '--page-width',
                        help='Output pdf file width (e.g. 7 inches).',
                        type=positive_num,
                        default=None,
                        required=False)

Now, if the value is a correlation coefficient (or anything in a range) would it be possible using action or types to write something more general using:

def ranged_num(a_value, lowest=-1, highest=1):
    """Check a numeric is in expected range."""
    if not (a_value >= lowest and a_value <= highest):
        raise argparse.ArgumentTypeError("Not in range.")
    return a_value 

That could later be added like:

parser_grp.add_argument('-c', '--correlation',
                        help='A value for the correlation coefficient',
                        type=ranged_num(-1,1),
                        default=None,
                        required=False)

I have tried in several ways but whithout success.

Thank you

5
  • You need a higher-order function to do this, a function that returns a function. type needs to be a function that accepts a single (note: string) argument, so that's what ranged_num(-1, 1) needs to return. Commented Nov 23, 2018 at 11:28
  • Yes... That's it. I will dig into that. Commented Nov 23, 2018 at 11:32
  • The question is how will I pass the range (-1,1) to this higher-order function since the normal way is to pass a function definition and not a function call to the type argument of add_argument() method. I recall that I don't want to write a particular function for each range. Commented Nov 23, 2018 at 11:42
  • That's the point of a higher-order function; the function that returns the type function can also take parameters. You would write type=ranged_num(-1, 1), and that function would return an appropriately-configures function that accepts the string argument. Commented Nov 23, 2018 at 11:44
  • functools.partial can be used to bind the low and high of ranged_num. Commented Nov 23, 2018 at 16:12

1 Answer 1

7

Per the documentation:

type= can take any callable that takes a single string argument and returns the converted value

Therefore, to use it like type=ranged_num(-1,1), your ranged_num function must return a function itself. A function that returns a function (or accepts a function as an argument, or both) is often referred to as a "higher-order function".

Here's a minimal example:

def ranged_num(lowest=-1, highest=1):
    """Check a numeric is in expected range."""
    def type_func(a_value):
        a_value = int(a_value)  # or "float"; you could also have error handling here
        if not (a_value >= lowest and a_value <= highest):  # I'd rewrite this to an "or"
            raise argparse.ArgumentTypeError("Not in range.")
        return a_value
    return type_func

Now ranged_num creates and returns a function, type_func, that is responsible for handling the string coming from the command line.

Sign up to request clarification or add additional context in comments.

3 Comments

I was on the way.Did not think about the fact that lowest and highest where visible inside type_func... Thanks a lot for this short lesson. Best
@user451460 no worries; if you want more information on that, it's referred to as a "closure"
This is also nicer with a or... Thanks.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.