Tutorial 1

Task: define a predicate expression that given a list of values, returns True if:

  1. all values are either strings or integers

  2. if the value is an integer, than it should be less than 5

  3. if the value is a string, it should start with foo

This will show a couple of concepts and useful standard predicates.

Let’s start with the first requirement. We start first with the code and then explain it:

from predicate import all_p, is_int_p, is_str_p

predicate = all_p(is_int_p | is_str_p)

In the example above we import 3 predicates. The all_p accepts one parameter, which is another predicate. The is_int_p checks if a value is an integer. And finally the is_str_p checks if a value is a string.

We combine the is_int_p and is_str_p with the | operator, resulting in a new predicate.

This is sufficient for the first requirement. Now lets restrict the allowed value of the integer to less than 5:

from predicate import all_p, is_int_p, is_str_p, lt_p

is_int_lt_5 = is_int_p & lt_p(5)

predicate = all_p(is_int_lt_5 | is_str_p)

As you can see, we imported the lt_p predicate. This checks if a given value, is less than the parameter (5 in this case). We could have made this a one-liner, but we assigned it to a new predicate variable is_int_lt_5. With this addition, we have implemented the second requirement.

Finally we turn to the third requirement, which says that if the value is a string, it should start with “foo”. This sounds like an ideal candidate for a regular expression, and indeed such a predicate is also available:

from predicate import all_p, is_int_p, is_str_p, lt_p, regex_p

is_int_lt_5 = is_int_p & lt_p(5)
is_str_foo = is_str_p & regex_p("^foo.*")

predicate = all_p(is_int_lt_5 | is_str_foo)

Now you are ready to check your new predicate against the requirements, for example:

predicate([1, "foo"])  # True
predicate([1, "foo", None])  # False, None is not valid
predicate([5, "foo"])  # False, 5 is to big
predicate([1, "bar"])  # False, "bar" doesn't begin with "foo"

Let’s now reuse this predicate to generate some sample values:

from predicate import generate_true
from more_itertools import take

result = take(10, generate_true(predicate))

Notice that this may take a few seconds. On my system the output was:

[
    [],
    (1, -1, "foo", "foo!", "foo!", 'foo"'),
    {0, -5, "foo"},
    ["foo5xT", "foo5xT", "foo", "foo ", "foo!", -1],
    (0, -1, -1, -1, "foo"),
    {"fooU7", "fooRV8TMtF", "foor959aO5"},
    [-9, "foo", "foojBA", "foo ", "foo "],
    ("foohqhdJr", "foou", "foo", 1, 1, 1, 1, 1, 2),
    {1, "foo", "foo!", -1, 'foo"'},
    ["foo", -1, -1, "foo", "foo", "foo ", 0, 'foo"', 'foo"'],
]

If you don’t want to check manually if these values are correct, you can of course use a predicate:

validate = all_p(predicate)
validate(result)

This should result in True. If not, please submit a bug report.