18

Using Python, am finding it difficult to get filter() to work with lambda for cases where more than 1 argument needs to be passed as is the case in the following snippet:

max_validation = lambda x,y,z: x < y < z
sequence1 = [1,4,8]
filter(max_validation, sequence1)

It raises the following error:

TypeError: <lambda>() takes exactly 3 arguments (1 given)

Please suggest as to what am doing wrong here.

martineau
  • 119,623
  • 25
  • 170
  • 301
sidhshar
  • 1,063
  • 1
  • 10
  • 18

9 Answers9

23

It's a little bit difficult to figure out exactly what you're trying to do. I'm going to interpret your question, then provide an answer. If this is not correct, please modify your question or comment on this answer.

Question

I have sequences that are exactly three elements long. Here's one:

sequence1 = [1, 4, 8]

I want to ensure that the first element is less than the second element, which should in turn be less than the third element. I've written the following function to do so:

max_validation = lambda x, y, z: x < y < z

How do I apply this using filter? Using filter(max_validation, sequence1) doesn't work.

Answer

Filter applies your function to each element of the provided iterable, picking it if the function returns True and discarding it if the function returns False.

In your case, filter first looks at the value 1. It tries to pass that into your function. Your function expects three arguments, and only one is provided, so this fails.

You need to make two changes. First, put your three-element sequence into a list or other sequence.

sequences = [[1, 4, 8], [2, 3, 9], [3, 2, 3]]
max_validation = lambda x: x[0] < x[1] < x[2] and len(x) == 3

I've added two other sequences to test. Because sequences is a list of a list, each list gets passed to your test function. Even if you're testing just one sequence, you should use [[1, 4, 8]] so that the entire sequence to test gets passed into your function.

I've also modified max_validation so that it accepts just one argument: the list to test. I've also added and len(x) == 3 to ensure that the sequences are only 3 elements in length

Wesley
  • 10,652
  • 4
  • 37
  • 52
11

The function passed to filter() only gets a single argument passed to it, which is the current element in the iterable being iterated.. If you need something fancier than that then filter() won't do.

Ignacio Vazquez-Abrams
  • 776,304
  • 153
  • 1,341
  • 1,358
5

Straight from the docs of Python Filters

Note that filter(function, iterable) is equivalent to [item for item in iterable if function(item)] if function is not None and [item for item in iterable if item] if function is None.

So, you can just process single arguments with Python filters. That effectively means you cannot use filters for the your example. You would have to write custom-code for the same.

userdj
  • 61
  • 2
4

It's possible to do this using a closure:

>>> def foo(a,b):
...   def bar(c):
...     return a+b+c
...   return bar
... 
>>> x = foo(1,2)
>>> x(3)
6
>>> y = foo(100,0)
>>> y(1)
101
>>> x(1)
4
Tyler Eaves
  • 12,879
  • 1
  • 32
  • 39
  • 1
    I don't think you've really understood the OP's problem. – Karl Knechtel Dec 08 '10 at 19:02
  • 1
    EH? I think I understood perfectly. Filter will call the function with one arg at a time. I proved a way to pass additional args in. of course the additional args don't come from the list, but that's just not how filter works. – Tyler Eaves Dec 08 '10 at 19:06
3

I hope you are aware of?

>>> max([1, 4, 8])
8

filter() takes a single argument. In your case, it will take 1. Then 4. Then 8.

user225312
  • 126,773
  • 69
  • 172
  • 181
2

This would work for sequences of any length:

all(x < y for x, y in zip(seq, seq[1:]))

What does there happens?

For sequence 1, 2, 3... you take sequences 1, 2, 3... and 2, 3, 4... and zip them together to sequence (1, 2), (2, 3), ... Then you check if statement 'x < y' holds for every pair.

And this will work for any associative rule you want to check.

Useful links:

  1. slices in Python

  2. zip in Python docs

  3. all in Python docs

Community
  • 1
  • 1
walkingpendulum
  • 190
  • 1
  • 11
1

I think all others didn't get the point. the error message is for lambda function not for the filter. You should rather call it this way:

filter(max_validation, *sequence1)

add a star on the list transform it into three arguments, then it will work.

wiswit
  • 5,599
  • 7
  • 30
  • 32
0

I'm in agreement with both @walkingpendulum and @Wesley, depending on the interpretation of the actual problem statement. So parsing through the ambiguity in the problem statement: If you're sequentially comparing one item to its previous value in an iterable object, a lambda expression is overkill, just use a list comprehension:

[1 if i < i+1 else 0 for i in sequence1]

If you're comparing objects, then just compare them -- a lambda expression wouldn't work firstly because you're only passing one argument where the lambda expression you defined you're passing three and lambda is generally applied across an iterable object. To compare objects, there are simple constructs for that:

sequence1 == some_other_sequence

and

x, y, z = 1, 2, 3
x < y < z

And lastly, if you want to apply a lambda expression to an iterable object, map can get you there: (arbitrary lambda function)

map(lambda x: x > 1, sequence1)

Otherwise @walkingpendulum and @Wesley cover the other interpretations

WaveRider
  • 475
  • 4
  • 10
-3

You could change

max_validation = lambda x,y,z: x < y < z

to

max_validation = lambda (x,y,z): x < y < z
Floern
  • 33,559
  • 24
  • 104
  • 119
Supratim
  • 65
  • 4
  • 1
    File "", line 1 max_validation = lambda (x,y,z): x < y < z ^ SyntaxError: invalid syntax – Xexeo Feb 29 '20 at 16:37