Передача функции с двумя аргументами для 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