Het gebruik van filter, kaart en reducering in Python 3

filter, map en reduce werken perfect in Python 2. Hier is een voorbeeld:

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

Maar in Python 3 ontvang ik de volgende uitvoer:

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

Ik zou het op prijs stellen als iemand me zou kunnen uitleggen waarom dit zo is.

Screenshot van code voor meer duidelijkheid:

 IDLE-sessies van Python 2 en 3 naast elkaar


Antwoord 1, autoriteit 100%

Je kunt over de wijzigingen lezen in Wat is er nieuw in Python 3.0. Je moet het grondig lezen als je van 2.x naar 3.x gaat, aangezien er veel is veranderd.

Het hele antwoord hier zijn citaten uit de documentatie.

Views en iterators In plaats van lijsten

Sommige bekende API’s retourneren geen lijsten meer:

  • […]
  • map() en filter() retourneren iterators. Als je echt een lijst nodig hebt, is een snelle oplossing b.v. list(map(...)), maar een betere oplossing is vaak om een ​​lijstbegrip te gebruiken (vooral wanneer de originele code lambda gebruikt), of de code te herschrijven zodat er geen lijst nodig is helemaal niet. Bijzonder lastig is dat map() wordt aangeroepen voor de bijwerkingen van de functie; de juiste transformatie is om een ​​normale for-lus te gebruiken (aangezien het maken van een lijst gewoon verspilling zou zijn).
  • […]

Gebouwen

  • […]
  • Verwijderd reduce(). Gebruik functools.reduce() als je het echt nodig hebt; 99 procent van de tijd is een expliciete for-lus echter beter leesbaar.
  • […]

Antwoord 2, autoriteit 25%

De functionaliteit van map en filter is met opzet gewijzigd om iterators terug te geven, en reduce is als ingebouwde functie verwijderd en in functools.reduce.

Dus, voor filter en map kun je ze omsluiten met list() om de resultaten te zien zoals je eerder deed.

p>

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

De aanbeveling is nu dat u uw gebruik van kaart en filter vervangt door generatorexpressies of lijstbegrippen. Voorbeeld:

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

Ze zeggen dat for-loops 99 procent van de tijd gemakkelijker te lezen zijn dan te verkleinen, maar ik zou het gewoon bij functools.reduce houden.

Bewerken: het cijfer van 99 procent is rechtstreeks afkomstig uit de titel Wat is er nieuw in Python 3.0 pagina geschreven door Guido van Rossum.


Antwoord 3, autoriteit 3%

Als aanvulling op de andere antwoorden, klinkt dit als een prima use-case voor een contextmanager die de namen van deze functies opnieuw toewijst aan functies die een lijst retourneren en reduce introduceren in de globale naamruimte.

Een snelle implementatie kan er als volgt uitzien:

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

Met een gebruik dat er als volgt uitziet:

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

Welke afdrukken:

190
[1, 2]

Alleen mijn 2 cent 🙂


Antwoord 4, autoriteit 2%

Aangezien de reduce methode is verwijderd uit de ingebouwde functie van Python3, vergeet dan niet om de functools in je code te importeren. Bekijk het onderstaande codefragment.

import functools
my_list = [10,15,20,25,35]
sum_numbers = functools.reduce(lambda x ,y : x+y , my_list)
print(sum_numbers)

Antwoord 5

Een van de voordelen van in kaart brengen, filteren en verkleinen is hoe leesbaar ze worden als je ze aan elkaar ‘ketent’ om iets complexs te doen. De ingebouwde syntaxis is echter niet leesbaar en is allemaal “achterwaarts”. Ik raad dus aan om het pakket PyFunctional te gebruiken (https://pypi.org/project /PyFunctioneel/).
Hier is een vergelijking van de twee:

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

PyFunctionele versie

Zeer leesbare syntaxis. Je kunt zeggen:

“Ik heb een reeks vluchtbestemmingen. Waarvan ik wil komen
de dict-sleutel als de stad in de dict-waarden staat. Filter tot slot de
lege lijsten die ik daarbij heb gemaakt.”

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 != []) \

Standaard Python-versie

Het is allemaal achterstevoren. Je moet zeggen:

“OK, er is dus een lijst. Ik wil er lege lijsten uit filteren. Waarom?
Omdat ik eerst de dict-sleutel kreeg als de stad in de dict-waarden was.
Oh, de lijst waar ik dit mee doe is flight_destinations_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())
               )
    )

Antwoord 6

Hier zijn voorbeelden van functies voor filteren, toewijzen en verkleinen.

getallen = [10,11,12,22,34,43,54,34,67,87,88,98,99,87,44,66]

//Filter

oddNumbers = lijst(filter(lambda x: x%2 != 0, getallen))

print(oddNumbers)

//Kaart

multiplyOf2 = lijst(kaart(lambda x: x*2, getallen))

print(multiplyOf2)

//Verminderen

De reduceerfunctie, aangezien deze niet vaak wordt gebruikt, is verwijderd uit de ingebouwde functies in Python 3. Deze is nog steeds beschikbaar in de functools-module, dus u kunt het volgende doen:

van functools import verminderen

sumOfNumbers = reduce(lambda x,y: x+y, numbers)

print(sumOfNumbers)


Antwoord 7

from functools import reduce
def f(x):
    return x % 2 != 0 and x % 3 != 0
print(*filter(f, range(2, 25)))
#[5, 7, 11, 13, 17, 19, 23]
def cube(x):
    return x**3
print(*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

Het werkt zoals het is. Gebruik * of lijst om de uitvoer van de kaart te krijgen


Antwoord 8

Lambda

Probeer het verschil te begrijpen tussen een normaal gedefinieerde functie en een lambda-functie. Dit is een programma dat de kubus van een gegeven waarde retourneert:

# Python code to illustrate cube of a number 
# showing difference between def() and lambda(). 
def cube(y): 
    return y*y*y 
lambda_cube = lambda y: y*y*y 
# using the normally 
# defined function 
print(cube(5)) 
# using the lamda function 
print(lambda_cube(5)) 

uitvoer:

125
125

Zonder Lambda te gebruiken:

  • Hier geven ze allebei de kubus van een bepaald getal terug. Maar terwijl we def gebruikten, moesten we een functie definiëren met een naamkubus en moesten we er een waarde aan doorgeven. Na uitvoering moesten we ook het resultaat retourneren van waaruit de functie werd aangeroepen met behulp van het return-sleutelwoord.

Lambda gebruiken:

  • Lambda-definitie bevat geen return -statement, het bevat altijd een expressie die wordt geretourneerd. We kunnen ook overal een lambda-definitie plaatsen waar een functie wordt verwacht, en we hoeven deze helemaal niet aan een variabele toe te wijzen. Dit is de eenvoud van lambda-functies.

Lambda-functies kunnen worden gebruikt in combinatie met ingebouwde functies zoals filter(), map() en reduce().

lambda() met filter()

De functie filter() in Python neemt een functie en een lijst als argumenten op. Dit biedt een elegante manier om alle elementen van een reeks reeks uit te filteren, waarvoor de functie True retourneert.

my_list = [1, 5, 4, 6, 8, 11, 3, 12]
new_list = list(filter(lambda x: (x%2 == 0) , my_list))
print(new_list)
ages = [13, 90, 17, 59, 21, 60, 5]
adults = list(filter(lambda age: age>18, ages)) 
print(adults) # above 18 yrs 

uitvoer:

[4, 6, 8, 12]
[90, 59, 21, 60]

lambda() met kaart()

De functie map() in Python neemt een functie en een lijst als argument op. De functie wordt aangeroepen met een lambda-functie en een lijst en er wordt een nieuwe lijst geretourneerd die alle lambda-gemodificeerde items bevat die door die functie voor elk item worden geretourneerd.

my_list = [1, 5, 4, 6, 8, 11, 3, 12]
new_list = list(map(lambda x: x * 2 , my_list))
print(new_list)
cities = ['novi sad', 'ljubljana', 'london', 'new york', 'paris'] 
# change all city names 
# to upper case and return the same 
uppered_cities = list(map(lambda city: str.upper(city), cities)) 
print(uppered_cities)

uitvoer:

[2, 10, 8, 12, 16, 22, 6, 24]
['NOVI SAD', 'LJUBLJANA', 'LONDON', 'NEW YORK', 'PARIS']

verminderen

reduce() werkt anders dan map() en filter(). Het retourneert geen nieuwe lijst op basis van de function en iterabel die we hebben doorgegeven. In plaats daarvan retourneert het een enkele waarde.

Ook in Python 3 is reduce() geen ingebouwde functie meer, en deze is te vinden in de module functools.

De syntaxis is:

reduce(function, sequence[, initial])

reduce() werkt door de function aan te roepen die we hebben doorgegeven voor de eerste twee items in de reeks. Het resultaat dat wordt geretourneerd door de function wordt gebruikt in een andere aanroep van function samen met het volgende (derde in dit geval), element.

Het optionele argument initial wordt, indien aanwezig, gebruikt aan het begin van deze “lus” met het eerste element in de eerste aanroep van function. In zekere zin is het initial element het 0e element, vóór het eerste, indien verstrekt.

lambda() met reduce()

De functie reduce() in Python neemt een functie en een lijst als argument op. De functie wordt aangeroepen met een lambda-functie en een iterable en een nieuw gereduceerd resultaat wordt geretourneerd. Dit voert een herhalende bewerking uit over de paren van de iterabele.

from functools import reduce
my_list = [1, 1, 2, 3, 5, 8, 13, 21, 34] 
sum = reduce((lambda x, y: x + y), my_list) 
print(sum) # sum of a list
print("With an initial value: " + str(reduce(lambda x, y: x + y, my_list, 100)))
88
With an initial value: 188

Deze functies zijn gemaksfuncties. Ze zijn er zodat je omslachtige code kunt vermijden, maar vermijd het gebruik van zowel hen als lambda-expressies te veel, omdat “dat kan”, omdat het vaak kan leiden tot onleesbare code die moeilijk te onderhouden is. Gebruik ze alleen als het absoluut duidelijk is wat er aan de hand is zodra je naar de functie of lambda-expressie kijkt.

LEAVE A REPLY

Please enter your comment!
Please enter your name here

9 + three =

Other episodes