在python中将带有两个参数的函数传递给filter()

问题描述 投票:8回答:8

鉴于以下列表:

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

我想过滤超过3个字符的字符串。我用以下代码实现了这一点:

使用for循环:

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

但我希望我的代码更通用,所以我以后可以过滤任何长度的字符串,所以我使用一个函数和for循环:

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

我希望使用filter()实现相同的通用性,但我无法实现这一点。如果我使用上面的函数get_long(),当我将它与filter()一起使用时,我根本无法将参数传递给它。这是不可能的还是有办法绕过它?

我的代码与filter()的具体情况:

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

    long_dna_filter = filter(is_long, DNA_list)
python list python-2.7
8个回答
15
投票

使用lambda提供阈值,如下所示:

filter(lambda seq: get_long(seq, 3),
       dna_list)

15
投票

你要做的事情被称为partial function application:你有一个带有多个参数的函数(在这种情况下,2)并且希望得到一个从它派生的函数,其中一个或多个参数被修复,然后你可以传递给filter

某些语言(特别是功能语言)具有“内置”功能。在python中,您可以使用lambdas来执行此操作(正如其他人所示)或者您可以使用functools library。特别是,functools.partial

partial()用于部分函数应用程序,它“冻结”函数的参数和/或关键字的某些部分,从而产生具有简化签名的新对象。例如,partial()可用于创建一个callable,其行为类似于int()函数,其中base参数默认为2:

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

所以你可以这样做:

filter(functools.partial(get_long, treshold=13), DNA_list)

10
投票

你需要使用filter()吗?为什么不使用更多的Pythonic列表理解?

例:

>>> DNA_list = ['ATAT', 'GTGTACGT', 'AAAAGGTT']
>>> threshold = 3
>>> long_dna = [dna_seq for dna_seq in DNA_list if len(dna_seq) > threshold]
>>> long_dna
['ATAT', 'GTGTACGT', 'AAAAGGTT']

>>> threshold = 4
>>> [dna_seq for dna_seq in DNA_list if len(dna_seq) > threshold]
['GTGTACGT', 'AAAAGGTT']

这种方法的优点是,将它转换为一个发生器是微不足道的,它可以根据你的应用提供改进的存储器和执行,例如:如果你有很多DNA序列,并且你想迭代它们,那么将它们作为一个列表来实现会一次消耗大量的内存。等效的生成器只需要用圆括号[]替换方括号()

>>> long_dna = (dna_seq for dna_seq in DNA_list if len(dna_seq) > threshold)
<generator object <genexpr> at 0x7f50de229cd0>
>>> list(long_dna)
['GTGTACGT', 'AAAAGGTT']

在Python 2中,这种性能改进不是filter()的选项,因为它返回一个列表。在Python 3中,filter()返回一个更类似于生成器的过滤器对象。


4
投票

你可以让is_long返回一个函数,它可以接受dna,就像这样

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

然后在filter中使用它,就像这样

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

注意:不要使用is运算符来比较布尔值或数字。而是尽可能地依赖数据的真实性。所以,在你的情况下,你可以写这样的第二个版本

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

引用programming recommendations in PEP-8

不要使用==将布尔值与True或False进行比较。

 Yes:   if greeting:
 No:    if greeting == True:
 Worse: if greeting is True:

0
投票

以下是使用lambda的几种方法。第一个使用默认关键字参数来保持所需的长度。第二个只是在lambda体内嵌入所需的长度。

#Create a list of strings
s = 'abcdefghi'
data = [s[:i+1] for i in range(len(s))]
print data

thresh = 3
print filter(lambda seq, n=thresh: len(seq) > n, data)

print filter(lambda seq: len(seq) > 5, data)

产量

['a', 'ab', 'abc', 'abcd', 'abcde', 'abcdef', 'abcdefg', 'abcdefgh', 'abcdefghi']
['abcd', 'abcde', 'abcdef', 'abcdefg', 'abcdefgh', 'abcdefghi']
['abcdef', 'abcdefg', 'abcdefgh', 'abcdefghi']

在第一个例子中,你也可以这样做:

print filter(lambda seq, n=3: len(seq) > n, data)

类似地,在第二个示例中,您可以使用本地(或全局)变量替换文字5,例如:

thresh = 5
print filter(lambda seq: len(seq) > thresh, data)

0
投票

你总是可以创建一个callable来返回一个适合filter完成的比较的callable,如下例所示:

def main():
    dna_list = ['A', 'CA', 'TGATGATAC', 'GGGTAAAATC', 'TCG', 'AGGTCGCT', 'TT',
                'GGGTTGGA', 'C', 'TTGGAGGG']
    print('\n'.join(filter(length_at_least(3), dna_list)))


def length_at_least(value):
    return lambda item: len(item) >= value

# length_at_least = lambda value: lambda item: len(item) >= value

if __name__ == '__main__':
    main()

0
投票

你可以有一个更一般的情况。

因为function是python中的一个对象,所以你可以创建另一个函数,它返回你想要的函数。

def f(threshhold):
    def g(x):
        return len(x)>threshhold
    return g #return a function

this_function = f(3)

DNA_list = ['ATAT', 'GTGTACGT', 'AAAAGGTT','AAA','AAAA']
filter(this_function, DNA_list)

output: ['ATAT', 'GTGTACGT', 'AAAAGGTT', 'AAAA']

g是你真正想要的,f是创建它的功能。


0
投票

我使用了内部函数和非局部范围的不同解决方案,如下所示。我已修改此原始代码以便理解,因为我的代码不同。

希望这可以帮助。 :)

 def outerfun():
    charlimit = 3
    def is_long(dna):
        nonlocal charlimit
        return len(dna) > charlimit
    long_dna_filter = filter(is_long, DNA_list)     
    return long_dna_filter
© www.soinside.com 2019 - 2024. All rights reserved.