4

I have the following lambda expression:

constraints = lambda values: (
                              values['volume'] < 10, 
                              values['mass'] < 100,
                              values['nItems'] <= 10, 
                              values['nItems'] >= 5
                              )

I have a list that contains brands (dynamically populated), such as

brands = ['nestle', 'gatorate'] 

I want to be able to put in additional expressions dynamically such that:

constraints = lambda values: (
                              values['volume'] < 10, 
                              values['mass'] < 100,
                              values['nItems'] <= 10, 
                              values['nItems'] >= 5,

                              #dynamically adds these parameters
                              values['nestle'] < 5,
                              values['gatorate'] < 5
                              )

How can I do that using a for loop that iterates over brands and dynamically populates additional parameters into constraints from the brands list?

The answer can be in any form provided that I can get the desired constraints function.

4
  • Are you sure lambda is the way to go here? If the logic is not plain simple, I would go for a full-pledged function Commented Jun 17, 2016 at 10:11
  • I suppose you could add all(values[el] < 5 for el in brands) depending on scope... Commented Jun 17, 2016 at 10:15
  • @iTayb At this point, yes. This is an object that is used on other methods. Commented Jun 17, 2016 at 10:27
  • You can replace the lambda by a proper function and get some speedups. There is nothing that prevents your lambda from being a function. Commented Jun 17, 2016 at 10:32

2 Answers 2

4

First, don't use lambda where you don't need it

PEP8

Always use a def statement instead of an assignment statement that binds a lambda expression directly to an identifier.

Yes:

def f(x): return 2*x

No:

f = lambda x: 2*x

The first form means that the name of the resulting function object is specifically 'f' instead of the generic ''. This is more useful for tracebacks and string representations in general. The use of the assignment statement eliminates the sole benefit a lambda expression can offer over an explicit def statement (i.e. that it can be embedded inside a larger expression)

Make a list of functions

Now to the solution. Make a list of functions that will check your values.

Updating the list will mean updating the constraints.

def _constrain_main(values):
    """Static constraints"""
    return (values['volume'] < 10 and values['mass'] < 100 and
            values['nItems'] <= 10 and values['nItems'] >= 5)


def constrain(values):
    """Check for all constraints"""
    return all(c(values) for c in constraints)


# list of constraints
constraints = [_constrain_main]

# example
values = {
    'volume': 8,
    'mass': 80,
    'nItems': 8,
    'nestle': 6,
    'gatorate': 6,
}

print(constrain(values))    # True
# now append other lambdas (lambda is desired here)
constraints.append(lambda values: values['nestle'] < 5)
constraints.append(lambda values: values['gatorate'] < 5)
print(constrain(values))    # False
Sign up to request clarification or add additional context in comments.

3 Comments

Your answer contradicts itself. You first claim that the OP doesn't need to create a lambda when he doesn't need one. Then you create one lambda for each of the rules the OP wants to apply. Those lambda functions are completely unnecessary, specially only for checking comparisons. It can all be done without any lambda at all.
@ImanolLuengo Don't create lambda in a way the PEP8 says, otherwise it may be preferred. Sure it can be done without lambda, but for a price of extensive complexity.
Your answer is straightforward and simple, even though it didn't output the same results of my intended question. I was able to figure it out from your example and created my own solution. Thanks!
2

You can replace the lambda by a proper function and get some speedups. There is nothing that prevents your lambda from being a function.

Additionally, you cannot add dynamic expressions to a function. The closest approach would be to pass the keys and thresholds as parameters to your function:

def comparevalues(values, keys, thresholds, cmpfuncs):
    return [f(values[k], t) for f, k, t in zip(cmpfuncs, keys, thresholds)]

Here values is a dictionary, keys is a list of keys used in each "rule", threshold is the threshold applied to each "rule" and cmpfuncs are comparison functions used in each rule (e.g. <, <=, >...), you can create your own operators if they are complex, for simple thresholds you can use python's operators:

>>> import operator as op
>>> (5 > 6) == op.gt(5, 6) # Same operation, explicit vs function call

All 3 lists keys, thresholds and cmpfuncs should have the same length, as each of the elements forms a rule triplet.

As an example:

>>> import operator as op
>>> dictionary = {'a': 5, 'b': 10}

>>> keys = ['a', 'b', 'a']
>>> thresholds = [1, 4, 5]
>>> cmpfuncs = [op.gt, op.gt, op.lt]

>>> comparevalues(dictionary, keys, thresholds, cmpfuncs)
[True, True, False]

The above is creating 3 rules: (1) 'a' > 1, (2) 'b' > 4 and (3) 'a' < 5. Note that op.gt = > and op.lt = <.

If you want to dynamically add more operators, you just have to maintain a global lists of keys, threshodlsandcmpfuncs`, update them with new rules, and pass them with every function call.


Now that the approach is explained, find bellow a way of making the same problem more portable:

def comparerules(values, rules):
    return [f(values[k], t) for f, k, t in rules]

The approach is the same as above, but in this case rules is a list of triplets, where each triplet is a tuple of the form (cmpfunc, key, threshold).

To use this function with equivalent rules to the above example:

>>> values = {'a': 5, 'b': 10}
>>> rules = [(op.gt, 'a', 1), (op.gt, 'b', 4), (op.lt, 'a', 5)]

>>> comparerules(values, rules)
[True, True, False]

This way, you only have to maintain a global rules array, edit it to append/remove new rules, and send it in every function call:

>>> rules += [(op.gt, 'b', 10)] # values['b'] > 10
>>> comparerules(values, rules)
[True, True, False, False]

And last, to check whether values satisfies all the conditions, just use python's all:

>>> all(comparerules(values, rules))
False

1 Comment

Your answer is great and it gives me a clear understanding of what needs to be done here. Would have given you a tick if I could give 2 of them.

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.