Python sąrašo suvokimas Vs. kortelę

Ar yra priežastis pasirinkti map() kad suprastumėte sąrašą ar atvirkščiai? Ar kiekvienas iš jų yra veiksmingesnis ar labiau vertinamas kaip kitas?

551
08 авг. nustatė TimothyAWiseman 08 rug . 2009-08-08 02:43 '09 at 2:43 am 2009-08-08 02:43
@ 10 atsakymų

Kartais map kai kuriais atvejais gali būti mikroskopiškai greitesnis (kai NEGALITE šiam tikslui lambda, bet naudojant tą pačią funkciją žemėlapyje ir sąraše). Apskaitos sąrašai gali būti greitesni kitais atvejais, ir dauguma (ne visų) pythonistas juos laiko tiesioginiu ir suprantamesniu.

Mažos kortelės pranašumo pavyzdys, kai naudojate tą pačią funkciją:

 $ python -mtimeit -s'xs=range(10)' 'map(hex, xs)' 100000 loops, best of 3: 4.86 usec per loop $ python -mtimeit -s'xs=range(10)' '[hex(x) for x in xs]' 100000 loops, best of 3: 5.58 usec per loop 

Pavyzdys, kaip veiklos rezultatų palyginimas visiškai atšaukiamas, kai kortelei reikia lambda:

 $ python -mtimeit -s'xs=range(10)' 'map(lambda x: x+2, xs)' 100000 loops, best of 3: 4.24 usec per loop $ python -mtimeit -s'xs=range(10)' '[x+2 for x in xs]' 100000 loops, best of 3: 2.32 usec per loop 
513
08 авг. Atsakymas, kurį pateikė Alex Martelli 08 Aug 2009-08-08 02:45 '09, 02:45 am 2009-08-08 02:45

atvejais

  • Bendras atvejis : jums beveik visada reikia naudoti sąrašų supratimą „python“, nes bus aiškiau, ką darote, kad naujokai programuotojai galėtų skaityti jūsų kodą. (Tai netaikoma kitoms kalboms, kuriose gali būti naudojamos kitos idėjos.) Dar akivaizdesnis yra tai, ką darote su python programuotojais, nes iteracijos pythono pagrindas yra de facto principas; jie yra tikėtini.
  • Mažiau paplitęs atvejis . Tačiau, jei jau turite tam tikrą funkciją, dažnai išmintinga naudoti map , nors jis laikomas „ne pythoniniu“. Pavyzdžiui, map(sum, myLists) yra elegantiškesnis / trumpesnis už [sum(x) for x in myLists] . Jūs gaunate eleganciją, kuri neturėtų būti manekeno kintamasis (pavyzdžiui, sum(x) for x... arba sum(_) for _... arba sum(readableName) for readableName... ), kurią turite įvesti du kartus, tiesiog kartoti. Tas pats argumentas yra susijęs su filter ir reduce ir vienu iš itertools modulio: jei jau turite funkciją, galite tęsti ir atlikti tam tikrą funkcinį programavimą. Tai pagerina skaitymą tam tikrose situacijose ir praranda jį kitose (pvz., Naujokų programuotojai, keletas argumentų) ... bet kodo skaitymas labai priklauso nuo jūsų komentarų.
  • Beveik niekada : galite naudoti map kaip gryną abstrakčią funkciją atlikdami funkcinį programavimą, kai įvedate map arba kario map , arba kitaip turite galimybę kalbėti apie map kaip funkciją. Pavyzdžiui, fmap funktoriaus sąsaja, vadinama fmap apibendrina ekraną bet kurios duomenų struktūros atžvilgiu. Tai labai reti pythone, nes python gramatika verčia jus naudoti generatoriaus stilių kalbėti apie iteraciją; negalite lengvai apibendrinti. (Kartais tai yra gera ir kartais bloga.) Galbūt galite rasti retų pythono pavyzdžių, kur map(f, *lists) yra pagrįstas dalykas. Artimiausias pavyzdys, kurį galiu pateikti, yra sumEach = partial(map,sum) , tai yra viena eilutė, kuri yra labai maždaug lygi:

 def sumEach(myLists): return [sum(_) for _ in myLists] 
  • Tiesiog naudokite for -loop“ : taip pat, žinoma, galite naudoti tik „-loop“. Nors funkcinis programavimas nėra toks elegantiškas, kartais net vietiniai kintamieji kodą daro aiškesnes neišvengiamose programavimo kalbose, pvz., „Python“, nes tokiu būdu žmonės yra labai įpratę skaityti kodą. Be „slydimo“, taip pat, kaip taisyklė, yra efektyviausios, kai paprasčiausiai atliekate sudėtingą operaciją, kuri nesukuria sąrašo, pvz., Sąrašų ir žemėlapių, optimizuotų (pvz., Sumodeliuoti ar sukurti medį ir tt) - pagal mažiausiai veiksmingas atminties požiūriu (nebūtinai laiko atžvilgiu, kur blogiausiu atveju tikiuosi, kad tai yra pastovus veiksnys, išskyrus kai kuriuos retus patologinius šiukšlių rinkinius).

„Pitonizmas“

Man nepatinka žodis „pythonic“, nes nematau, kad mano akyse jis visada yra elegantiškas. Tačiau map ir filter bei panašių funkcijų (pvz., Labai naudingo itertools modulio) stilius gali būti laikomas ne pythonistiniu.

Laziness

Kalbant apie efektyvumą, kaip ir dauguma funkcinių programinės įrangos konstrukcijų, MAP gali būti LASINO ir iš tikrųjų yra tingus pythone . Tai reiškia, kad galite tai padaryti („python3“), o kompiuteris nepasibaigs atminties ir neteks visų išsaugotų duomenų:

 >>> map(str, range(10**100)) <map object at 0x2201d50> 

Pabandykite tai padaryti su sąrašu:

 >>> [str(n) for n in range(10**100)] # DO NOT TRY THIS AT HOME OR YOU WILL BE SAD # 

Atkreipkite dėmesį, kad supratimo sąrašai iš esmės yra tingūs, bet pythonas nusprendė juos įgyvendinti kaip ne tingus. Tačiau „python“ palaiko tingus sąrašus kaip tokias generatoriaus išraiškas:

 >>> (str(n) for n in range(10**100)) <generator object <genexpr> at 0xacbdef> 

Iš esmės galite galvoti apie sintaksę [...] , kuri perduoda generatoriaus išraišką sąrašo konstruktoriuje, pavyzdžiui, list(x for x in range(5)) .

Trumpai tariamas pavyzdys

 from operator import neg print({x:x**2 for x in map(neg,range(5))}) print({x:x**2 for x in [-y for y in range(5)]}) print({x:x**2 for x in (-y for y in range(5))}) 

Sąrašų sąrašai nėra tingūs, todėl gali prireikti daugiau atminties (jei nenaudojate generavimo galimybių). Kvadratiniai skliaustai [...] dažnai tampa akivaizdūs, ypač skliausteliuose. Kita vertus, kartais jūs esate verbozė, pavyzdžiui, įveskite [x for x in... tol, kol išlaikysite iteratoriaus kintamuosius trumpus, supratimo sąrašai paprastai yra aiškesni, nebent grįžtumėte iš kodo. Bet visada galite nukrypti nuo savo kodo.

 print( {x:x**2 for x in (-y for y in range(5))} ) 

arba pertraukos:

 rangeNeg5 = (-y for y in range(5)) print( {x:x**2 for x in rangeNeg5} ) 

Python3 našumo palyginimas

map dabar map tingus:

 % python3 -mtimeit -s 'xs=range(1000)' 'f=lambda x:x' 'z=map(f,xs)' 1000000 loops, best of 3: 0.336 usec per loop ^^^^^^^^^ 

Todėl, jei nenaudojate visų savo duomenų arba iš anksto nežinote, kiek duomenų jums reikia, map python3 (ir generatoriaus išraiškos python2 arba python3) leis jums išvengti jų verčių skaičiavimo iki paskutinio momento. Paprastai tai paprastai viršija bet kokią pridėtinę vertę, kai naudojate map . Trūkumas yra tas, kad jis yra labai ribotas pythone, skirtingai nuo daugelio funkcinių kalbų: jūs gaunate tik šį pranašumą, jei prieigą prie savo duomenų iš kairės į dešinę „tvarkingai“, nes python generatoriaus išraiškas gali įvertinti tik užsakymas x[0], x[1], x[2],...

Tačiau mes sakome, kad turime iš anksto sukurtą funkciją f norėtume map , ir ignoruojame map tingumą, nedelsdami priversti reitingą iš list(...) . Mes gauname labai įdomių rezultatų:

 % python3 -mtimeit -s 'xs=range(1000)' 'f=lambda x:x' 'z=list(map(f,xs))' 10000 loops, best of 3: 165/124/135 usec per loop ^^^^^^^^^^^^^^^ for list(<map object>) % python3 -mtimeit -s 'xs=range(1000)' 'f=lambda x:x' 'z=[f(x) for x in xs]' 10000 loops, best of 3: 181/118/123 usec per loop ^^^^^^^^^^^^^^^^^^ for list(<generator>), probably optimized % python3 -mtimeit -s 'xs=range(1000)' 'f=lambda x:x' 'z=list(f(x) for x in xs)' 1000 loops, best of 3: 215/150/150 usec per loop ^^^^^^^^^^^^^^^^^^^^^^ for list(<generator>) 

Rezultatai pateikiami kaip AAA / BBB / CCC, kur A buvo atlikta su „Intel“ darbo stotimi 2010 m., Naudojant „Python 3.“, „B“ ir „C“ buvo atlikti su AMD darbo stotimi iki 2013 m. , su labai skirtinga įranga. Panašu, kad žemėlapių ir sąrašų palyginimas yra panašus į rezultatus, kuriuos labiausiai įtakoja kiti atsitiktiniai veiksniai. Vienintelis dalykas, kurį galime pasakyti, yra tai, kad keistai, nors tikimės, kad sąrašų [...] interpretacija veiks geriau nei generatoriaus išraiškos (...) , map efektyvesnis išraiškoms generatorius (dar kartą, darant prielaidą, kad visos vertės yra įvertintos / naudojamos).

Svarbu suprasti, kad šie testai rodo labai paprastą funkciją (tapatybės funkciją); tačiau tai yra gerai, nes jei funkcija buvo sudėtinga, tuomet našumo pridėtinės vertės būtų nereikšmingos, palyginti su kitais programos veiksniais. (Gali būti įdomu patikrinti kitus paprastus dalykus, pvz., f=lambda x:x+x )

Jei galite perskaityti „python build“, galite naudoti dis modulį, kad sužinotumėte, ar tai tikrai vyksta užkulisiuose:

 >>> listComp = compile('[f(x) for x in xs]', 'listComp', 'eval') >>> dis.dis(listComp) 1 0 LOAD_CONST 0 (<code object <listcomp> at 0x2511a48, file "listComp", line 1>) 3 MAKE_FUNCTION 0 6 LOAD_NAME 0 (xs) 9 GET_ITER 10 CALL_FUNCTION 1 13 RETURN_VALUE >>> listComp.co_consts (<code object <listcomp> at 0x2511a48, file "listComp", line 1>,) >>> dis.dis(listComp.co_consts[0]) 1 0 BUILD_LIST 0 3 LOAD_FAST 0 (.0) >> 6 FOR_ITER 18 (to 27) 9 STORE_FAST 1 (x) 12 LOAD_GLOBAL 0 (f) 15 LOAD_FAST 1 (x) 18 CALL_FUNCTION 1 21 LIST_APPEND 2 24 JUMP_ABSOLUTE 6 >> 27 RETURN_VALUE 

 >>> listComp2 = compile('list(f(x) for x in xs)', 'listComp2', 'eval') >>> dis.dis(listComp2) 1 0 LOAD_NAME 0 (list) 3 LOAD_CONST 0 (<code object <genexpr> at 0x255bc68, file "listComp2", line 1>) 6 MAKE_FUNCTION 0 9 LOAD_NAME 1 (xs) 12 GET_ITER 13 CALL_FUNCTION 1 16 CALL_FUNCTION 1 19 RETURN_VALUE >>> listComp2.co_consts (<code object <genexpr> at 0x255bc68, file "listComp2", line 1>,) >>> dis.dis(listComp2.co_consts[0]) 1 0 LOAD_FAST 0 (.0) >> 3 FOR_ITER 17 (to 23) 6 STORE_FAST 1 (x) 9 LOAD_GLOBAL 0 (f) 12 LOAD_FAST 1 (x) 15 CALL_FUNCTION 1 18 YIELD_VALUE 19 POP_TOP 20 JUMP_ABSOLUTE 3 >> 23 LOAD_CONST 0 (None) 26 RETURN_VALUE 

 >>> evalledMap = compile('list(map(f,xs))', 'evalledMap', 'eval') >>> dis.dis(evalledMap) 1 0 LOAD_NAME 0 (list) 3 LOAD_NAME 1 (map) 6 LOAD_NAME 2 (f) 9 LOAD_NAME 3 (xs) 12 CALL_FUNCTION 2 15 CALL_FUNCTION 1 18 RETURN_VALUE 

Atrodo, geriau naudoti sintaksę [...] nei list(...) . Deja, map klasė yra šiek tiek nepermatoma išardymui, tačiau mes galime tai padaryti su greičio testu.

366
20 июня '11 в 8:41 2011-06-20 08:41 atsakymas pateikiamas ninjagecko birželio 20 d. 11 val. 8:41 2011-06-20 08:41

Užuot supratę sąrašą, turėtumėte naudoti map ir filter .

Tikslas yra tikslas , kodėl jums tai patinka, net jei jie nėra „Pythonic“, tai: Jie reikalauja, kad / lambdas veiktų kaip argumentai, įvedantys naują sritį .

Man buvo įkūnyta daugiau nei vieną kartą:

 for x, y in somePoints: # (several lines of code here) squared = [x ** 2 for x in numbers] # Oops, x was silently overwritten! 

bet jei sakiau

 for x, y in somePoints: # (several lines of code here) squared = map(lambda x: x ** 2, numbers) 

tada viskas būtų gerai.

Galima sakyti, kad buvau kvailas naudoti tą patį kintamąjį tame pačiame lauke.

Aš ne. Iš pradžių kodas buvo puikus - du x nebuvo toje pačioje srityje.
Tik po to, kai perkėliau vidinį bloką į kitą kodo, kuriame iškilo problema, skyrių (skaitykite: problema išlaikymo metu, o ne vystymosi metu), ir aš to nesitikėjau.

Taip, jei jūs niekada nepadarysite šios klaidos, tada sąvokų sąrašas yra elegantiškesnis. Bet iš asmeninės patirties (ir matydamas, kad kiti daro tą pačią klaidą), aš mačiau, kad taip atsitiko gana daug kartų, ir manau, kad verta neužmiršti, kai šios klaidos prasiskverbia į jūsų kodą.

Išvada:

Naudokite map ir filter . Jie apsaugo nuo plonų sunkių diagnostikos klaidų, susijusių su vietove.

Pastaba:

Nepamirškite apsvarstyti galimybę naudoti „ imap ir „ ifilter („ itertools ), jei jie tinka jūsų situacijai!

78
21 нояб. atsakymą pateikė Mehrdad, lapkričio 21 d. 2012-11-21 01:28 '12 at 1:28 2012-11-21 01:28

Faktiškai „Python 3“ map ir skaičiavimų vaizdai visiškai kitaip elgiasi. Pažvelkite į šią Python 3 programą:

 def square(x): return x*x squares = map(square, [1, 2, 3]) print(list(squares)) print(list(squares)) 

Jūs galite tikėtis, kad jis du kartus įrašys eilutę „[1, 4, 9]“, o vietoj to įveskite „[1, 4, 9]“ ir tada „[]“. Kai pirmą kartą žiūrite squares , atrodo, kad jis elgiasi kaip trijų elementų seka, o antrą kartą - tuščias.

„Python 2“ map grąžina paprastą seną sąrašą, kaip ir skirtingomis kalbomis. Apatinė eilutė yra ta, kad map grąžinimo vertė Python 3 (ir imap į Python 2) nėra sąrašas - tai iteratorius!

Elementai suvartojami, kai kartojate per iteratorių, skirtingai nuo to, kai kartojate per sąrašą. Štai kodėl squares paskutinėje print(list(squares)) eilutėje print(list(squares)) atrodo tuščiai.

Apibendrinant:

  • Dirbdami su iteratoriais, turite prisiminti, kad jie yra valstybės ir kad jie persiunčia juos.
  • Sąrašai yra labiau nuspėjami, nes jie keičiasi tik tada, kai jie yra mutuoti; jie yra konteineriai.
  • Be to, premija: skaičiai, stygos ir eilutės yra dar labiau nuspėjami, nes jie visai negali keistis; jie yra reikšmės.
35
01 окт. atsakymas pateikiamas raek 01 okt. 2013-10-01 16:09 '13, 16:09, 2013-10-01 16:09

Manau, kad supratimo sąrašai paprastai labiau išreiškia tai, ką bandau daryti, nei map - jie abu tai daro, bet pirmasis išsaugo psichinę apkrovą, bandydamas išsiaiškinti, kas gali būti sudėtinga lambda išraiška.

Taip pat kažkur vyksta interviu (aš negaliu jo išgirsti), kur Guido užrašo lambda , o funkcinės funkcijos yra tai, ką jis labiausiai apgailestavo priimdamas Python'e, todėl galite pateikti argumentą, kad jie nėra -Pythonic.

15
08 авг. Atsakyti Dan 08 rug. 2009-08-08 02:59 '09, 02:59 am. 2009-08-08 02:59

Jei planuojate rašyti bet kokį asinchroninį, lygiagretų ar paskirstytą kodą, tikriausiai pageidaujate map sąrašo įžvalgos, nes dauguma asinchroninių, lygiagrečių ar paskirstytų paketų suteikia map funkciją python map perkrovimui. Tada, nuvažiavus atitinkamą map funkciją į likusį savo kodą, gali tekti pakeisti originalų serijos kodą taip, kad jis veiktų lygiagrečiai (ir pan.).

13
08 июня '14 в 20:03 2014-06-08 20:03 Atsakymą pateikė Mike McKerns birželio 8 d. 14 d. 20:03 2014-06-08 20:03

Čia yra vienas iš galimų atvejų:

 map(lambda op1,op2: op1*op2, list1, list2) 

prieš

 [op1*op2 for op1,op2 in zip(list1,list2)] 

Manau, kad „zip“ () yra gaila ir nereikalinga pridėtinė vertė, kurią reikia leisti, jei norite naudoti sąrašus vietoj žemėlapio. Būtų puiku, jei tai paaiškintų, teigiamai ar neigiamai.

13
02 нояб. Atsakymas pateikiamas Andz 02 lapkričio. 2009-11-02 11:42 '09, 11:42 2009-11-02 11:42

Kita priežastis naudoti sąrašo supratimą per žemėlapį () ir filtrą () yra ta, kad „Psyco“ negali sukompiliuoti šių funkcijų.

Žr. Http://psyco.sourceforge.net/

6
22 февр. Breto atsakymas vasario 22 d 2010-02-22 00:27 '10 ne 0:27 2010-02-22 00:27

Taigi, kadangi „Python 3“, map() yra iteratorius, reikia nepamiršti, ko jums reikia: iteratorių arba list .

Kaip jau minėta, „AlexMartelli“, map() greitesnis nei sąrašo supratimas, tik jei nenaudojate lambda funkcijos.

Aš jums parodysiu šiek tiek laiko palyginimus.

<sub> Python 3.5.2 ir CPython
Aš naudoju Jupiterio kompiuterį ir ypač %timeit integruotą magiją
Matavimai : s == 1000 ms == 1000 * 1000 µs = 1000 * 1000 * 1000 ns

Sąranka:

 x_list = [(i, i+1, i+2, i*2, i-9) for i in range(1000)] i_list = list(range(1000)) 

Integruota funkcija:

 %timeit map(sum, x_list) # creating iterator object # Output: The slowest run took 9.91 times longer than the fastest. # This could mean that an intermediate result is being cached. # 1000000 loops, best of 3: 277 ns per loop %timeit list(map(sum, x_list)) # creating list with map # Output: 1000 loops, best of 3: 214 µs per loop %timeit [sum(x) for x in x_list] # creating list with list comprehension # Output: 1000 loops, best of 3: 290 µs per loop 

lambda :

 %timeit map(lambda i: i+1, i_list) # Output: The slowest run took 8.64 times longer than the fastest. # This could mean that an intermediate result is being cached. # 1000000 loops, best of 3: 325 ns per loop %timeit list(map(lambda i: i+1, i_list)) # Output: 1000 loops, best of 3: 183 µs per loop %timeit [i+1 for i in i_list] # Output: 10000 loops, best of 3: 84.2 µs per loop 

Taip pat yra toks dalykas, kaip generatoriaus išraiška, žr. PEP-0289 . Taigi maniau, kad būtų naudinga ją pridėti prie palyginimo.

 %timeit (sum(i) for i in x_list) # Output: The slowest run took 6.66 times longer than the fastest. # This could mean that an intermediate result is being cached. # 1000000 loops, best of 3: 495 ns per loop %timeit list((sum(x) for x in x_list)) # Output: 1000 loops, best of 3: 319 µs per loop %timeit (i+1 for i in i_list) # Output: The slowest run took 6.83 times longer than the fastest. # This could mean that an intermediate result is being cached. # 1000000 loops, best of 3: 506 ns per loop %timeit list((i+1 for i in i_list)) # Output: 10000 loops, best of 3: 125 µs per loop 

Jums reikia list objekto:

Naudokite sąrašą, jei tai yra pasirinktinė funkcija, naudokite list(map()) jei yra integruota funkcija

Jums nereikia list objekto, jums reikia tik kartotinio:

Visada naudokite map() !

5
03 дек. atsakymas pateiktas pagal vishes_shell 03 dec. 2016-12-03 17:18 '16 at 17:18 2016-12-03 17:18

Manau, kad labiausiai „Pythonic“ kelias yra naudoti sąrašą, o ne map ir filter . Taip yra todėl, kad supratimo sąrašai yra aiškesni nei map ir filter .

 In [1]: odd_cubes = [x ** 3 for x in range(10) if x % 2 == 1] # using a list comprehension In [2]: odd_cubes_alt = list(map(lambda x: x ** 3, filter(lambda x: x % 2 == 1, range(10)))) # using map and filter In [3]: odd_cubes == odd_cubes_alt Out[3]: True 

Kaip matote, supratimas nereikalauja papildomų lambda išraiškų kaip map . Be to, supratimas taip pat leidžia lengvai filtruoti, o map reikia filter kuris leistų filtruoti.

0
04 сент. atsakymą pateikė lmiguelvargasf 04 sep . 2017-09-04 20:20 '17, 20:20 pm 2017-09-04 20:20