Передача функции с двумя аргументами для filter () в python

дан следующий список:

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

Я хочу фильтровать строки длиной более 3 символов. Я достигаю этого со следующим кодом:

С for loop:

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

но я хочу, чтобы мой код был более общим, поэтому я могу позже фильтровать строки любой длины, поэтому я использую функцию и для цикла:

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)

8 ответов


использовать lambda чтобы обеспечить порог, например:

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

то, что вы пытаетесь сделать, называется частичное применение функции: у вас есть функция с несколькими аргументами (в данном случае 2) и вы хотите получить функцию, производную от нее, с одним или несколькими фиксированными аргументами, которые вы можете передать в filter.

некоторые языки (особенно функциональные) имеют эту функциональность "встроенную". В python вы можете использовать lambdas для этого (как показали другие) или вы можете использовать functools библиотека. В в частности,functools.partial:

partial () используется для применения частичной функции, которая "замораживает" некоторую часть аргументов функции и/или ключевых слов, в результате чего появляется новый объект с упрощенной сигнатурой. Например, partial () можно использовать для создания вызываемого объекта, который ведет себя как функция int (), где базовый аргумент по умолчанию равен двум:

>>> 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)

вам нужно использовать filter()? Почему бы не использовать более подходящие для Python понимание списке?

пример:

>>> 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']

этот метод имеет то преимущество, что его тривиально преобразовать в генератор, который может обеспечить улучшенную память и выполнение в зависимости от вашего приложения, например, если у вас есть много последовательностей ДНК, и вы хотите перебирать их, реализуя их как список, будет потреблять много памяти за один раз. Эквивалентный генератор просто требует замены square скобки [] С круглой скобки ():

>>> 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() возвращает объект фильтра, более похожий на генератор.


можно сделать 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)

цитирую рекомендации по программированию в PEP-8,

не сравнивайте логические значения с True или False, используя == .

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

вот еще несколько способов использования 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)

ты всегда можешь создать вызываемую что возвращает вызываемый подходит для сравнения сделал filter как показано в следующем примере:

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()

вы можете иметь более общий случай.

поскольку функция является объектом в 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-это функция, которая его создает.


я использовал другое решение, используя внутреннюю функцию и нелокальную область, как показано ниже. Я изменил этот исходный код для понимания, поскольку мой код отличается.

надеюсь, что это помогает. :)

 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