Acest post este inclus în buclă pe baza traducerilor de întrebări / răspunsuri populare din stackoverflow.
Când vedeți o funcție cu cuvântul cheie de randament în interior, aplicați trucul descris mai jos și veți înțelege imediat cum funcționează această funcție.
- 1. Mai întâi adăugați o linie la funcția: result = [].
- 2. Înlocuiți fiecare apel de randament cu result.append (expr).
- 3. Adăugați rezultatul întoarcerii la sfârșitul definiției funcției.
- 4. Hooray! Acum nu există apel de randament - acum este ușor să înțelegeți ce are funcția.
- 5 / Înțeles? Returnați definiția funcției în starea inițială.
Acest truc vă va ajuta să înțelegeți ideea din spatele funcției, precum și să expuneți în mod clar logica implementării acesteia. Cu o singură diferență: ceea ce se întâmplă atunci când utilizați randamentul este foarte diferit de modul în care utilizați lista. De multe ori se apropie cu ajutorul generatorului (randament face ca generator de funcții normale) va fi mai puțin lacomi: utilizarea memoriei este mai mică și viteza de operare este mai mare. Și uneori un truc poate introduce o execuție a unei funcții într-o buclă infinită, chiar dacă funcția inițială funcționează așa cum ar trebui. Citiți mai departe pentru a afla mai multe
Distingeți între obiectele iterate, iteratorii și generatoarele iterate
În primul rând, vom aborda interfața iterator. Când scrieți:
Python ia următoarele două etape:
Obține obiectul iterator pentru lista de erori.
Apelarea iter (mylist) returnează un obiect cu metoda next () (sau __next __ () pentru Py3).
Utilizează iteratorul care rezultă pentru a trece prin elementele listă. Apoi pentru x în mylist. în mod succesiv, a apelat metoda .next () obținută în prima etapă a iteratorului, iar valoarea pe care returnează .next () este atribuită la x. Dacă lista se termină, .next () aruncă o excepție StopIteration.
De fapt, Python face pașii de mai sus de fiecare dată când este necesar pentru a trece succesiv prin conținutul obiectului: modul de utilizare pentru ciclul, și sub formă de cod therlist.extend (mylist)
Obiecte iterate
Mylistul nostru este un obiect iterabil care implementează protocolul iterator. Dacă trebuie să faceți iterațiile instanței iterate în oricare dintre clasele dvs., atunci trebuie doar să implementați metoda __iter __ () în ea. Această metodă trebuie să returneze un iterator. Un iterator este un obiect cu metoda .next ().
Împreună cu __ __iter () puteți defini pentru clasa si metoda .Etapele (), apoi __iter __ () va returna sine. Este adevărat că această opțiune este potrivită doar pentru cazuri simple. Pentru cele mai complexe, în cazul în care sunt necesare mai multe iteratoare pentru a traversa membrii aceluiași obiect, acesta nu mai funcționează.
În general, acesta este protocolul iteratorului. Multe obiecte Python o implementează:
- Liste încorporate, dicționare, tupluri, seturi, fișiere
- Clase personalizate care implementează metoda __iter __ ()
- generatoare
Ciclul pentru nu știe cu care interacționează obiectul. Bucla urmează pur și simplu protocolul iterator și ia cu bucurie elementul după element cu fiecare apel nou. Listele returnează secvențial un element după un element, dicționarele dau succesiv chei, fișiere - linii de linie. Și generatoare. Ei bine, aici vine randamentul.
Dacă am scris întoarcere de trei ori întoarcerea, atunci f123 () va numi prima întoarcere întâlnită și funcția își va termina lucrarea. Cu toate acestea, deoarece am folosit randamentul, funcția devine neobișnuită și atunci când interpretul ajunge la randament, obiectul returnează un generator (mai degrabă decât valoarea calculată) - atunci funcția intră într-o stare suspendată. Când, de exemplu, atunci când un obiect generator este trecut în buclă for, f123 () reia lucrarea, execută până la următorul randament întâlnit și returnează următoarea valoare. Aceasta se produce până când buclele se termină deoarece generatorul aruncă o excepție StopIteration.
Astfel, obiectul generator de arata ca un fel de adaptor: pe de o parte implementează protocolul iterator, definind metode __iter __ () și următoare (), pe de altă parte, ea îndeplinește funcția exact la fel de mult ca este necesar pentru a produce valoarea următoare, apoi suspendează.
De ce să folosiți generatoare?
Puteți scrie codul care nu utilizează generatoare, dar implementează aceeași logică. De exemplu, folosind trucul cu lista temporară descrisă la începutul articolului. Acest truc nu va funcționa în cazuri cu bucle infinite, liste prea mari (ceea ce duce la consumul excesiv de memorie)
O altă abordare este de a scrie o anumită clasă de personalizat, care stochează starea între apeluri în copii lor și le vinde .Etapele metoda () (sau __next __ () în Py3). În funcție de algoritmul, codul în .Etapele metoda () poate fi foarte complex, care implică bug-uri ascunse și efecte secundare. Aici, generatoarele vor străluci, oferind o soluție simplă.