Как использовать фильтр, карту и уменьшить в Python 3

321

filter, mapИ reduceработа отлично в Python 2. Вот пример:

>>> def f(x):
        return x % 2 != 0 and x % 3 != 0
>>> filter(f, range(2, 25))
[5, 7, 11, 13, 17, 19, 23]

>>> def cube(x):
        return x*x*x
>>> map(cube, range(1, 11))
[1, 8, 27, 64, 125, 216, 343, 512, 729, 1000]

>>> def add(x,y):
        return x+y
>>> reduce(add, range(1, 11))
55

Но в Python 3 я получаю следующие выводы:

>>> filter(f, range(2, 25))
<filter object at 0x0000000002C14908>

>>> map(cube, range(1, 11))
<map object at 0x0000000002C82B70>

>>> reduce(add, range(1, 11))
Traceback (most recent call last):
  File "<pyshell#8>", line 1, in <module>
    reduce(add, range(1, 11))
NameError: name 'reduce' is not defined

Буду признателен, если кто-нибудь сможет объяснить мне, почему это так.

Скриншот кода для большей наглядности:

IDLE сессии Python 2 и 3 бок о бок

Дик Лукас
источник
1
Короче говоря, список не единственный тип данных. Если вы хотите список, скажем, вы хотите список. Но в большинстве случаев вам все равно нужно что-то еще.
Веки

Ответы:

346

Вы можете прочитать об изменениях в Что нового в Python 3.0 . Вы должны внимательно прочитать его, когда переходите с 2.x на 3.x, так как многое изменилось.

Весь ответ здесь - цитаты из документации.

Представления и итераторы вместо списков

Некоторые известные API больше не возвращают списки:

  • [...]
  • map()и filter()вернуть итераторы. Если вам действительно нужен список, быстрое исправление, например list(map(...)), но лучшее решение часто заключается в использовании понимания списка (особенно, когда исходный код использует лямбда-выражения) или переписывании кода, чтобы он вообще не нуждался в списке. Особенно сложно map()ссылаться на побочные эффекты функции; правильное преобразование заключается в использовании обычного forцикла (поскольку создание списка было бы просто расточительным).
  • [...]

Встроенные команды

  • [...]
  • Удалены reduce(). Используйте, functools.reduce()если вам действительно это нужно; однако в 99% случаев явный forцикл более читабелен.
  • [...]
nhahtdh
источник
21
Добавление list(map(...) везде ... как в мире это помогает удобочитаемости ... pythonне может справиться с прогрессивным / потоковым применением функциональных комбинаторов. На других языках я могу связать дюжину операций с коллекцией подряд, и она может быть прочитана. Вот? что ты хочешь - дюжина вложенных путей in??
Джавадба
11
Если вы работаете в императивном контексте, то цикл for, вероятно, является более читабельным вариантом. Но есть веские причины отдавать предпочтение функциональному контексту - и отказ от этого, чтобы вернуться к процедурному, может быть чертовски уродливым
MatrixManAtYrService
2
@javadba Вы уверены, что в «потоковом приложении» вам нужно добавить listвызов вообще? Я думал, что значение «потоковая передача» - это «список не создается вообще; полностью обработайте каждый элемент ввода, прежде чем переходить к следующему».
Нетленная ночь
@MatrixManAtYrService Если вы уверены, что поведение Python 2 - это то, что вам нужно, вы всегда можете просто переопределить map.
Нетленная ночь
6
Я до сих пор не могу понять, как аргумент читабельности приводит к такому изменению. Если бы это было из соображений производительности, я мог бы понять ...
Минато
86

Функциональность mapи filterбыла намеренно изменена, чтобы возвращать итераторы, а редукция была удалена из встроенной и помещена в functools.reduce.

Таким образом, для filterи map, вы можете обернуть их, list()чтобы увидеть результаты, как вы делали раньше.

>>> def f(x): return x % 2 != 0 and x % 3 != 0
...
>>> list(filter(f, range(2, 25)))
[5, 7, 11, 13, 17, 19, 23]
>>> def cube(x): return x*x*x
...
>>> list(map(cube, range(1, 11)))
[1, 8, 27, 64, 125, 216, 343, 512, 729, 1000]
>>> import functools
>>> def add(x,y): return x+y
...
>>> functools.reduce(add, range(1, 11))
55
>>>

В настоящее время рекомендуется заменить использование карты и фильтра выражениями генераторов или списками. Пример:

>>> def f(x): return x % 2 != 0 and x % 3 != 0
...
>>> [i for i in range(2, 25) if f(i)]
[5, 7, 11, 13, 17, 19, 23]
>>> def cube(x): return x*x*x
...
>>> [cube(i) for i in range(1, 11)]
[1, 8, 27, 64, 125, 216, 343, 512, 729, 1000]
>>>

Говорят, что циклы for в 99 процентах случаев легче читать, чем сокращать, но я бы просто придерживался их functools.reduce.

Редактировать : 99-процентный показатель взят прямо со страницы « Что нового в Python 3.0», созданной Гвидо ван Россумом.

Джошуа Д. Бойд
источник
5
Вам не нужно создавать дополнительные функции в списках. Просто используйте[i*i*i for i in range(1,11)]
Сяо
2
Вы абсолютно правы. Я сохранил функцию в примерах понимания списка, чтобы она выглядела аналогично примерам фильтра / карты.
Джошуа Д. Бойд
5
Я ** 3 также эквивалентен я * я * я
Бризер
5
@Breezer на самом деле i**3позвонит i.__pow__(3)и i*i*i i.__mul__(i).__mul__(i)(или что-то в этом роде). Для ints это не имеет значения, но для числовых / пользовательских классов это может даже привести к другим результатам.
синтоним
1
Я заметил, что всякий раз, когда мы слышим, что «Гвидо принял решение X», эта боль является вероятным исходом. Это отличный пример: list(list(list(.. )))сделать то, что уже было многословно в python.
Джавадба
12

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

Быстрая реализация может выглядеть так:

from contextlib import contextmanager    

@contextmanager
def noiters(*funcs):
    if not funcs: 
        funcs = [map, filter, zip] # etc
    from functools import reduce
    globals()[reduce.__name__] = reduce
    for func in funcs:
        globals()[func.__name__] = lambda *ar, func = func, **kwar: list(func(*ar, **kwar))
    try:
        yield
    finally:
        del globals()[reduce.__name__]
        for func in funcs: globals()[func.__name__] = func

С использованием, которое выглядит так:

with noiters(map):
    from operator import add
    print(reduce(add, range(1, 20)))
    print(map(int, ['1', '2']))

Какие отпечатки:

190
[1, 2]

Просто мои 2 цента :-)

Димитрис Фасаракис Хиллиард
источник
1
pythonкак язык беспорядок - но она имеет V хорошей до отличной библиотеки: numpy, pandas, statsmodelsи друзей .. Я был Buliding библиотеки удобства , как вы показать здесь , чтобы уменьшить боль родного языка - но потеряли энергию и стараться не уходить далеко от data.frame/ datatableили xarray. Но слава за попытку ..
Джавадба
7

Поскольку reduceметод был удален из встроенной функции из Python3, не забудьте импортировать его functoolsв свой код. Пожалуйста, посмотрите на фрагмент кода ниже.

import functools
my_list = [10,15,20,25,35]
sum_numbers = functools.reduce(lambda x ,y : x+y , my_list)
print(sum_numbers)
Бикаш Сингх
источник
2

Вот примеры функций фильтра, отображения и сокращения.

числа = [10,11,12,22,34,43,54,34,67,87,88,98,99,87,44,66]

//Фильтр

oddNumbers = list (фильтр (лямбда x: x% 2! = 0, числа))

печать (oddNumbers)

//Карта

multiplyOf2 = список (карта (лямбда х: х * 2, цифры))

печать (multiplyOf2)

// Уменьшить

Функция Reduce, поскольку она обычно не используется, была удалена из встроенных функций в Python 3. Она по-прежнему доступна в модуле functools, поэтому вы можете сделать:

от импорта functools

sumOfNumbers = уменьшить (лямбда х, у: х + у, числа)

печать (sumOfNumbers)

Йогендра Сингх
источник
0

Одно из преимуществ сопоставления, фильтрации и уменьшения состоит в том, насколько разборчивыми они становятся, когда вы объединяете их в цепочки, чтобы сделать что-то сложное. Тем не менее, встроенный синтаксис не читается и все "назад". Итак, я предлагаю использовать PyFunctionalпакет ( https://pypi.org/project/PyFunctional/ ). Вот сравнение двух:

flight_destinations_dict = {'NY': {'London', 'Rome'}, 'Berlin': {'NY'}}

PyFunctional версия

Очень разборчивый синтаксис. Ты можешь сказать:

«У меня есть последовательность пунктов назначения рейсов. Из которых я хочу получить ключ dict, если город находится в значениях dict. Наконец, отфильтруйте пустые списки, которые я создал в процессе».

from functional import seq  # PyFunctional package to allow easier syntax

def find_return_flights_PYFUNCTIONAL_SYNTAX(city, flight_destinations_dict):
    return seq(flight_destinations_dict.items()) \
        .map(lambda x: x[0] if city in x[1] else []) \
        .filter(lambda x: x != []) \

Версия Python по умолчанию

Это все задом наперед. Вы должны сказать:

«Хорошо, итак, есть список. Я хочу отфильтровать из него пустые списки. Почему? Потому что я впервые получил ключ dict, если город был в значениях dict. О, список, для которого я делаю это, - flight_destination_dict. "

def find_return_flights_DEFAULT_SYNTAX(city, flight_destinations_dict):
    return list(
        filter(lambda x: x != [],
               map(lambda x: x[0] if city in x[1] else [], flight_destinations_dict.items())
               )
    )
Даниил
источник