Passing a function with two arguments to filter()

2020-07-06 05:55发布

Given the following list:

DNA_list = ['ATAT', 'GTGTACGT', 'AAAAGGTT']

I want to filter strings longer than 3 characters. I achieve this with the following code:

With for loop:

long_dna = []
for element in DNA_list:
    length = len(element)
    if int(length) > 3:
        long_dna.append(element)
print long_dna

But I want my code to be more general, so I can later filter strings of any length, so I use a function and for loop:

def get_long(dna_seq, threshold):
    return len(dna_seq) > threshold

long_dna_loop2 = []
for element in DNA_list:
    if get_long(element, 3) is True:
        long_dna_loop2.append(element)
print long_dna_loop2

I want to achieve the same generality using filter() but I cannot achieve this. If I use the above function get_long(), I simply cannot pass arguments to it when I use it with filter(). Is it just not possible or is there a way around it?

My code with filter() for the specific case:

def is_long(dna):
        return len(dna) > 3

    long_dna_filter = filter(is_long, DNA_list)

8条回答
该账号已被封号
2楼-- · 2020-07-06 06:54

What you are trying to do is known as partial function application: you have a function with multiple arguments (in this case, 2) and want to get a function derived from it with one or more arguments fixed, which you can then pass to filter.

Some languages (especially functional ones) have this functionality "built in". In python, you can use lambdas to do this (as others have shown) or you can use the functools library. In particular, functools.partial:

The partial() is used for partial function application which “freezes” some portion of a function’s arguments and/or keywords resulting in a new object with a simplified signature. For example, partial() can be used to create a callable that behaves like the int() function where the base argument defaults to two:

>>> from functools import partial
>>> basetwo = partial(int, base=2)
>>> basetwo.__doc__ = 'Convert base 2 string to an int.'
>>> basetwo('10010')
18

So you can do:

filter(functools.partial(get_long, treshold=13), DNA_list)
查看更多
Emotional °昔
3楼-- · 2020-07-06 06:58

You can make is_long return a function, which can accept dna, like this

>>> def is_long(length):
...     return lambda dna: len(dna) > length
... 

and then use it in filter, like this

>>> filter(is_long(3), DNA_list)
['ATAT', 'GTGTACGT', 'AAAAGGTT']
>>> filter(is_long(4), DNA_list)
['GTGTACGT', 'AAAAGGTT']

Note: Don't use is operator to compare booleans or numbers. Instead rely on the truthiness of the data as much as possible. So, in your case, you could have written your second version like this

if get_long(element, 3):
    long_dna_loop2.append(element)

Quoting programming recommendations in PEP-8,

Don't compare boolean values to True or False using == .

 Yes:   if greeting:
 No:    if greeting == True:
 Worse: if greeting is True:
查看更多
登录 后发表回答