Python este un limbaj de programare general, versatil și puternic. Este o limbă maternă excelentă, deoarece este concisă și ușor de citit. Orice vrei să faci, Python o poate face. De la dezvoltarea web la învățarea automată până la știința datelor
Python este un limbaj de programare general, versatil și puternic. Este o limbă maternă excelentă, deoarece este concisă și ușor de citit. Orice vrei să faci, Python o poate face. De la dezvoltarea web la învățarea automată până la știința datelor
Structuri de date Acest capitol descrie unele lucruri pe care le-ați învățat deja în detaliu și adaugă și câteva lucruri noi.Mai multe despre Liste
Tipul de date listă are mai multe metode. Iată toate metodele de listă a obiectelor:
- listă. adăugați ( x )
-
Adăugați un element la sfârșitul listei. Echivalent cu o [len (a):] = [x] .
- listă. extinde ( L )
-
Extindeți lista adăugând toate elementele din lista dată. Echivalent cu un [len (a):] = L .
- listă. inserați ( i , x )
-
Introduceți un element într-o poziție dată. Primul argument este indexul elementului înaintea căruia se introduce, astfel încât a.insert (0, x) se inserează în partea din față a listei și a.insert (len (a), x) este echivalent cu a.append ( x) .
- listă. eliminați ( x )
-
Eliminați primul element din lista a cărei valoare este x . Este o eroare dacă nu există un astfel de element.
- listă. pop ( [ i ] )
-
Scoateți articolul din poziția dată din listă și returnați-l. Dacă nu este specificat niciun index, a.pop () elimină și returnează ultimul element din listă. (Parantezele pătrate în jurul valorii de i în semnătura metodei indică faptul că parametrul este opțional, nu că trebuie să introduceți paranteze pătrate în acea poziție. Veți vedea această notație frecvent în Python Library Reference.)
- listă. clar ( )
-
Eliminați toate elementele din listă. Echivalent cu del a [:] .
- listă. index ( x )
-
Returnați indexul în lista primului element a cărui valoare este x . Este o eroare dacă nu există un astfel de element.
- listă. număr ( x )
-
Reveniți de câte ori x apare în listă.
- listă. sort ( )
-
Sortați elementele din listă în loc.
- listă. invers ( )
-
Inversați elementele din listă în loc.
- listă. copie ( )
-
Returnați o copie superioară a listei. Echivalent cu un [:] .
Un exemplu care utilizează majoritatea metodelor listă:
S-ar putea să fi observat că metode cum ar fi inserarea , ștergerea sau sortarea care modifică lista nu au nici o valoare returnată imprimată - acestea returnează Niciuna . [1] Acesta este un principiu de proiectare pentru toate structurile de date mutabile din Python.
Tipul de date listă are mai multe metode. Iată toate metodele de listă a obiectelor:
- listă. adăugați ( x )
- Adăugați un element la sfârșitul listei. Echivalent cu o [len (a):] = [x] .
- listă. extinde ( L )
- Extindeți lista adăugând toate elementele din lista dată. Echivalent cu un [len (a):] = L .
- listă. inserați ( i , x )
- Introduceți un element într-o poziție dată. Primul argument este indexul elementului înaintea căruia se introduce, astfel încât a.insert (0, x) se inserează în partea din față a listei și a.insert (len (a), x) este echivalent cu a.append ( x) .
- listă. eliminați ( x )
- Eliminați primul element din lista a cărei valoare este x . Este o eroare dacă nu există un astfel de element.
- listă. pop ( [ i ] )
- Scoateți articolul din poziția dată din listă și returnați-l. Dacă nu este specificat niciun index, a.pop () elimină și returnează ultimul element din listă. (Parantezele pătrate în jurul valorii de i în semnătura metodei indică faptul că parametrul este opțional, nu că trebuie să introduceți paranteze pătrate în acea poziție. Veți vedea această notație frecvent în Python Library Reference.)
- listă. clar ( )
- Eliminați toate elementele din listă. Echivalent cu del a [:] .
- listă. index ( x )
- Returnați indexul în lista primului element a cărui valoare este x . Este o eroare dacă nu există un astfel de element.
- listă. număr ( x )
- Reveniți de câte ori x apare în listă.
- listă. sort ( )
- Sortați elementele din listă în loc.
- listă. invers ( )
- Inversați elementele din listă în loc.
- listă. copie ( )
- Returnați o copie superioară a listei. Echivalent cu un [:] .
Un exemplu care utilizează majoritatea metodelor listă:
S-ar putea să fi observat că metode cum ar fi inserarea , ștergerea sau sortarea care modifică lista nu au nici o valoare returnată imprimată - acestea returnează Niciuna . [1] Acesta este un principiu de proiectare pentru toate structurile de date mutabile din Python.
Ce să mai citim?
5.1.1. Utilizarea listelor ca stive
Metodele din listă fac foarte ușor utilizarea unei liste ca stiva, unde ultimul element adăugat este primul element preluat ("last-in, first-out"). Pentru a adăuga un element în partea de sus a stivei, utilizați atașați () . Pentru a prelua un element din partea de sus a stivei, folosiți pop () fără un index explicit. De exemplu:
Metodele din listă fac foarte ușor utilizarea unei liste ca stiva, unde ultimul element adăugat este primul element preluat ("last-in, first-out"). Pentru a adăuga un element în partea de sus a stivei, utilizați atașați () . Pentru a prelua un element din partea de sus a stivei, folosiți pop () fără un index explicit. De exemplu:
5.1.2. Utilizarea listelor ca Cozile
Este, de asemenea, posibilă utilizarea unei liste ca o coadă, în care primul element adăugat este primul element preluat ("first-in, first-out"); totuși, listele nu sunt eficiente în acest scop. În timp ce apendicele și pop-urile de la sfârșitul listei sunt rapide, inserarea sau ștampila de la începutul unei liste este lentă (deoarece toate celelalte elemente trebuie să fie deplasate de una).
Pentru a pune în aplicare o coadă, utilizați collections.deque care a fost proiectat pentru a avea rapide apendice și apare de la ambele capete. De exemplu:
Este, de asemenea, posibilă utilizarea unei liste ca o coadă, în care primul element adăugat este primul element preluat ("first-in, first-out"); totuși, listele nu sunt eficiente în acest scop. În timp ce apendicele și pop-urile de la sfârșitul listei sunt rapide, inserarea sau ștampila de la începutul unei liste este lentă (deoarece toate celelalte elemente trebuie să fie deplasate de una).
Pentru a pune în aplicare o coadă, utilizați collections.deque care a fost proiectat pentru a avea rapide apendice și apare de la ambele capete. De exemplu:
5.1.3. Lista înțelegerilor
Lista de înțelegeri oferă o modalitate concisă de a crea liste. Aplicațiile comune sunt de a face noi liste în care fiecare element este rezultatul unor operații aplicate fiecărui membru al unei alte secvențe sau iterabil sau pentru a crea o subsecventa a acelor elemente care satisfac o anumită condiție.
De exemplu, presupunem că vrem să creăm o listă de pătrate, cum ar fi:
Putem obtine acelasi rezultat cu:
Aceasta este, de asemenea, echivalentă cu pătratele = listă (hartă (lambda x: x ** 2,interval (10))) , dar este mai concisă și mai lizibilă.
O înțelegere a listei constă din paranteze care conțin o expresie urmată de o clauză pentru , apoi de zero sau mai mult pentru sau în cazul clauzelor. Rezultatul va fi o nouă listă care rezultă din evaluarea expresiei în contextul clauzelor for și if care o urmează. De exemplu, această listcomp combină elementele a două liste dacă nu sunt egale:
și este echivalent cu:
Dacă expresia este o tuplă (de exemplu, (x, y) din exemplul anterior), trebuie să fie paranteză.
Lista comprehensions poate conține expresii complexe și funcții imbricate:
Lista de înțelegeri oferă o modalitate concisă de a crea liste. Aplicațiile comune sunt de a face noi liste în care fiecare element este rezultatul unor operații aplicate fiecărui membru al unei alte secvențe sau iterabil sau pentru a crea o subsecventa a acelor elemente care satisfac o anumită condiție.
De exemplu, presupunem că vrem să creăm o listă de pătrate, cum ar fi:
Putem obtine acelasi rezultat cu:
Aceasta este, de asemenea, echivalentă cu pătratele = listă (hartă (lambda x: x ** 2,interval (10))) , dar este mai concisă și mai lizibilă.
O înțelegere a listei constă din paranteze care conțin o expresie urmată de o clauză pentru , apoi de zero sau mai mult pentru sau în cazul clauzelor. Rezultatul va fi o nouă listă care rezultă din evaluarea expresiei în contextul clauzelor for și if care o urmează. De exemplu, această listcomp combină elementele a două liste dacă nu sunt egale:
și este echivalent cu:
Dacă expresia este o tuplă (de exemplu, (x, y) din exemplul anterior), trebuie să fie paranteză.
Lista comprehensions poate conține expresii complexe și funcții imbricate:
5.1.4. Înțelegeri ale listei încorporate
Expresia inițială într-o înțelegere a listei poate fi orice expresie arbitrară, incluzând o altă înțelegere a listei.
Luați în considerare următorul exemplu de matrice 3x4 implementată ca o listă cu 3 liste de lungime 4:
Următoarea listă de înțelegere va transpune rândurile și coloanele:
Așa cum am văzut în secțiunea anterioară, lista listată imbricată este evaluată în contextul pentrucare o urmează, deci acest exemplu este echivalent cu:
care, la rândul său, este la fel ca:
În lumea reală, ar trebui să preferați funcțiile încorporate în declarațiile de flux complexe. Funcția zip () ar face o treabă excelentă pentru acest caz de utilizare:
Expresia inițială într-o înțelegere a listei poate fi orice expresie arbitrară, incluzând o altă înțelegere a listei.
Luați în considerare următorul exemplu de matrice 3x4 implementată ca o listă cu 3 liste de lungime 4:
Următoarea listă de înțelegere va transpune rândurile și coloanele:
Așa cum am văzut în secțiunea anterioară, lista listată imbricată este evaluată în contextul pentrucare o urmează, deci acest exemplu este echivalent cu:
care, la rândul său, este la fel ca:
În lumea reală, ar trebui să preferați funcțiile încorporate în declarațiile de flux complexe. Funcția zip () ar face o treabă excelentă pentru acest caz de utilizare:
5.2. Del Declarația
Există o modalitate de a elimina un element dintr-o listă în locul indexului său, în locul valorii sale: instrucțiunea del . Aceasta diferă de metoda pop () care returnează o valoare. Instrucțiunea delpoate fi de asemenea folosită pentru a elimina felii dintr-o listă sau pentru a șterge întreaga listă (ceea ce am făcut mai devreme prin alocarea unei liste goale la felie). De exemplu:
del poate fi, de asemenea, utilizat pentru a șterge variabilele întregi:
Referindu-se la denumirea de mai jos este o eroare (cel puțin până când îi este atribuită o altă valoare). Vom găsi alte utilizări pentru del mai târziu.
Există o modalitate de a elimina un element dintr-o listă în locul indexului său, în locul valorii sale: instrucțiunea del . Aceasta diferă de metoda pop () care returnează o valoare. Instrucțiunea delpoate fi de asemenea folosită pentru a elimina felii dintr-o listă sau pentru a șterge întreaga listă (ceea ce am făcut mai devreme prin alocarea unei liste goale la felie). De exemplu:
del poate fi, de asemenea, utilizat pentru a șterge variabilele întregi:
Referindu-se la denumirea de mai jos este o eroare (cel puțin până când îi este atribuită o altă valoare). Vom găsi alte utilizări pentru del mai târziu.
5.3. Tupele și secvențe
Am văzut că listele și șirurile au multe proprietăți comune, cum ar fi operațiile de indexare și de tăiere. Acestea sunt două exemple de tipuri de date de secvență (vedeți Tipuri de secvențe - listă, tuplă, interval ). Din moment ce Python este un limbaj în evoluție, pot fi adăugate alte tipuri de date secvențiale. Există, de asemenea, un alt tip de date standard de secvență: tupla .
O trupă constă dintr-un număr de valori separate prin virgule, de exemplu:
După cum vedeți, pe tuplele de ieșire sunt întotdeauna închise în paranteze, astfel că nopțile imbricate sunt interpretate corect; ele pot fi introduse cu sau fără paranteze înconjurătoare, deși adesea paranteze sunt necesare oricum (dacă tuplul face parte dintr-o expresie mai mare). Nu este posibilă alocarea elementelor individuale ale unei tuple, însă este posibil să se creeze tupluri care conțin obiecte mutabile, cum ar fi liste.
Deși tuplele pot părea similare cu listele, ele sunt adesea folosite în situații diferite și în scopuri diferite. Tupele sunt imuabile și conțin, de obicei, o secvență eterogenă de elemente care sunt accesate prin despachetare (a se vedea mai târziu în această secțiune) sau indexare (sau chiar prin atribut în cazul numerelor ). Listele sunt mutabile , iar elementele lor sunt, de obicei, omogene și sunt accesate prin iterarea în listă.
O problemă specială este construirea de tupluri care conțin 0 sau 1 elemente: sintaxa are unele ciudățenii suplimentare pentru a le acomoda. Gândurile goale sunt construite de o pereche goală de paranteze; un tuplu cu un element este construit urmând o valoare cu o virgulă (nu este suficient să închideți o singură valoare în paranteze). Urât, dar eficace. De exemplu:
Declarația t = 12345, 54321, "salut!" este un exemplu de ambalare în tuple : valorile 12345 , 54321 și "salut!" sunt ambalate împreună într-o tuplă. Este posibilă și operarea inversă:
Aceasta se numește, suficient de adecvat, despachetarea succesivă și funcționează pentru orice secvență din partea dreaptă. Descărcarea secvențelor necesită existența a numeroase variabile pe partea stângă a semnalului egal, deoarece există elemente în secvență. Rețineți că atribuirea mai multor este într-adevăr doar o combinație de împachetare tuplă și despachetare succesivă.
Am văzut că listele și șirurile au multe proprietăți comune, cum ar fi operațiile de indexare și de tăiere. Acestea sunt două exemple de tipuri de date de secvență (vedeți Tipuri de secvențe - listă, tuplă, interval ). Din moment ce Python este un limbaj în evoluție, pot fi adăugate alte tipuri de date secvențiale. Există, de asemenea, un alt tip de date standard de secvență: tupla .
O trupă constă dintr-un număr de valori separate prin virgule, de exemplu:
După cum vedeți, pe tuplele de ieșire sunt întotdeauna închise în paranteze, astfel că nopțile imbricate sunt interpretate corect; ele pot fi introduse cu sau fără paranteze înconjurătoare, deși adesea paranteze sunt necesare oricum (dacă tuplul face parte dintr-o expresie mai mare). Nu este posibilă alocarea elementelor individuale ale unei tuple, însă este posibil să se creeze tupluri care conțin obiecte mutabile, cum ar fi liste.
Deși tuplele pot părea similare cu listele, ele sunt adesea folosite în situații diferite și în scopuri diferite. Tupele sunt imuabile și conțin, de obicei, o secvență eterogenă de elemente care sunt accesate prin despachetare (a se vedea mai târziu în această secțiune) sau indexare (sau chiar prin atribut în cazul numerelor ). Listele sunt mutabile , iar elementele lor sunt, de obicei, omogene și sunt accesate prin iterarea în listă.
O problemă specială este construirea de tupluri care conțin 0 sau 1 elemente: sintaxa are unele ciudățenii suplimentare pentru a le acomoda. Gândurile goale sunt construite de o pereche goală de paranteze; un tuplu cu un element este construit urmând o valoare cu o virgulă (nu este suficient să închideți o singură valoare în paranteze). Urât, dar eficace. De exemplu:
Declarația t = 12345, 54321, "salut!" este un exemplu de ambalare în tuple : valorile 12345 , 54321 și "salut!" sunt ambalate împreună într-o tuplă. Este posibilă și operarea inversă:
Aceasta se numește, suficient de adecvat, despachetarea succesivă și funcționează pentru orice secvență din partea dreaptă. Descărcarea secvențelor necesită existența a numeroase variabile pe partea stângă a semnalului egal, deoarece există elemente în secvență. Rețineți că atribuirea mai multor este într-adevăr doar o combinație de împachetare tuplă și despachetare succesivă.
5.4. Seturi
Python include, de asemenea, un tip de date pentru seturi . Un set este o colecție neordonată fără elemente duplicate. Utilizările de bază includ testarea membrilor și eliminarea intrărilor duplicate. Setarea obiectelor suportă și operații matematice cum ar fi unirea, intersecția, diferența și diferența simetrică.
Comenzile curry sau funcția set () pot fi folosite pentru a crea seturi. Notă: pentru a crea un set gol trebuie să utilizați set () , nu {} ; acesta din urmă creează un dicționar gol, o structură de date pe care o discutăm în secțiunea următoare.
Iată o scurtă demonstrație:
Python include, de asemenea, un tip de date pentru seturi . Un set este o colecție neordonată fără elemente duplicate. Utilizările de bază includ testarea membrilor și eliminarea intrărilor duplicate. Setarea obiectelor suportă și operații matematice cum ar fi unirea, intersecția, diferența și diferența simetrică.
Comenzile curry sau funcția set () pot fi folosite pentru a crea seturi. Notă: pentru a crea un set gol trebuie să utilizați set () , nu {} ; acesta din urmă creează un dicționar gol, o structură de date pe care o discutăm în secțiunea următoare.
Iată o scurtă demonstrație:
5.5. Dicționare
Un alt tip de date util construit în Python este dicționarul (consultați Tipuri de cartografiere - dict ). Dicționarele sunt uneori găsite în alte limbi ca "amintiri asociative" sau "matrice asociative". Spre deosebire de secvențe, care sunt indexate printr-o serie de numere, dicționarele sunt indexate prin chei , care pot fi orice tip imuabil; șirurile și numerele pot fi întotdeauna chei. Tupele pot fi folosite ca chei dacă conțin numai șiruri, numere sau tuple; dacă o tuplă conține un obiect mutabil, direct sau indirect, nu poate fi folosit ca o cheie. Nu puteți utiliza listele ca chei, deoarece listele pot fi modificate în loc utilizând asignări index, alocări slice sau metode cum ar fi append () și extend () .
Cel mai bine este să gândiți un dicționar ca un set neordonat de perechi de chei: valoare , cu cerința că cheile sunt unice (într-un singur dicționar). O pereche de bretele creează un dicționar gol: {} . Plasarea unei liste separate de virgulă a perechilor de chei: valoare în cadrul brațelor adaugă cheia inițială: perechi de valori în dicționar; acesta este și modul în care sunt scrise dictionarele de ieșire.
Principalele operații dintr-un dicționar sunt stocarea unei valori cu unele chei și extragerea valorii date de tastă. Este, de asemenea, posibil să ștergeți o pereche cheie: valoare cu del . Dacă stocați utilizând o cheie deja utilizată, vechea valoare asociată acelei chei este uitată. Este o eroare să extrageți o valoare utilizând o cheie inexistentă.
Efectuarea unei liste (d.keys ()) într-un dicționar returnează o listă a tuturor cheilor folosite în dicționar, în ordine arbitrară (dacă doriți să fie sortată, utilizați în schimb sortate (d.keys () ). [2]Pentru a verifica dacă o singură tastă este în dicționar, utilizați cuvântul cheie în .
Iată un mic exemplu folosind un dicționar:
În plus, înțelegerile dict pot fi folosite pentru a crea dicționare din expresii cheie și valori arbitrare:
Când cheile sunt șiruri simple, uneori este mai ușor să specificați perechi folosind argumentele cuvintelor cheie:
Un alt tip de date util construit în Python este dicționarul (consultați Tipuri de cartografiere - dict ). Dicționarele sunt uneori găsite în alte limbi ca "amintiri asociative" sau "matrice asociative". Spre deosebire de secvențe, care sunt indexate printr-o serie de numere, dicționarele sunt indexate prin chei , care pot fi orice tip imuabil; șirurile și numerele pot fi întotdeauna chei. Tupele pot fi folosite ca chei dacă conțin numai șiruri, numere sau tuple; dacă o tuplă conține un obiect mutabil, direct sau indirect, nu poate fi folosit ca o cheie. Nu puteți utiliza listele ca chei, deoarece listele pot fi modificate în loc utilizând asignări index, alocări slice sau metode cum ar fi append () și extend () .
Cel mai bine este să gândiți un dicționar ca un set neordonat de perechi de chei: valoare , cu cerința că cheile sunt unice (într-un singur dicționar). O pereche de bretele creează un dicționar gol: {} . Plasarea unei liste separate de virgulă a perechilor de chei: valoare în cadrul brațelor adaugă cheia inițială: perechi de valori în dicționar; acesta este și modul în care sunt scrise dictionarele de ieșire.
Principalele operații dintr-un dicționar sunt stocarea unei valori cu unele chei și extragerea valorii date de tastă. Este, de asemenea, posibil să ștergeți o pereche cheie: valoare cu del . Dacă stocați utilizând o cheie deja utilizată, vechea valoare asociată acelei chei este uitată. Este o eroare să extrageți o valoare utilizând o cheie inexistentă.
Efectuarea unei liste (d.keys ()) într-un dicționar returnează o listă a tuturor cheilor folosite în dicționar, în ordine arbitrară (dacă doriți să fie sortată, utilizați în schimb sortate (d.keys () ). [2]Pentru a verifica dacă o singură tastă este în dicționar, utilizați cuvântul cheie în .
Iată un mic exemplu folosind un dicționar:
În plus, înțelegerile dict pot fi folosite pentru a crea dicționare din expresii cheie și valori arbitrare:
Când cheile sunt șiruri simple, uneori este mai ușor să specificați perechi folosind argumentele cuvintelor cheie:
5.6. Tehnici looping
Când se creează buclă prin dicționare, cheia și valoarea corespunzătoare pot fi preluate în același timp folosind metoda items () .
Când se trece printr-o secvență, indicele de poziție și valoarea corespunzătoare pot fi preluate în același timp folosind funcția enumerate () .
Pentru a intra în două sau mai multe secvențe în același timp, intrările pot fi asociate cu funcția zip () .
Pentru a bifa o secvență în sens invers, mai întâi specificați secvența într-o direcție înainte și apoi apelați funcția inversă () .
Pentru a bifa o secvență în ordinea sortată, utilizați funcția sort () care returnează o nouă listă sortată, lăsând în același timp sursa neschimbată.
Pentru a modifica o secvență pe care o repetați în interiorul bucla (de exemplu, pentru a duplica anumite elemente), este recomandat să creați o copie. Lovitura peste o secvență nu face implicit o copie. Notatia de felie face ca acest lucru deosebit de convenabil:
Când se creează buclă prin dicționare, cheia și valoarea corespunzătoare pot fi preluate în același timp folosind metoda items () .
Când se trece printr-o secvență, indicele de poziție și valoarea corespunzătoare pot fi preluate în același timp folosind funcția enumerate () .
Pentru a intra în două sau mai multe secvențe în același timp, intrările pot fi asociate cu funcția zip () .
Pentru a bifa o secvență în sens invers, mai întâi specificați secvența într-o direcție înainte și apoi apelați funcția inversă () .
Pentru a bifa o secvență în ordinea sortată, utilizați funcția sort () care returnează o nouă listă sortată, lăsând în același timp sursa neschimbată.
Pentru a modifica o secvență pe care o repetați în interiorul bucla (de exemplu, pentru a duplica anumite elemente), este recomandat să creați o copie. Lovitura peste o secvență nu face implicit o copie. Notatia de felie face ca acest lucru deosebit de convenabil:
5.7. Mai multe despre condițiile
Condițiile utilizate în instrucțiunile de timp și dacă pot conține orice operatori, nu doar comparații.
Operatorii de comparație în și nu în Verificați dacă apare o valoare (nu apare) într - o secvență. Operatorii este și este nu compara dacă două obiecte sunt într - adevăr același obiect; acest lucru contează doar pentru obiecte mutabile, cum ar fi listele. Toți operatorii de comparare au aceeași prioritate, care este mai mică decât cea a tuturor operatorilor numerici.
Comparațiile pot fi înlănțuite. De exemplu, un < b == c testează dacă a este mai mic decât b și în plus b este egal cu c .
Comparațiile pot fi combinate folosind operatorii booleani și și sau , iar rezultatul unei comparații (sau al oricărei alte expresii booleene) poate fi negat cu nu . Acestea au priorități mai mici decât operatorii de comparație; între ele, nu are cea mai mare prioritate și sau cea mai mică, astfel încât A și nu Bsau C este echivalent cu (A și (nu B)) sau C . Ca întotdeauna, parantezele pot fi folosite pentru a exprima compoziția dorită.
Operatorii booleeni și și sau sau așa-numiții operatori de scurt-circuit : argumentele lor sunt evaluate de la stânga la dreapta și evaluarea se oprește de îndată ce rezultatul este determinat. De exemplu, dacă A și C sunt adevărate , dar B este fals, A și B și C nu evaluează expresia C . Atunci când este folosit ca valoare generală și nu ca booleană, valoarea de retur a operatorului de scurt-circuit este ultimul argument evaluat.
Este posibil să se atribuie rezultatul unei comparații sau al altei expresii booleene unei variabile. De exemplu,
Rețineți că în Python, spre deosebire de C, atribuirea nu poate apărea în interiorul expresiilor. Programatorii C ar putea bâzâi despre acest lucru, dar evită o clasă comună de probleme întâlnite în programele C: tastând = într-o expresie când == a fost intenționată.
Condițiile utilizate în instrucțiunile de timp și dacă pot conține orice operatori, nu doar comparații.
Operatorii de comparație în și nu în Verificați dacă apare o valoare (nu apare) într - o secvență. Operatorii este și este nu compara dacă două obiecte sunt într - adevăr același obiect; acest lucru contează doar pentru obiecte mutabile, cum ar fi listele. Toți operatorii de comparare au aceeași prioritate, care este mai mică decât cea a tuturor operatorilor numerici.
Comparațiile pot fi înlănțuite. De exemplu, un < b == c testează dacă a este mai mic decât b și în plus b este egal cu c .
Comparațiile pot fi combinate folosind operatorii booleani și și sau , iar rezultatul unei comparații (sau al oricărei alte expresii booleene) poate fi negat cu nu . Acestea au priorități mai mici decât operatorii de comparație; între ele, nu are cea mai mare prioritate și sau cea mai mică, astfel încât A și nu Bsau C este echivalent cu (A și (nu B)) sau C . Ca întotdeauna, parantezele pot fi folosite pentru a exprima compoziția dorită.
Operatorii booleeni și și sau sau așa-numiții operatori de scurt-circuit : argumentele lor sunt evaluate de la stânga la dreapta și evaluarea se oprește de îndată ce rezultatul este determinat. De exemplu, dacă A și C sunt adevărate , dar B este fals, A și B și C nu evaluează expresia C . Atunci când este folosit ca valoare generală și nu ca booleană, valoarea de retur a operatorului de scurt-circuit este ultimul argument evaluat.
Este posibil să se atribuie rezultatul unei comparații sau al altei expresii booleene unei variabile. De exemplu,
Rețineți că în Python, spre deosebire de C, atribuirea nu poate apărea în interiorul expresiilor. Programatorii C ar putea bâzâi despre acest lucru, dar evită o clasă comună de probleme întâlnite în programele C: tastând = într-o expresie când == a fost intenționată.
5.8. Compararea secvențelor și a altor tipuri
Obiectele de secvență pot fi comparate cu alte obiecte cu același tip de secvență. Comparația folosește ordonarea lexicografică : mai întâi se compară primele două elemente și dacă acestea diferă, determină rezultatul comparației; dacă sunt egale, următoarele două elemente sunt comparate și așa mai departe, până când fiecare secvență este epuizată. Dacă două elemente care trebuie comparate sunt ele însele secvențe de același tip, comparația lexicografică se face recursiv. Dacă toate elementele a două secvențe se compară egale, secvențele sunt considerate egale. Dacă o secvență este o sub-secvență inițială a celeilalte, secvența mai scurtă este cea mai mică (mai mică). Ordonarea lexicografică pentru șiruri de caractere utilizează numărul codului Unicode pentru a comanda caractere individuale. Câteva exemple de comparații între secvențe de același tip:
Rețineți că compararea obiectelor de diferite tipuri cu < sau > este legală, cu condiția ca obiectele să aibă metode de comparare adecvate. De exemplu, tipurile numerice mixte sunt comparate în funcție de valoarea lor numerică, deci 0 este egală cu 0,0, etc. Altfel, în loc să furnizeze o comandă arbitrară, interpretul va ridica o excepție TypeError .
Note de subsol
[1] Alte limbi pot returna obiectul mutant, care permite înlănțuirea metodei, cum ar fi d-> inserați ("a") -> eliminați ("b") -> sort (); .
[2] Apelarea tastelor d () va returna un obiect de vizualizare a dicționarului . Acceptă operații precum testul de membru și iterația, dar conținutul său nu este independent de dicționarul original - este doar o viziune .
Obiectele de secvență pot fi comparate cu alte obiecte cu același tip de secvență. Comparația folosește ordonarea lexicografică : mai întâi se compară primele două elemente și dacă acestea diferă, determină rezultatul comparației; dacă sunt egale, următoarele două elemente sunt comparate și așa mai departe, până când fiecare secvență este epuizată. Dacă două elemente care trebuie comparate sunt ele însele secvențe de același tip, comparația lexicografică se face recursiv. Dacă toate elementele a două secvențe se compară egale, secvențele sunt considerate egale. Dacă o secvență este o sub-secvență inițială a celeilalte, secvența mai scurtă este cea mai mică (mai mică). Ordonarea lexicografică pentru șiruri de caractere utilizează numărul codului Unicode pentru a comanda caractere individuale. Câteva exemple de comparații între secvențe de același tip:
Rețineți că compararea obiectelor de diferite tipuri cu < sau > este legală, cu condiția ca obiectele să aibă metode de comparare adecvate. De exemplu, tipurile numerice mixte sunt comparate în funcție de valoarea lor numerică, deci 0 este egală cu 0,0, etc. Altfel, în loc să furnizeze o comandă arbitrară, interpretul va ridica o excepție TypeError .
Note de subsol
[1] | Alte limbi pot returna obiectul mutant, care permite înlănțuirea metodei, cum ar fi d-> inserați ("a") -> eliminați ("b") -> sort (); . |
[2] | Apelarea tastelor d () va returna un obiect de vizualizare a dicționarului . Acceptă operații precum testul de membru și iterația, dar conținutul său nu este independent de dicționarul original - este doar o viziune . |
Modulele
Dacă renunțați la interpretul Python și îl introduceți din nou, definițiile pe care le-ați făcut (funcții și variabile) sunt pierdute. Prin urmare, dacă doriți să scrieți un program oarecum mai lung, vă recomandăm mai bine să utilizați un editor de text pentru a pregăti intrarea pentru interpret și pentru ao executa cu acel fișier ca intrare. Acest lucru este cunoscut ca crearea unui script . Pe măsură ce programul dvs. devine mai lung, vă recomandăm să îl împărțiți în mai multe fișiere pentru o întreținere mai ușoară. Poate doriți să utilizați o funcție utilă pe care ați scris-o în mai multe programe, fără a copia definiția acesteia în fiecare program.
Pentru a sprijini acest lucru, Python are o modalitate de a pune definițiile într-un fișier și de a le utiliza într-un script sau într-o instanță interactivă a interpretului. Un astfel de fișier este numit modul ; definițiile dintr-un modul pot fi importate în alte module sau în modulul principal (colecția de variabile la care aveți acces într-un script executat la nivel superior și în modul calculator).
Un modul este un fișier care conține definiții și declarații Python. Numele fișierului este numele modulului cu sufixul .py atașat. În cadrul unui modul, numele modulului (ca șir) este disponibil ca valoare a variabilei globale __name__ . De exemplu, utilizați editorul de text preferat pentru a crea un fișier numit fibo.py în directorul curent cu următorul conținut:
Acum introduceți interpretul Python și importați acest modul cu următoarea comandă:
Aceasta nu introduce numele funcțiilor definite în fibo direct în tabela simbolică curentă; intră doar în numele modulului fibo acolo. Folosind numele modulului puteți accesa funcțiile:
Dacă intenționați să utilizați o funcție de multe ori, puteți să o atribuiți unui nume local:
6.1. Mai multe despre module
Un modul poate conține instrucțiuni executabile, precum și definiții ale funcțiilor. Aceste instrucțiuni sunt destinate inițializării modulului. Ele sunt executate numai pentru prima dată când numele modulului este întâlnit într-o declarație de import. [1] (De asemenea, se execută dacă fișierul este executat ca script).
Fiecare modul are propria tabelă de simboluri private, care este utilizată ca tabelă simbol globală de toate funcțiile definite în modul. Astfel, autorul unui modul poate folosi variabilele globale în modul fără să se preocupe de ciocniri accidentale cu variabilele globale ale utilizatorului. Pe de altă parte, dacă știți ce faceți, puteți atinge variabilele globale ale unui modul cu aceeași notație folosită pentru a vă referi la funcțiile sale, modname.itemname .
Modulele pot importa alte module. Este obișnuit, dar nu este necesar să plasați toate declarațiile de import la începutul unui modul (sau script, de pildă). Numele modulelor importate sunt plasate în tabelul simbolului global al modulului de import.
Există o variantă a instrucțiunii de import care importează numele dintr-un modul direct în tabelul cu simboluri al modulului de import. De exemplu:
Acest lucru nu introduce numele modulului din care se fac importurile în tabelul simbolurilor locale (așa că în exemplul de față fiboxul nu este definit).
Există chiar și o variantă pentru a importa toate denumirile pe care un modul le definește:
Aceasta importă toate denumirile, cu excepția celor care încep cu o subliniere ( _ ). În majoritatea cazurilor, programatorii Python nu utilizează această facilitate deoarece introduc un set necunoscut de nume în interpret, ascunzând eventual unele lucruri pe care le-ați definit deja.
Rețineți că, în general, practica importării * de la un modul sau un pachet este încruntată, deoarece provoacă adesea un cod ușor de citit. Cu toate acestea, este bine să o utilizați pentru a salva scrierea în sesiuni interactive.
Notă
Din motive de eficiență, fiecare modul este importat doar o singură sesiune de interpret. Prin urmare, dacă modificați modulele, trebuie să reporniți interpretul - sau, dacă este doar un modul pe care doriți să-l testați interactiv, utilizați imp.reload () , de ex. Import imp; imp.reload (modulename) .
Un modul poate conține instrucțiuni executabile, precum și definiții ale funcțiilor. Aceste instrucțiuni sunt destinate inițializării modulului. Ele sunt executate numai pentru prima dată când numele modulului este întâlnit într-o declarație de import. [1] (De asemenea, se execută dacă fișierul este executat ca script).
Fiecare modul are propria tabelă de simboluri private, care este utilizată ca tabelă simbol globală de toate funcțiile definite în modul. Astfel, autorul unui modul poate folosi variabilele globale în modul fără să se preocupe de ciocniri accidentale cu variabilele globale ale utilizatorului. Pe de altă parte, dacă știți ce faceți, puteți atinge variabilele globale ale unui modul cu aceeași notație folosită pentru a vă referi la funcțiile sale, modname.itemname .
Modulele pot importa alte module. Este obișnuit, dar nu este necesar să plasați toate declarațiile de import la începutul unui modul (sau script, de pildă). Numele modulelor importate sunt plasate în tabelul simbolului global al modulului de import.
Există o variantă a instrucțiunii de import care importează numele dintr-un modul direct în tabelul cu simboluri al modulului de import. De exemplu:
Acest lucru nu introduce numele modulului din care se fac importurile în tabelul simbolurilor locale (așa că în exemplul de față fiboxul nu este definit).
Există chiar și o variantă pentru a importa toate denumirile pe care un modul le definește:
Aceasta importă toate denumirile, cu excepția celor care încep cu o subliniere ( _ ). În majoritatea cazurilor, programatorii Python nu utilizează această facilitate deoarece introduc un set necunoscut de nume în interpret, ascunzând eventual unele lucruri pe care le-ați definit deja.
Rețineți că, în general, practica importării * de la un modul sau un pachet este încruntată, deoarece provoacă adesea un cod ușor de citit. Cu toate acestea, este bine să o utilizați pentru a salva scrierea în sesiuni interactive.
Notă
Din motive de eficiență, fiecare modul este importat doar o singură sesiune de interpret. Prin urmare, dacă modificați modulele, trebuie să reporniți interpretul - sau, dacă este doar un modul pe care doriți să-l testați interactiv, utilizați imp.reload () , de ex. Import imp; imp.reload (modulename) .
6.1.1. Executarea modulelor ca scripturi
Când rulați un modul Python cu
codul din modul va fi executat, ca și cum l-ați importat, dar cu __name__ setat la "__main__" . Aceasta înseamnă că prin adăugarea acestui cod la sfârșitul modulului dvs.:
puteți face fișierul utilizabil ca script, precum și un modul importabil, deoarece codul care analizează linia de comandă rulează numai dacă modulul este executat ca fișier "principal":
Dacă modulul este importat, codul nu este rulat:
Acest lucru este adesea folosit fie pentru a oferi o interfață de utilizare convenabilă unui modul, fie pentru scopuri de testare (funcționarea modulului ca script execută o suită de testare).
Când rulați un modul Python cu
codul din modul va fi executat, ca și cum l-ați importat, dar cu __name__ setat la "__main__" . Aceasta înseamnă că prin adăugarea acestui cod la sfârșitul modulului dvs.:
puteți face fișierul utilizabil ca script, precum și un modul importabil, deoarece codul care analizează linia de comandă rulează numai dacă modulul este executat ca fișier "principal":
Dacă modulul este importat, codul nu este rulat:
Acest lucru este adesea folosit fie pentru a oferi o interfață de utilizare convenabilă unui modul, fie pentru scopuri de testare (funcționarea modulului ca script execută o suită de testare).
6.1.2. Calea de căutare a modulului
Când un modul numit spam este importat, interpretul caută mai întâi un modul încorporat cu acest nume. Dacă nu este găsit, acesta caută apoi un fișier numit spam.py într-o listă de directoare date de variabila sys.path . sys.path este inițializată din aceste locații:
- directorul care conține scriptul de intrare (sau directorul curent).
- PYTHONPATH (o listă de nume de directoare, cu aceeași sintaxă ca variabila shellPATH ).
- implicit pentru instalare.
După inițializare, programele Python pot modifica sys.path . Directorul care conține scriptul care se execută este plasat la începutul căii de căutare, înainte de calea standard a bibliotecii. Aceasta înseamnă că script-urile din directorul respectiv vor fi încărcate în locul modulelor cu același nume în directorul bibliotecii. Aceasta este o eroare, cu excepția cazului în care se intenționează înlocuirea. Vezi secțiunea Module standard pentru mai multe informații.
Când un modul numit spam este importat, interpretul caută mai întâi un modul încorporat cu acest nume. Dacă nu este găsit, acesta caută apoi un fișier numit spam.py într-o listă de directoare date de variabila sys.path . sys.path este inițializată din aceste locații:
- directorul care conține scriptul de intrare (sau directorul curent).
- PYTHONPATH (o listă de nume de directoare, cu aceeași sintaxă ca variabila shellPATH ).
- implicit pentru instalare.
După inițializare, programele Python pot modifica sys.path . Directorul care conține scriptul care se execută este plasat la începutul căii de căutare, înainte de calea standard a bibliotecii. Aceasta înseamnă că script-urile din directorul respectiv vor fi încărcate în locul modulelor cu același nume în directorul bibliotecii. Aceasta este o eroare, cu excepția cazului în care se intenționează înlocuirea. Vezi secțiunea Module standard pentru mai multe informații.
6.1.3. „Compilat“ fișiere Python
Pentru a accelera modulele de încărcare, Python cachează versiunea compilată a fiecărui modul din directorul __pycache__ sub modulul de nume . versiunea .pyc , unde versiunea codifică formatul fișierului compilat; conține, în general, numărul versiunii Python. De exemplu, în versiunea CPython 3.3, versiunea compilate a spam.py va fi stocată ca __pycache __ / spam.cpython-33.pyc . Această convenție de numire permite modulelor compilate din diferite versiuni și diferite versiuni ale Python să coexiste.
Python verifică data modificării sursei împotriva versiunii compilate pentru a vedea dacă este depășită și trebuie să fie recompilată. Acesta este un proces complet automat. De asemenea, modulele compilate sunt independente de platformă, astfel încât aceeași bibliotecă poate fi împărțită între sisteme cu arhitecturi diferite.
Python nu verifică memoria cache în două situații. În primul rând, întotdeauna recompilează și nu stochează rezultatul pentru modulul încărcat direct din linia de comandă. În al doilea rând, nu verifică memoria cache dacă nu există niciun modul sursă. Pentru a suporta o distribuție non-sursă (compilate numai), modulul compilat trebuie să fie în directorul sursă și nu trebuie să existe un modul sursă.
Câteva sfaturi pentru experți:
- Puteți utiliza comutatoarele -O sau -OO din comanda Python pentru a reduce dimensiunea unui modul compilat. -O Comutatorul elimină afirma declarații, -OO comutatorul îndepårteazå afirma declarații și șiruri __doc__. Întrucât unele programe se pot baza pe faptul că acestea sunt disponibile, trebuie să utilizați această opțiune numai dacă știți ce faceți. Modulele "optimizate" au un .pyo mai degrabă decât un sufix .pyc și sunt de obicei mai mici. Eliberările viitoare pot schimba efectele optimizării.
- Un program nu rulează mai repede când este citit dintr-un fișier .pyc sau .pyo decât atunci când este citit dintr-un fișier .py ; singurul lucru care este mai rapid despre fișierele .pyc sau .pyo este viteza cu care sunt încărcate.
- Modulul compileall poate crea fișiere .pyc (sau fișiere .pyo când se utilizează -O ) pentru toate modulele dintr-un director.
- Există mai multe detalii despre acest proces, inclusiv o diagramă a deciziilor, în PEP 3147.
Pentru a accelera modulele de încărcare, Python cachează versiunea compilată a fiecărui modul din directorul __pycache__ sub modulul de nume . versiunea .pyc , unde versiunea codifică formatul fișierului compilat; conține, în general, numărul versiunii Python. De exemplu, în versiunea CPython 3.3, versiunea compilate a spam.py va fi stocată ca __pycache __ / spam.cpython-33.pyc . Această convenție de numire permite modulelor compilate din diferite versiuni și diferite versiuni ale Python să coexiste.
Python verifică data modificării sursei împotriva versiunii compilate pentru a vedea dacă este depășită și trebuie să fie recompilată. Acesta este un proces complet automat. De asemenea, modulele compilate sunt independente de platformă, astfel încât aceeași bibliotecă poate fi împărțită între sisteme cu arhitecturi diferite.
Python nu verifică memoria cache în două situații. În primul rând, întotdeauna recompilează și nu stochează rezultatul pentru modulul încărcat direct din linia de comandă. În al doilea rând, nu verifică memoria cache dacă nu există niciun modul sursă. Pentru a suporta o distribuție non-sursă (compilate numai), modulul compilat trebuie să fie în directorul sursă și nu trebuie să existe un modul sursă.
Câteva sfaturi pentru experți:
- Puteți utiliza comutatoarele -O sau -OO din comanda Python pentru a reduce dimensiunea unui modul compilat. -O Comutatorul elimină afirma declarații, -OO comutatorul îndepårteazå afirma declarații și șiruri __doc__. Întrucât unele programe se pot baza pe faptul că acestea sunt disponibile, trebuie să utilizați această opțiune numai dacă știți ce faceți. Modulele "optimizate" au un .pyo mai degrabă decât un sufix .pyc și sunt de obicei mai mici. Eliberările viitoare pot schimba efectele optimizării.
- Un program nu rulează mai repede când este citit dintr-un fișier .pyc sau .pyo decât atunci când este citit dintr-un fișier .py ; singurul lucru care este mai rapid despre fișierele .pyc sau .pyo este viteza cu care sunt încărcate.
- Modulul compileall poate crea fișiere .pyc (sau fișiere .pyo când se utilizează -O ) pentru toate modulele dintr-un director.
- Există mai multe detalii despre acest proces, inclusiv o diagramă a deciziilor, în PEP 3147.
6.2. Module standard
Python vine cu o bibliotecă de module standard, descrisă într-un document separat, Python Library Reference ("Biblioteca de referință" de mai jos). Unele module sunt construite în interpret; acestea oferă acces la operații care nu fac parte din miezul limbii, dar sunt totuși construite, fie pentru eficiență, fie pentru a oferi acces la primitivi ai sistemului de operare, cum ar fi apelurile de sistem. Setul de astfel de module este o opțiune de configurare care depinde și de platforma de bază. De exemplu, modulul winreg este furnizat numai pe sistemele Windows. Un modul special merită o atenție deosebită: sys , care este construit în fiecare interpret Python. Variabilele sys.ps1 și sys.ps2 definiți șirurile utilizate ca mesaje primare și secundare:
Aceste două variabile sunt definite numai dacă interpretul este în modul interactiv.
Variabila sys.path este o listă de șiruri care determină calea de căutare a modulelor de interpretare. Aceasta este inițializată la o cale implicită luată din variabila de mediuPYTHONPATH sau dintr-o valoare implicită încorporată dacă PYTHONPATH nu este setat. Puteți să o modificați utilizând operațiuni de listă standard:
Python vine cu o bibliotecă de module standard, descrisă într-un document separat, Python Library Reference ("Biblioteca de referință" de mai jos). Unele module sunt construite în interpret; acestea oferă acces la operații care nu fac parte din miezul limbii, dar sunt totuși construite, fie pentru eficiență, fie pentru a oferi acces la primitivi ai sistemului de operare, cum ar fi apelurile de sistem. Setul de astfel de module este o opțiune de configurare care depinde și de platforma de bază. De exemplu, modulul winreg este furnizat numai pe sistemele Windows. Un modul special merită o atenție deosebită: sys , care este construit în fiecare interpret Python. Variabilele sys.ps1 și sys.ps2 definiți șirurile utilizate ca mesaje primare și secundare:
Aceste două variabile sunt definite numai dacă interpretul este în modul interactiv.
Variabila sys.path este o listă de șiruri care determină calea de căutare a modulelor de interpretare. Aceasta este inițializată la o cale implicită luată din variabila de mediuPYTHONPATH sau dintr-o valoare implicită încorporată dacă PYTHONPATH nu este setat. Puteți să o modificați utilizând operațiuni de listă standard:
6.3. Dir () Funcția
Funcția built-in dir () este utilizată pentru a afla ce nume definește un modul. Returnează o listă ordonată de șiruri de caractere:
Rețineți că acesta afișează toate tipurile de nume: variabile, module, funcții etc.
dir () nu conține numele funcțiilor și variabilelor încorporate. Dacă doriți o listă a acestora, acestea sunt definite în modulul standard builtins :
Funcția built-in dir () este utilizată pentru a afla ce nume definește un modul. Returnează o listă ordonată de șiruri de caractere:
Rețineți că acesta afișează toate tipurile de nume: variabile, module, funcții etc.
dir () nu conține numele funcțiilor și variabilelor încorporate. Dacă doriți o listă a acestora, acestea sunt definite în modulul standard builtins :
6.4. Pachete
Pachetele reprezintă o modalitate de a structura spațiul de nume al modulelor Python utilizând "nume de module punctate". De exemplu, numele modulului AB desemneaza un submodul B într - un pachet numit A . La fel ca utilizarea modulelor salvează autorii diferitelor module de faptul că trebuie să vă faceți griji în legătură cu numele variabilelor globale ale fiecăruia, folosirea numelor de module întrerupte salvează autorii pachetelor multi-modul cum ar fi NumPy sau Biblioteca de Imagistică Python de la a fi nevoiți să vă faceți griji pentru fiecare numele altor module.
Să presupunem că doriți să proiectați o colecție de module (un "pachet") pentru o manipulare uniformă a fișierelor audio și a datelor de sunet. Există multe formate diferite de fișiere de sunet (de obicei, recunoscute de extensia lor, de exemplu: .wav , .aiff , .au ), astfel încât este posibil să fie nevoie să creați și să mențineți o colecție de module pentru convertirea între diferitele formate de fișiere. Există, de asemenea, multe operațiuni diferite pe care doriți să le efectuați pe date de sunet (cum ar fi amestecarea, adăugarea de ecouri, aplicarea unei funcții egalizatoare, crearea unui efect stereo artificial), astfel încât, în plus, veți scrie un flux neîntrerupt de module pentru a efectua aceste operațiuni. Iată o structură posibilă pentru pachetul dvs. (exprimată în termeni de sistem de fișiere ierarhic):
La importul pachetului, Python caută directoarele pe sys.path căutând subdirectorul de pachete.
Cele __init__.py Fișierele sunt necesare pentru a face Python trateze directoarele ca pachete care conțin; acest lucru se face pentru a împiedica directoarele cu un nume comun, cum ar fi șirul , să ascundă neintenționat module valide care apar ulterior pe calea de căutare a modulelor. În cel mai simplu caz, __init__.py poate fi doar un fișier gol, dar poate executa și codul de inițializare pentru pachet sau poate seta variabila __all__ descrisă mai târziu.
Utilizatorii pachetului pot importa module individuale din pachet, de exemplu:
Acest lucru încarcă submodul sound.effects.echo . Trebuie să fie menționată cu numele său complet.
O modalitate alternativă de a importa submodul este:
De asemenea, se încarcă ecoul submodulului și îl face disponibil fără prefixul pachetului, astfel încât acesta poate fi folosit după cum urmează:
O altă variantă este importul direct al funcției sau al variabilei dorite:
Din nou, acest lucru încarcă ecou submodule , dar acest lucru face ca funcția sa echofilter () să fie direct disponibilă:
Rețineți că atunci când utilizați din elementul de import al pachetelor , elementul poate fi fie un submodul (sau subpachetul) al pachetului, fie un alt nume definit în pachet, ca o funcție, o clasă sau o variabilă. Instrucțiunea de import verifică mai întâi dacă articolul este definit în pachet; dacă nu, presupune că este un modul și încearcă să îl încarce. Dacă nu reușește să o găsească, o excepție ImportError este ridicată.
Contrar, atunci când se utilizează o sintaxă precum importul item.subitem.subsubitem , fiecare articol, cu excepția ultimului, trebuie să fie un pachet; ultimul element poate fi un modul sau un pachet, dar nu poate fi o clasă sau o funcție sau o variabilă definită în articolul anterior.
Pachetele reprezintă o modalitate de a structura spațiul de nume al modulelor Python utilizând "nume de module punctate". De exemplu, numele modulului AB desemneaza un submodul B într - un pachet numit A . La fel ca utilizarea modulelor salvează autorii diferitelor module de faptul că trebuie să vă faceți griji în legătură cu numele variabilelor globale ale fiecăruia, folosirea numelor de module întrerupte salvează autorii pachetelor multi-modul cum ar fi NumPy sau Biblioteca de Imagistică Python de la a fi nevoiți să vă faceți griji pentru fiecare numele altor module.
Să presupunem că doriți să proiectați o colecție de module (un "pachet") pentru o manipulare uniformă a fișierelor audio și a datelor de sunet. Există multe formate diferite de fișiere de sunet (de obicei, recunoscute de extensia lor, de exemplu: .wav , .aiff , .au ), astfel încât este posibil să fie nevoie să creați și să mențineți o colecție de module pentru convertirea între diferitele formate de fișiere. Există, de asemenea, multe operațiuni diferite pe care doriți să le efectuați pe date de sunet (cum ar fi amestecarea, adăugarea de ecouri, aplicarea unei funcții egalizatoare, crearea unui efect stereo artificial), astfel încât, în plus, veți scrie un flux neîntrerupt de module pentru a efectua aceste operațiuni. Iată o structură posibilă pentru pachetul dvs. (exprimată în termeni de sistem de fișiere ierarhic):
La importul pachetului, Python caută directoarele pe sys.path căutând subdirectorul de pachete.
Cele __init__.py Fișierele sunt necesare pentru a face Python trateze directoarele ca pachete care conțin; acest lucru se face pentru a împiedica directoarele cu un nume comun, cum ar fi șirul , să ascundă neintenționat module valide care apar ulterior pe calea de căutare a modulelor. În cel mai simplu caz, __init__.py poate fi doar un fișier gol, dar poate executa și codul de inițializare pentru pachet sau poate seta variabila __all__ descrisă mai târziu.
Utilizatorii pachetului pot importa module individuale din pachet, de exemplu:
Acest lucru încarcă submodul sound.effects.echo . Trebuie să fie menționată cu numele său complet.
O modalitate alternativă de a importa submodul este:
De asemenea, se încarcă ecoul submodulului și îl face disponibil fără prefixul pachetului, astfel încât acesta poate fi folosit după cum urmează:
O altă variantă este importul direct al funcției sau al variabilei dorite:
Din nou, acest lucru încarcă ecou submodule , dar acest lucru face ca funcția sa echofilter () să fie direct disponibilă:
Rețineți că atunci când utilizați din elementul de import al pachetelor , elementul poate fi fie un submodul (sau subpachetul) al pachetului, fie un alt nume definit în pachet, ca o funcție, o clasă sau o variabilă. Instrucțiunea de import verifică mai întâi dacă articolul este definit în pachet; dacă nu, presupune că este un modul și încearcă să îl încarce. Dacă nu reușește să o găsească, o excepție ImportError este ridicată.
Contrar, atunci când se utilizează o sintaxă precum importul item.subitem.subsubitem , fiecare articol, cu excepția ultimului, trebuie să fie un pachet; ultimul element poate fi un modul sau un pachet, dar nu poate fi o clasă sau o funcție sau o variabilă definită în articolul anterior.
6.4.1. Importul * dintr-un pachet
Acum ce se întâmplă atunci când utilizatorul scrie din importul sound.effects * ? În mod ideal, s-ar speranța că aceasta va ieși într-o oarecare măsură în sistemul de fișiere, va afla care submodule sunt prezente în pachet și le importă pe toate. Acest lucru ar putea dura mult timp, iar sub-modulele de import ar putea avea efecte secundare nedorite care ar trebui să se producă numai atunci când submodulul este importat explicit.
Singura soluție este ca autorul pachetului să furnizeze un indice explicit al pachetului. Instrucțiunea de import utilizează următoarea convenție: dacă codul __init__.py al unui pachet definește o listă numită __all__ , este considerată lista cu numele de module care ar trebui să fie importate când se întâlnește din importul de pachete * . Depinde de autorul pachetului să păstreze actualizarea acestei liste atunci când este lansată o nouă versiune a pachetului. Autorii pachetelor pot, de asemenea, să decidă să nu o susțină, dacă nu văd o utilizare pentru importul * din pachetul lor. De exemplu, fișierul sunet / efecte / __ init__.py ar putea conține următorul cod:
Acest lucru ar însemna că importul * de la sound.effects ar importa cele trei submodule numite ale pachetului de sunet .
Dacă __all__ nu este definit, declarația de la Sound.Effects import * nu nu importa toate submodule din pachetul Sound.Effects în spațiul de nume curent; se asigură numai că au fost importate efecte sonore ale pachetului (eventual executând orice cod de inițializare în __init__.py) și apoi importă orice nume sunt definite în pachet. Aceasta include orice nume definite (și submodule încărcate explicit) de __init__.py . Acesta include, de asemenea, toate submodulele pachetului care au fost încărcate în mod explicit de declarațiile anterioare de import . Luați în considerare acest cod:
În acest exemplu, modulele de ecou și surround sunt importate în spațiul de nume curent deoarece sunt definite în pachetul sound.effects atunci când declarația de import din ... este executată. (Aceasta funcționează și atunci când __all__ este definit.)
Deși anumite module sunt proiectate să exporte numai nume care urmează anumite modele atunci când utilizați import * , este încă considerată o practică nepotrivită în codul de producție.
Rețineți că nu este nimic în neregulă cu utilizarea din pachetul de import specific_submodule ! De fapt, aceasta este notația recomandată dacă modulul de import nu trebuie să utilizeze submodule cu același nume din diferite pachete.
Acum ce se întâmplă atunci când utilizatorul scrie din importul sound.effects * ? În mod ideal, s-ar speranța că aceasta va ieși într-o oarecare măsură în sistemul de fișiere, va afla care submodule sunt prezente în pachet și le importă pe toate. Acest lucru ar putea dura mult timp, iar sub-modulele de import ar putea avea efecte secundare nedorite care ar trebui să se producă numai atunci când submodulul este importat explicit.
Singura soluție este ca autorul pachetului să furnizeze un indice explicit al pachetului. Instrucțiunea de import utilizează următoarea convenție: dacă codul __init__.py al unui pachet definește o listă numită __all__ , este considerată lista cu numele de module care ar trebui să fie importate când se întâlnește din importul de pachete * . Depinde de autorul pachetului să păstreze actualizarea acestei liste atunci când este lansată o nouă versiune a pachetului. Autorii pachetelor pot, de asemenea, să decidă să nu o susțină, dacă nu văd o utilizare pentru importul * din pachetul lor. De exemplu, fișierul sunet / efecte / __ init__.py ar putea conține următorul cod:
Acest lucru ar însemna că importul * de la sound.effects ar importa cele trei submodule numite ale pachetului de sunet .
Dacă __all__ nu este definit, declarația de la Sound.Effects import * nu nu importa toate submodule din pachetul Sound.Effects în spațiul de nume curent; se asigură numai că au fost importate efecte sonore ale pachetului (eventual executând orice cod de inițializare în __init__.py) și apoi importă orice nume sunt definite în pachet. Aceasta include orice nume definite (și submodule încărcate explicit) de __init__.py . Acesta include, de asemenea, toate submodulele pachetului care au fost încărcate în mod explicit de declarațiile anterioare de import . Luați în considerare acest cod:
În acest exemplu, modulele de ecou și surround sunt importate în spațiul de nume curent deoarece sunt definite în pachetul sound.effects atunci când declarația de import din ... este executată. (Aceasta funcționează și atunci când __all__ este definit.)
Deși anumite module sunt proiectate să exporte numai nume care urmează anumite modele atunci când utilizați import * , este încă considerată o practică nepotrivită în codul de producție.
Rețineți că nu este nimic în neregulă cu utilizarea din pachetul de import specific_submodule ! De fapt, aceasta este notația recomandată dacă modulul de import nu trebuie să utilizeze submodule cu același nume din diferite pachete.
6.4.2. Referințe-pachet intra
Când pachetele sunt structurate în subpachete (ca în cazul pachetului de sunet din exemplu), puteți utiliza importurile absolute pentru a vă referi la submodulele pachetelor de frați. De exemplu, dacă modulul sound.filters.vocoder trebuie să utilizeze modulul ecou din pachetul sound.effects , acesta poate folosi din sound.effects import echo .
Puteți scrie , de asemenea , importurile relative, cu de la modulul de import numele sub formă de declarație de import. Aceste importuri utilizează puncte de vârf pentru a indica pachetele actuale și părțile implicate în importul relativ. De exemplu, de la modulul surround , este posibil să utilizați:
Rețineți că importurile relative se bazează pe numele modulului curent. Deoarece numele modulului principal este întotdeauna "__main__" , modulele destinate a fi utilizate ca modul principal al unei aplicații Python trebuie să utilizeze întotdeauna importuri absolute.
Când pachetele sunt structurate în subpachete (ca în cazul pachetului de sunet din exemplu), puteți utiliza importurile absolute pentru a vă referi la submodulele pachetelor de frați. De exemplu, dacă modulul sound.filters.vocoder trebuie să utilizeze modulul ecou din pachetul sound.effects , acesta poate folosi din sound.effects import echo .
Puteți scrie , de asemenea , importurile relative, cu de la modulul de import numele sub formă de declarație de import. Aceste importuri utilizează puncte de vârf pentru a indica pachetele actuale și părțile implicate în importul relativ. De exemplu, de la modulul surround , este posibil să utilizați:
Rețineți că importurile relative se bazează pe numele modulului curent. Deoarece numele modulului principal este întotdeauna "__main__" , modulele destinate a fi utilizate ca modul principal al unei aplicații Python trebuie să utilizeze întotdeauna importuri absolute.
6.4.3. Pachete în mai multe directoare
Pachetele suportă încă un atribut special, __path__ . Aceasta este inițializată pentru a fi o listă care conține numele directorului care ține fișierul __init__.py al pachetului înainte ca codul din acel fișier să fie executat. Această variabilă poate fi modificată; acest lucru afectează căutările viitoare pentru module și subpachete conținute în pachet.
În timp ce această caracteristică nu este adesea necesară, ea poate fi utilizată pentru a extinde setul de module găsite într-un pachet.
Note de subsol
[1] De fapt, definițiile funcțiilor sunt și "declarații" care sunt "executate"; execuția unei definiții a funcției la nivel de modul introduce numele funcției în tabela simbolică globală a modulului.
Pachetele suportă încă un atribut special, __path__ . Aceasta este inițializată pentru a fi o listă care conține numele directorului care ține fișierul __init__.py al pachetului înainte ca codul din acel fișier să fie executat. Această variabilă poate fi modificată; acest lucru afectează căutările viitoare pentru module și subpachete conținute în pachet.
În timp ce această caracteristică nu este adesea necesară, ea poate fi utilizată pentru a extinde setul de module găsite într-un pachet.
Note de subsol
[1] | De fapt, definițiile funcțiilor sunt și "declarații" care sunt "executate"; execuția unei definiții a funcției la nivel de modul introduce numele funcției în tabela simbolică globală a modulului. |
7. Intrări și ieșiri
Există mai multe modalități de prezentare a rezultatelor unui program; datele pot fi tipărite într-o formă ușor de citit de om sau scrise într-un fișier pentru utilizare ulterioară. Acest capitol va discuta câteva posibilități.
7.1. Formatul de ieșire pentru fancier
Până acum am întâlnit două modalități de scriere a valorilor: declarații de expresie și funcția print (). (A treia cale este folosirea metodei write () a obiectelor de fișier, fișierul de ieșire standard poate fi referit ca sys.stdout .
Deseori veți dori mai mult control asupra formatării ieșirii decât prin simpla imprimare a valorilor separate de spațiu. Există două moduri de formatare a ieșirii; prima modalitate este de a face toate manipularea string-ului; folosind operațiuni de tăiere în șir și operații de concatenare, puteți crea orice aspect pe care să-l puteți imagina. Tipul de șir are câteva metode care efectuează operații utile pentru ștanțarea șirurilor la o anumită lățime a coloanei; acestea vor fi discutate în scurt timp. A doua modalitate este de a folosi metoda str.format () .
O întrebare rămâne, bineînțeles: cum convertiți valori în șiruri de caractere? Din fericire, Python are modalități de a converti orice valoare într-un șir: treceți-l la funcțiile repr () sau str () .
Funcția str () are rolul de a returna reprezentări ale valorilor care pot fi citite de oameni, în timp ce repr () este destinat să genereze reprezentări care pot fi citite de interpret (sau vor forța un SyntaxError dacă nu există o sintaxă echivalentă). Pentru obiectele care nu au o reprezentare specială pentru consumul uman, str () va reveni la aceeași valoare ca și repr () . Multe valori, cum ar fi numere sau structuri precum liste și dicționare, au aceeași reprezentare utilizând oricare dintre funcții. Șirurile, în special, au două reprezentări distincte.
Cateva exemple:
Iată două modalități de a scrie o tabelă cu pătrate și cuburi:
(Rețineți că în primul exemplu, un spațiu între fiecare coloană a fost adăugat prin modul print () : acesta adaugă întotdeauna spații între argumentele sale.)
Acest exemplu demonstrează metoda str.rjust () a obiectelor șir, care justifică corect un șir dintr-un câmp de o anumită lățime, umplând-l cu spații în stânga. Există metode similare str.ljust () șistr.center () . Aceste metode nu scriu nimic, ci doar returnează un nou șir. Dacă șirul de introducere este prea lung, nu îl trunchiază, ci îl returnează neschimbat; acest lucru va afecta structura coloanei, dar de obicei este mai bună decât alternativa, care ar fi mincinoasă despre o valoare. (Dacă vreți cu adevărat trunchierea, puteți adăuga întotdeauna o operație cu felie, ca în x.ljust (n) [: n].)
Există o altă metodă, str.zfill () , care pătrunde într- un șir numeric în stânga cu zerouri. Înțelege semnele plus și minus:
Parantezele și caracterele din ele (numite câmpuri de format) sunt înlocuite cu obiectele transmise în metoda str.format () . Un număr din paranteze poate fi folosit pentru a se referi la poziția obiectului trecut în metoda str.format () .
În cazul în care argumentele de cuvinte cheie sunt utilizate în metoda str.format () , valorile lor sunt menționate folosind numele argumentului.
Argumentele poziționate și ale cuvintelor cheie pot fi combinate arbitrar:
'a' (se aplică ascii () ), '! s' (se aplică str () ) și '! r' (se aplică repr () ) poate fi folosit pentru a converti valoarea înainte de a fi formatat:
Un opțional ":" și un specificator de format poate urma numele câmpului. Acest lucru permite un control mai mare asupra modului în care este formatată valoarea. Exemplul de mai jos trasează Pi la trei locuri după zecimală.
Trimiterea unui număr întreg după ':' va determina ca acest câmp să aibă un număr minim de caractere lățime. Acest lucru este util pentru a face mese destul.
Dacă aveți un șir de format foarte lung pe care nu doriți să-l împărțiți, ar fi bine dacă ați putea face referire la variabilele care urmează să fie formatate în funcție de nume în loc de poziție. Acest lucru se poate face prin simpla trecere a dictului și folosirea parantezelor [] pentru a accesa cheile
Acest lucru se poate face și prin trecerea tabelului ca argumente pentru cuvinte cheie cu notația '**'.
Acest lucru este util în special în combinație cu funcția încorporată vars () , care returnează un dicționar care conține toate variabilele locale.
Pentru o prezentare completă a formatării șirului cu str.format () , consultați Sintaxa de formatare a șirului .
Până acum am întâlnit două modalități de scriere a valorilor: declarații de expresie și funcția print (). (A treia cale este folosirea metodei write () a obiectelor de fișier, fișierul de ieșire standard poate fi referit ca sys.stdout .
Deseori veți dori mai mult control asupra formatării ieșirii decât prin simpla imprimare a valorilor separate de spațiu. Există două moduri de formatare a ieșirii; prima modalitate este de a face toate manipularea string-ului; folosind operațiuni de tăiere în șir și operații de concatenare, puteți crea orice aspect pe care să-l puteți imagina. Tipul de șir are câteva metode care efectuează operații utile pentru ștanțarea șirurilor la o anumită lățime a coloanei; acestea vor fi discutate în scurt timp. A doua modalitate este de a folosi metoda str.format () .
O întrebare rămâne, bineînțeles: cum convertiți valori în șiruri de caractere? Din fericire, Python are modalități de a converti orice valoare într-un șir: treceți-l la funcțiile repr () sau str () .
Funcția str () are rolul de a returna reprezentări ale valorilor care pot fi citite de oameni, în timp ce repr () este destinat să genereze reprezentări care pot fi citite de interpret (sau vor forța un SyntaxError dacă nu există o sintaxă echivalentă). Pentru obiectele care nu au o reprezentare specială pentru consumul uman, str () va reveni la aceeași valoare ca și repr () . Multe valori, cum ar fi numere sau structuri precum liste și dicționare, au aceeași reprezentare utilizând oricare dintre funcții. Șirurile, în special, au două reprezentări distincte.
Cateva exemple:
Iată două modalități de a scrie o tabelă cu pătrate și cuburi:
(Rețineți că în primul exemplu, un spațiu între fiecare coloană a fost adăugat prin modul print () : acesta adaugă întotdeauna spații între argumentele sale.)
Acest exemplu demonstrează metoda str.rjust () a obiectelor șir, care justifică corect un șir dintr-un câmp de o anumită lățime, umplând-l cu spații în stânga. Există metode similare str.ljust () șistr.center () . Aceste metode nu scriu nimic, ci doar returnează un nou șir. Dacă șirul de introducere este prea lung, nu îl trunchiază, ci îl returnează neschimbat; acest lucru va afecta structura coloanei, dar de obicei este mai bună decât alternativa, care ar fi mincinoasă despre o valoare. (Dacă vreți cu adevărat trunchierea, puteți adăuga întotdeauna o operație cu felie, ca în x.ljust (n) [: n].)
Există o altă metodă, str.zfill () , care pătrunde într- un șir numeric în stânga cu zerouri. Înțelege semnele plus și minus:
Parantezele și caracterele din ele (numite câmpuri de format) sunt înlocuite cu obiectele transmise în metoda str.format () . Un număr din paranteze poate fi folosit pentru a se referi la poziția obiectului trecut în metoda str.format () .
În cazul în care argumentele de cuvinte cheie sunt utilizate în metoda str.format () , valorile lor sunt menționate folosind numele argumentului.
Argumentele poziționate și ale cuvintelor cheie pot fi combinate arbitrar:
'a' (se aplică ascii () ), '! s' (se aplică str () ) și '! r' (se aplică repr () ) poate fi folosit pentru a converti valoarea înainte de a fi formatat:
Un opțional ":" și un specificator de format poate urma numele câmpului. Acest lucru permite un control mai mare asupra modului în care este formatată valoarea. Exemplul de mai jos trasează Pi la trei locuri după zecimală.
Trimiterea unui număr întreg după ':' va determina ca acest câmp să aibă un număr minim de caractere lățime. Acest lucru este util pentru a face mese destul.
Dacă aveți un șir de format foarte lung pe care nu doriți să-l împărțiți, ar fi bine dacă ați putea face referire la variabilele care urmează să fie formatate în funcție de nume în loc de poziție. Acest lucru se poate face prin simpla trecere a dictului și folosirea parantezelor [] pentru a accesa cheile
Acest lucru se poate face și prin trecerea tabelului ca argumente pentru cuvinte cheie cu notația '**'.
Acest lucru este util în special în combinație cu funcția încorporată vars () , care returnează un dicționar care conține toate variabilele locale.
Pentru o prezentare completă a formatării șirului cu str.format () , consultați Sintaxa de formatare a șirului .
7.1.1. Formatarea șirului vechi
% Operatorul poate fi de asemenea utilizat pentru formatarea șir de caractere. Aceasta interpretează argumentul stâng la fel ca un șir de format sprintf () -style pentru a fi aplicat argumentului drept și returnează șirul rezultat din această operație de formatare. De exemplu:
% Operatorul poate fi de asemenea utilizat pentru formatarea șir de caractere. Aceasta interpretează argumentul stâng la fel ca un șir de format sprintf () -style pentru a fi aplicat argumentului drept și returnează șirul rezultat din această operație de formatare. De exemplu:
7.2. Citirea și scrierea fișierelor
open () returnează un obiect de fișier și este cel mai frecvent utilizat cu două argumente: open (nume fișier, mod) .
Primul argument este un șir care conține numele fișierului. Al doilea argument este un alt șir care conține câteva caractere care descriu modul în care fișierul va fi utilizat. Modul poate fi "r" atunci când fișierul va fi citit numai, "w" pentru scris doar (un fișier existent cu același nume va fi șters), iar"a" deschide fișierul pentru adăugare; toate datele scrise în fișier sunt adăugate automat la final. 'r +' deschide fișierul pentru citire și scriere. Modul Argumentul este opțională; "r" va fi asumat dacă este omis.
În mod normal, fișierele sunt deschise în modul text , adică citiți și scrieți șiruri din și în fișier, care sunt codificate într-o anumită codificare (implicit UTF-8). "b" atașat la modul deschide fișierul în modul binar : acum datele sunt citite și scrise sub formă de obiecte bytes. Acest mod ar trebui să fie utilizat pentru toate fișierele care nu conțin text.
În modul text, setarea implicită la citire este de a converti terminațiile liniei specifice platformei ( \ n pe Unix, \ r \ n pe Windows) la doar \ n . Când scrieți în modul text, implicit este să convertiți aparițiile \ n înapoi la terminalele liniei specifice platformei. Această modificare a datelor din fișiere în spatele scenei este bună pentru fișierele text, dar va corupe datele binare, cum ar fi cele din fișierele JPEGsau EXE . Fiți foarte atent să utilizați modul binar atunci când citiți și scrieți astfel de fișiere.
open () returnează un obiect de fișier și este cel mai frecvent utilizat cu două argumente: open (nume fișier, mod) .
Primul argument este un șir care conține numele fișierului. Al doilea argument este un alt șir care conține câteva caractere care descriu modul în care fișierul va fi utilizat. Modul poate fi "r" atunci când fișierul va fi citit numai, "w" pentru scris doar (un fișier existent cu același nume va fi șters), iar"a" deschide fișierul pentru adăugare; toate datele scrise în fișier sunt adăugate automat la final. 'r +' deschide fișierul pentru citire și scriere. Modul Argumentul este opțională; "r" va fi asumat dacă este omis.
În mod normal, fișierele sunt deschise în modul text , adică citiți și scrieți șiruri din și în fișier, care sunt codificate într-o anumită codificare (implicit UTF-8). "b" atașat la modul deschide fișierul în modul binar : acum datele sunt citite și scrise sub formă de obiecte bytes. Acest mod ar trebui să fie utilizat pentru toate fișierele care nu conțin text.
În modul text, setarea implicită la citire este de a converti terminațiile liniei specifice platformei ( \ n pe Unix, \ r \ n pe Windows) la doar \ n . Când scrieți în modul text, implicit este să convertiți aparițiile \ n înapoi la terminalele liniei specifice platformei. Această modificare a datelor din fișiere în spatele scenei este bună pentru fișierele text, dar va corupe datele binare, cum ar fi cele din fișierele JPEGsau EXE . Fiți foarte atent să utilizați modul binar atunci când citiți și scrieți astfel de fișiere.
7.2.1. Metode ale obiectelor de fișiere
Restul exemplelor din această secțiune presupun că un obiect de fișier numit f a fost deja creat.
Pentru a citi conținutul unui fișier, apelați f.read (size) , care citește o cantitate de date și o returnează ca un obiect șir sau bytes. dimensiunea este un argument numeric opțional. Atunci când mărimea este omisă sau negativă, întregul conținut al fișierului va fi citit și returnat; problema dvs. este dacă fișierul este de două ori mai mare decât memoria mașinii dvs. În caz contrar, la cele mai multe dimensiuni, octeții sunt citiți și returnați. Dacă sa ajuns la sfârșitul fișierului, f.read () va returna un șir gol ( '' ).
f.readline () citește o singură linie din fișier; un caracter de linie nouă ( \ n ) este lăsat la sfârșitul șirului și este omis pe ultima linie a fișierului dacă fișierul nu se termină într-o linie nouă. Aceasta face ca valoarea de retur să fie neechivocă; dacă f.readline () returnează un șir gol, sa ajuns la sfârșitul fișierului, în timp ce o linie necompletată este reprezentată de '\ n' , un șir care conține doar o singură linie nouă.
Pentru citirea liniilor dintr-un fișier, puteți să le cuplați peste obiectul de fișier. Aceasta este memorie eficientă, rapidă și duce la un cod simplu:
Dacă doriți să citiți toate liniile unui fișier dintr-o listă, puteți utiliza și lista (f) sau f.readlines () .
f.write (string) scrie conținutul șirului în fișier, returnând numărul de caractere scrise.
Pentru a scrie ceva diferit de un șir, trebuie mai întâi să fie convertit într-un șir:
f.tell () returnează un număr întreg care dă poziția curentă a obiectului fișierului în fișierul reprezentat ca număr de octeți de la începutul fișierului când este în modul binar și un număr opac înmodul text .
Pentru a modifica poziția obiectului fișierului, utilizați f.seek (offset, from_what) . Poziția este calculată prin adăugarea de compensare la un punct de referință; punctul de referință este selectat de argumentul from_what . O from_what valoare de 0 măsuri de la începutul fișierului, 1 utilizează poziția fișierului curent și 2 utilizează sfârșitul fișierului ca punct de referință. from_what poate fi omis și defaults la 0, folosind începutul fișierului ca punct de referință.
În fișierele text (cele deschise fără un b în șirul de moduri) sunt permise doar căutările relative la începutul fișierului (excepția este căutată până la sfârșitul fișierului cu căutarea (0, 2) ) și singurele valori ofset valide sunt cele returnate de la f.tell () , sau zero. Orice altă valoare compensată produce comportament nedefinit.
Când ați terminat cu un fișier, apelați f.close () pentru al închide și a elibera toate resursele de sistem preluate de fișierul deschis. După ce a sunat f.close () , încercările de a utiliza obiectul de fișier vor eșua automat.
Este o bună practică să folosească cu cuvinte cheie atunci când se ocupă cu obiecte de fișiere. Acest lucru are avantajul că fișierul este închis corespunzător după terminarea suitei sale, chiar dacă se ridică o excepție pe parcurs. Este, de asemenea, mult mai scurtă decât scrierea încercăriiechivalente - în cele din urmă blocuri:
Obiectele de fișiere au câteva metode suplimentare, cum ar fi isatty () și truncate (), care sunt mai puțin utilizate; consultați Biblioteca Referință pentru un ghid complet al obiectelor de fișier.
Restul exemplelor din această secțiune presupun că un obiect de fișier numit f a fost deja creat.
Pentru a citi conținutul unui fișier, apelați f.read (size) , care citește o cantitate de date și o returnează ca un obiect șir sau bytes. dimensiunea este un argument numeric opțional. Atunci când mărimea este omisă sau negativă, întregul conținut al fișierului va fi citit și returnat; problema dvs. este dacă fișierul este de două ori mai mare decât memoria mașinii dvs. În caz contrar, la cele mai multe dimensiuni, octeții sunt citiți și returnați. Dacă sa ajuns la sfârșitul fișierului, f.read () va returna un șir gol ( '' ).
f.readline () citește o singură linie din fișier; un caracter de linie nouă ( \ n ) este lăsat la sfârșitul șirului și este omis pe ultima linie a fișierului dacă fișierul nu se termină într-o linie nouă. Aceasta face ca valoarea de retur să fie neechivocă; dacă f.readline () returnează un șir gol, sa ajuns la sfârșitul fișierului, în timp ce o linie necompletată este reprezentată de '\ n' , un șir care conține doar o singură linie nouă.
Pentru citirea liniilor dintr-un fișier, puteți să le cuplați peste obiectul de fișier. Aceasta este memorie eficientă, rapidă și duce la un cod simplu:
Dacă doriți să citiți toate liniile unui fișier dintr-o listă, puteți utiliza și lista (f) sau f.readlines () .
f.write (string) scrie conținutul șirului în fișier, returnând numărul de caractere scrise.
Pentru a scrie ceva diferit de un șir, trebuie mai întâi să fie convertit într-un șir:
f.tell () returnează un număr întreg care dă poziția curentă a obiectului fișierului în fișierul reprezentat ca număr de octeți de la începutul fișierului când este în modul binar și un număr opac înmodul text .
Pentru a modifica poziția obiectului fișierului, utilizați f.seek (offset, from_what) . Poziția este calculată prin adăugarea de compensare la un punct de referință; punctul de referință este selectat de argumentul from_what . O from_what valoare de 0 măsuri de la începutul fișierului, 1 utilizează poziția fișierului curent și 2 utilizează sfârșitul fișierului ca punct de referință. from_what poate fi omis și defaults la 0, folosind începutul fișierului ca punct de referință.
În fișierele text (cele deschise fără un b în șirul de moduri) sunt permise doar căutările relative la începutul fișierului (excepția este căutată până la sfârșitul fișierului cu căutarea (0, 2) ) și singurele valori ofset valide sunt cele returnate de la f.tell () , sau zero. Orice altă valoare compensată produce comportament nedefinit.
Când ați terminat cu un fișier, apelați f.close () pentru al închide și a elibera toate resursele de sistem preluate de fișierul deschis. După ce a sunat f.close () , încercările de a utiliza obiectul de fișier vor eșua automat.
Este o bună practică să folosească cu cuvinte cheie atunci când se ocupă cu obiecte de fișiere. Acest lucru are avantajul că fișierul este închis corespunzător după terminarea suitei sale, chiar dacă se ridică o excepție pe parcurs. Este, de asemenea, mult mai scurtă decât scrierea încercăriiechivalente - în cele din urmă blocuri:
Obiectele de fișiere au câteva metode suplimentare, cum ar fi isatty () și truncate (), care sunt mai puțin utilizate; consultați Biblioteca Referință pentru un ghid complet al obiectelor de fișier.
7.2.2. Salvarea datelor structurate cu json
Șirurile pot fi ușor scrise și citite dintr-un fișier. Numerele fac un efort mai mic, deoarece metoda read () returneaza numai siruri de caractere, care trebuie sa fie transmise unei functii precum int () , care ia un sir ca '123' si isi returneaza valoarea numerica 123. Cand vrei sa salvați mai complexe tipuri de date, cum ar fi liste imbricate și dicționare, parsarea și serializarea manuală devine complicată.
Mai degrabă decât dacă utilizatorii au în mod constant scrierea și depanarea codului pentru a salva tipuri de date complicate în fișiere, Python vă permite să utilizați formatul popular de schimb de date numit JSON (JavaScript Object Notation) . Modulul standard denumit json poate lua ierarhiile de date Python și le poate converti în reprezentări de șir; acest proces se numește serializare . Reconstruirea datelor din reprezentarea șirului se numește deserializare . Între serializarea și deserializarea, șirul reprezentând obiectul ar fi putut fi stocat într-un fișier sau date sau trimis printr-o conexiune de rețea la o anumită mașină îndepărtată.
Notă
Formatul JSON este utilizat în mod obișnuit de aplicații moderne pentru a permite schimbul de date. Mulți programatori sunt deja familiarizați cu acesta, ceea ce îl face o alegere bună pentru interoperabilitate.
Dacă aveți un obiect x , puteți vizualiza reprezentarea șirului JSON cu o linie simplă de cod:
O altă variantă a funcției dumps () , denumită dump () , serializează pur și simplu obiectul într-un fișier text . Deci, dacă f este un obiect de fișier text deschis pentru scriere, putem face acest lucru:
Pentru a decoda obiectul din nou, dacă f este un obiect de fișier text care a fost deschis pentru citire:
Această tehnică de serializare simplă poate gestiona liste și dicționare, însă serializarea instanțelor de clasă arbitrare din JSON necesită un efort suplimentar. Referința pentru modulul json conține o explicație a acestui lucru.
Vezi si
pickle - modulul pickle
Spre deosebire de JSON , pickle este un protocol care permite serializarea obiectelor Python arbitrar complexe. Ca atare, este specific pentru Python și nu poate fi folosit pentru a comunica cu aplicații scrise în alte limbi. Este, de asemenea, nesigur în mod implicit: deserializarea datelor de muraturi care provin dintr-o sursă neîncrezătoare pot executa un cod arbitrar, dacă datele au fost create de un atacator calificat.
Șirurile pot fi ușor scrise și citite dintr-un fișier. Numerele fac un efort mai mic, deoarece metoda read () returneaza numai siruri de caractere, care trebuie sa fie transmise unei functii precum int () , care ia un sir ca '123' si isi returneaza valoarea numerica 123. Cand vrei sa salvați mai complexe tipuri de date, cum ar fi liste imbricate și dicționare, parsarea și serializarea manuală devine complicată.
Mai degrabă decât dacă utilizatorii au în mod constant scrierea și depanarea codului pentru a salva tipuri de date complicate în fișiere, Python vă permite să utilizați formatul popular de schimb de date numit JSON (JavaScript Object Notation) . Modulul standard denumit json poate lua ierarhiile de date Python și le poate converti în reprezentări de șir; acest proces se numește serializare . Reconstruirea datelor din reprezentarea șirului se numește deserializare . Între serializarea și deserializarea, șirul reprezentând obiectul ar fi putut fi stocat într-un fișier sau date sau trimis printr-o conexiune de rețea la o anumită mașină îndepărtată.
Notă
Formatul JSON este utilizat în mod obișnuit de aplicații moderne pentru a permite schimbul de date. Mulți programatori sunt deja familiarizați cu acesta, ceea ce îl face o alegere bună pentru interoperabilitate.
Dacă aveți un obiect x , puteți vizualiza reprezentarea șirului JSON cu o linie simplă de cod:
O altă variantă a funcției dumps () , denumită dump () , serializează pur și simplu obiectul într-un fișier text . Deci, dacă f este un obiect de fișier text deschis pentru scriere, putem face acest lucru:
Pentru a decoda obiectul din nou, dacă f este un obiect de fișier text care a fost deschis pentru citire:
Această tehnică de serializare simplă poate gestiona liste și dicționare, însă serializarea instanțelor de clasă arbitrare din JSON necesită un efort suplimentar. Referința pentru modulul json conține o explicație a acestui lucru.
Vezi si
pickle - modulul pickle
Spre deosebire de JSON , pickle este un protocol care permite serializarea obiectelor Python arbitrar complexe. Ca atare, este specific pentru Python și nu poate fi folosit pentru a comunica cu aplicații scrise în alte limbi. Este, de asemenea, nesigur în mod implicit: deserializarea datelor de muraturi care provin dintr-o sursă neîncrezătoare pot executa un cod arbitrar, dacă datele au fost create de un atacator calificat.
8. Erori și excepții
Până acum mesajele de eroare nu au fost mai mult decât menționate, dar dacă ați încercat exemplele pe care le-ați văzut probabil unele. Există (cel puțin) două tipuri de erori distincte: erori de sintaxă și excepții .
8.1. Erori de sintaxă
Erorile de sintaxă, cunoscute și sub numele de erori de parsing, sunt probabil cele mai comune tipuri de reclamații pe care le obțineți în timp ce învățați încă Python:
Parserul repetă linia infracțională și afișează o mică "săgeată" care indică cel mai devreme punct din linia unde a fost detectată eroarea. Eroarea este cauzată de (sau cel puțin detectată) de tokenul precedent de săgeată: în exemplu, eroarea este detectată la funcția print () , deoarece un colon ( ':' ) lipsește înainte de aceasta. Numele fișierului și numărul liniei sunt tipărite, astfel încât să știți unde să căutați în cazul în care intrarea a venit dintr-un script.
Erorile de sintaxă, cunoscute și sub numele de erori de parsing, sunt probabil cele mai comune tipuri de reclamații pe care le obțineți în timp ce învățați încă Python:
Parserul repetă linia infracțională și afișează o mică "săgeată" care indică cel mai devreme punct din linia unde a fost detectată eroarea. Eroarea este cauzată de (sau cel puțin detectată) de tokenul precedent de săgeată: în exemplu, eroarea este detectată la funcția print () , deoarece un colon ( ':' ) lipsește înainte de aceasta. Numele fișierului și numărul liniei sunt tipărite, astfel încât să știți unde să căutați în cazul în care intrarea a venit dintr-un script.
8.2. Excepții
Chiar dacă o declarație sau o expresie este corectă din punct de vedere sintactic, poate provoca o eroare atunci când se face o încercare de ao executa. Erori detectate în timpul execuției sunt numite excepții și nu sunt necondiționat fatale: în curând veți afla cum să le gestionați în programele Python. Cele mai multe excepții nu sunt tratate de programe, cu toate acestea, și rezultă în mesaje de eroare, după cum se arată aici:
Ultimul rând al mesajului de eroare indică ce sa întâmplat. Excepțiile vin în diferite tipuri, iar tipul este tipărit ca parte a mesajului: tipurile din exemplu sunt ZeroDivisionError , NameError și TypeError . Șirul imprimat ca tip de excepție este numele excepției integrate care a apărut. Acest lucru este valabil pentru toate excepțiile integrate, dar nu este necesar să fie adevărat pentru excepțiile definite de utilizator (deși este o convenție utilă). Numele excepționale standard sunt identificatorii încorporați (nu sunt cuvinte cheie rezervate).
Restul liniei oferă detalii pe baza tipului de excepție și a ceea ce a cauzat-o.
Partea anterioară a mesajului de eroare arată contextul în care sa produs excepția, sub forma unei traseback de stivă. În general, acesta conține o linie de sursă a liniei sursă de traseback; cu toate acestea, nu va afișa liniile citite de la intrarea standard.
Excepțiile construite listează excepțiile încorporate și semnificațiile acestora.
Chiar dacă o declarație sau o expresie este corectă din punct de vedere sintactic, poate provoca o eroare atunci când se face o încercare de ao executa. Erori detectate în timpul execuției sunt numite excepții și nu sunt necondiționat fatale: în curând veți afla cum să le gestionați în programele Python. Cele mai multe excepții nu sunt tratate de programe, cu toate acestea, și rezultă în mesaje de eroare, după cum se arată aici:
Ultimul rând al mesajului de eroare indică ce sa întâmplat. Excepțiile vin în diferite tipuri, iar tipul este tipărit ca parte a mesajului: tipurile din exemplu sunt ZeroDivisionError , NameError și TypeError . Șirul imprimat ca tip de excepție este numele excepției integrate care a apărut. Acest lucru este valabil pentru toate excepțiile integrate, dar nu este necesar să fie adevărat pentru excepțiile definite de utilizator (deși este o convenție utilă). Numele excepționale standard sunt identificatorii încorporați (nu sunt cuvinte cheie rezervate).
Restul liniei oferă detalii pe baza tipului de excepție și a ceea ce a cauzat-o.
Partea anterioară a mesajului de eroare arată contextul în care sa produs excepția, sub forma unei traseback de stivă. În general, acesta conține o linie de sursă a liniei sursă de traseback; cu toate acestea, nu va afișa liniile citite de la intrarea standard.
Excepțiile construite listează excepțiile încorporate și semnificațiile acestora.
8.3. Excepții de manipulare
Este posibil să scrieți programe care să trateze excepțiile selectate. Uitați-vă la exemplul următor, care solicită utilizatorului introducerea până la introducerea unui număr întreg valid, dar permite utilizatorului să întrerupă programul (folosind Control-C sau orice suport acceptă sistemul de operare); rețineți că o întrerupere generată de utilizator este semnalizată prin ridicarea excepției KeyboardInterrupt .
- În primul rând, clauza try (declarația (e) între încercare și cu excepția cuvintelor cheie) este executat.
- Dacă nu se produce nicio excepție, clauza excepțională este omisă și executarea instrucțiunii try este terminată.
- Dacă se produce o excepție în timpul executării clauzei de încercare, restul clauzei este omisă. Apoi , în cazul în care tipul său se potrivește cu excepția numit după excepția cuvintelor cheie, clauza except este executat, iar apoi de execuție este după încercare declarația.
- Dacă apare o excepție care nu se potrivește cu excepția numită în clauza excepțională, aceasta este transmisă unor instrucțiuni exterioare de încercare ; dacă nu se găsește niciun manipulator, este o excepție nefolosită și execuția se oprește cu un mesaj așa cum este arătat mai sus.
O instrucțiune try poate avea mai mult de o excepție, cu excepția clauzei, pentru a specifica manipulatorii pentru diferite excepții. La cel mult un handler va fi executat. Handlerele gestionează exclusiv excepțiile care apar în clauza de încercare corespunzătoare, nu și în alte manipulatoare ale aceleiași instrucțiuni de încercare . O clauză excepțională poate numi mai multe excepții ca o paranteză paranteză, de exemplu:
Ultimul, cu excepția clauzei, poate omite numele de excepție, pentru a servi drept wildcard. Utilizați acest lucru cu precauție extremă, deoarece este ușor să mascați o eroare de programare reală în acest fel! De asemenea, poate fi folosit pentru a imprima un mesaj de eroare și apoi re-ridicați excepția (permițând unui apelant să facă față și excepției):
Încercare ... cu excepția declarație are o opțională clauză else , care, atunci când este prezent, trebuie să urmeze toate , cu excepția clauzelor. Este util pentru codul care trebuie executat dacă clauza de încercare nu ridică o excepție. De exemplu:
Utilizarea altfel clauza este mai bună decât adăugarea de cod suplimentar la încercare clauza deoarece evită prinderea în mod accidental o excepție care nu a fost ridicată de codul fiind protejat de încercare ... cu excepția declarație.
Când apare o excepție, poate avea o valoare asociată, cunoscută și ca argumentul excepției . Prezența și tipul argumentului depind de tipul excepției.
Clauza excepțională poate specifica o variabilă după numele excepției. Variabila este legată de o instanță de excepție cu argumentele stocate în instanța.args . Pentru comoditate, instanța de excepție definește __str __ () astfel încât argumentele pot fi imprimate direct fără a fi nevoie să se facă referință .args . De asemenea, se poate instantiza o exceptie inainte de ao ridica si de a adauga orice atribut dupa dorinta.
Dacă o excepție are argumente, ele sunt tipărite ca ultima parte ("detaliu") a mesajului pentru excepții nefolosite.
Excepții de manipulare nu se ocupă doar de excepții dacă apar imediat în clauza de încercare, dar și în cazul în care apar în interiorul funcțiilor care sunt numite (chiar indirect) în clauza de încercare. De exemplu:
Este posibil să scrieți programe care să trateze excepțiile selectate. Uitați-vă la exemplul următor, care solicită utilizatorului introducerea până la introducerea unui număr întreg valid, dar permite utilizatorului să întrerupă programul (folosind Control-C sau orice suport acceptă sistemul de operare); rețineți că o întrerupere generată de utilizator este semnalizată prin ridicarea excepției KeyboardInterrupt .
- În primul rând, clauza try (declarația (e) între încercare și cu excepția cuvintelor cheie) este executat.
- Dacă nu se produce nicio excepție, clauza excepțională este omisă și executarea instrucțiunii try este terminată.
- Dacă se produce o excepție în timpul executării clauzei de încercare, restul clauzei este omisă. Apoi , în cazul în care tipul său se potrivește cu excepția numit după excepția cuvintelor cheie, clauza except este executat, iar apoi de execuție este după încercare declarația.
- Dacă apare o excepție care nu se potrivește cu excepția numită în clauza excepțională, aceasta este transmisă unor instrucțiuni exterioare de încercare ; dacă nu se găsește niciun manipulator, este o excepție nefolosită și execuția se oprește cu un mesaj așa cum este arătat mai sus.
O instrucțiune try poate avea mai mult de o excepție, cu excepția clauzei, pentru a specifica manipulatorii pentru diferite excepții. La cel mult un handler va fi executat. Handlerele gestionează exclusiv excepțiile care apar în clauza de încercare corespunzătoare, nu și în alte manipulatoare ale aceleiași instrucțiuni de încercare . O clauză excepțională poate numi mai multe excepții ca o paranteză paranteză, de exemplu:
Ultimul, cu excepția clauzei, poate omite numele de excepție, pentru a servi drept wildcard. Utilizați acest lucru cu precauție extremă, deoarece este ușor să mascați o eroare de programare reală în acest fel! De asemenea, poate fi folosit pentru a imprima un mesaj de eroare și apoi re-ridicați excepția (permițând unui apelant să facă față și excepției):
Încercare ... cu excepția declarație are o opțională clauză else , care, atunci când este prezent, trebuie să urmeze toate , cu excepția clauzelor. Este util pentru codul care trebuie executat dacă clauza de încercare nu ridică o excepție. De exemplu:
Utilizarea altfel clauza este mai bună decât adăugarea de cod suplimentar la încercare clauza deoarece evită prinderea în mod accidental o excepție care nu a fost ridicată de codul fiind protejat de încercare ... cu excepția declarație.
Când apare o excepție, poate avea o valoare asociată, cunoscută și ca argumentul excepției . Prezența și tipul argumentului depind de tipul excepției.
Clauza excepțională poate specifica o variabilă după numele excepției. Variabila este legată de o instanță de excepție cu argumentele stocate în instanța.args . Pentru comoditate, instanța de excepție definește __str __ () astfel încât argumentele pot fi imprimate direct fără a fi nevoie să se facă referință .args . De asemenea, se poate instantiza o exceptie inainte de ao ridica si de a adauga orice atribut dupa dorinta.
Dacă o excepție are argumente, ele sunt tipărite ca ultima parte ("detaliu") a mesajului pentru excepții nefolosite.
Excepții de manipulare nu se ocupă doar de excepții dacă apar imediat în clauza de încercare, dar și în cazul în care apar în interiorul funcțiilor care sunt numite (chiar indirect) în clauza de încercare. De exemplu:
8.4. Ridicarea excepțiilor
Singurul argument pe care îl ridică indică excepția care trebuie ridicată. Aceasta trebuie să fie o instanță de excepție sau o clasă de excepție (o clasă care derivă din Excepție ).
Dacă trebuie să determinați dacă a fost ridicată o excepție, dar nu intenționați să o gestionați, o formă mai simplă a instrucțiunii de ridicare vă permite să re-ridicați excepția:
Singurul argument pe care îl ridică indică excepția care trebuie ridicată. Aceasta trebuie să fie o instanță de excepție sau o clasă de excepție (o clasă care derivă din Excepție ).
Dacă trebuie să determinați dacă a fost ridicată o excepție, dar nu intenționați să o gestionați, o formă mai simplă a instrucțiunii de ridicare vă permite să re-ridicați excepția:
8.5. Excepții definite de utilizator
Programele își pot numi excepțiile prin crearea unei noi clase de excepție (consultați clasele pentru mai multe despre clasele Python). Excepțiile ar trebui, de obicei, să fie derivate din clasa de excepție, direct sau indirect. De exemplu:
În acest exemplu, valoarea implicită __init __ () a excepției a fost înlocuită. Noul comportament creează pur și simplu atributul value . Aceasta înlocuiește comportamentul implicit al creării atributului args .
Clasele de excepție pot fi definite care fac ceea ce poate face orice altă clasă, dar de obicei sunt păstrate simple, adesea oferind doar un număr de atribute care permit ca informațiile despre eroare să fie extrase de către manipulanți pentru excepție. Când se creează un modul care poate genera mai multe erori distincte, o practică obișnuită este crearea unei clase de bază pentru excepțiile definite de acel modul și a unei subclase care să creeze clase excepționale specifice pentru diferite condiții de eroare:
Cele mai multe excepții sunt definite cu nume care se termină în "Eroare", similar cu denumirea excepțiilor standard.
Multe module standard definesc propriile excepții de a raporta erorile care pot apărea în funcțiile pe care le definesc. Mai multe informații despre clase sunt prezentate în capitolele Clase .
Programele își pot numi excepțiile prin crearea unei noi clase de excepție (consultați clasele pentru mai multe despre clasele Python). Excepțiile ar trebui, de obicei, să fie derivate din clasa de excepție, direct sau indirect. De exemplu:
În acest exemplu, valoarea implicită __init __ () a excepției a fost înlocuită. Noul comportament creează pur și simplu atributul value . Aceasta înlocuiește comportamentul implicit al creării atributului args .
Clasele de excepție pot fi definite care fac ceea ce poate face orice altă clasă, dar de obicei sunt păstrate simple, adesea oferind doar un număr de atribute care permit ca informațiile despre eroare să fie extrase de către manipulanți pentru excepție. Când se creează un modul care poate genera mai multe erori distincte, o practică obișnuită este crearea unei clase de bază pentru excepțiile definite de acel modul și a unei subclase care să creeze clase excepționale specifice pentru diferite condiții de eroare:
Cele mai multe excepții sunt definite cu nume care se termină în "Eroare", similar cu denumirea excepțiilor standard.
Multe module standard definesc propriile excepții de a raporta erorile care pot apărea în funcțiile pe care le definesc. Mai multe informații despre clase sunt prezentate în capitolele Clase .
8.6. Definirea acțiunilor de curățare
Instrucțiunea try are o altă clauză opțională, care este destinată să definească acțiunile de curățare care trebuie executate în toate circumstanțele. De exemplu:
O clauză finală este întotdeauna executată înainte de a părăsi instrucțiunea try , indiferent dacă a apărut o excepție sau nu. Atunci când a avut loc o excepție în încercați clauza și nu a fost tratată printr - o , cu excepția clauza (sau a avut loc într - o afară sau altceva clauza), acesta este re-raise după ce în cele din urmă clauza a fost executată. În cele din urmă clauză este , de asemenea , executat „pe cale de ieșire“ , atunci când orice altă clauză de încercare declarația este lăsată printr - o pauză , să continue sau a reveni declarație. Un exemplu mai complicat:
După cum puteți vedea, clauza finală este executată în orice caz. TypeError ridicată prin împărțirea două șiruri nu este tratată de către excepția clauzei și , prin urmare , reraise după ce în final clauza a fost executată.
În aplicațiile din lumea reală, clauza finală este utilă pentru eliberarea de resurse externe (cum ar fi fișiere sau conexiuni de rețea), indiferent dacă utilizarea resurselor a avut succes.
Instrucțiunea try are o altă clauză opțională, care este destinată să definească acțiunile de curățare care trebuie executate în toate circumstanțele. De exemplu:
O clauză finală este întotdeauna executată înainte de a părăsi instrucțiunea try , indiferent dacă a apărut o excepție sau nu. Atunci când a avut loc o excepție în încercați clauza și nu a fost tratată printr - o , cu excepția clauza (sau a avut loc într - o afară sau altceva clauza), acesta este re-raise după ce în cele din urmă clauza a fost executată. În cele din urmă clauză este , de asemenea , executat „pe cale de ieșire“ , atunci când orice altă clauză de încercare declarația este lăsată printr - o pauză , să continue sau a reveni declarație. Un exemplu mai complicat:
După cum puteți vedea, clauza finală este executată în orice caz. TypeError ridicată prin împărțirea două șiruri nu este tratată de către excepția clauzei și , prin urmare , reraise după ce în final clauza a fost executată.
În aplicațiile din lumea reală, clauza finală este utilă pentru eliberarea de resurse externe (cum ar fi fișiere sau conexiuni de rețea), indiferent dacă utilizarea resurselor a avut succes.
8.7. Acțiuni de curățare predefinite
Unele obiecte definesc acțiunile standard de curățare care trebuie întreprinse atunci când obiectul nu mai este necesar, indiferent dacă operația care utilizează obiectul a reușit sau nu a reușit. Uitați-vă la următorul exemplu, care încearcă să deschidă un fișier și să imprime conținutul acestuia pe ecran.
Problema cu acest cod este că ea lasă fișierul deschis pentru o perioadă nedeterminată de timp după ce această parte a codului a terminat executarea. Aceasta nu este o problemă în scenarii simple, dar poate fi o problemă pentru aplicațiile mai mari. Instrucțiunea cu permite ca obiectele ca fișierele să fie utilizate într-un mod care să asigure că acestea sunt întotdeauna curățate prompt și corect.
După executarea instrucțiunii, fișierul f este întotdeauna închis, chiar dacă a apărut o problemă în timpul procesării liniilor. Obiectele care, ca fișiere, oferă acțiuni de curățare predefinite, vor indica acest lucru în documentația lor.
Unele obiecte definesc acțiunile standard de curățare care trebuie întreprinse atunci când obiectul nu mai este necesar, indiferent dacă operația care utilizează obiectul a reușit sau nu a reușit. Uitați-vă la următorul exemplu, care încearcă să deschidă un fișier și să imprime conținutul acestuia pe ecran.
Problema cu acest cod este că ea lasă fișierul deschis pentru o perioadă nedeterminată de timp după ce această parte a codului a terminat executarea. Aceasta nu este o problemă în scenarii simple, dar poate fi o problemă pentru aplicațiile mai mari. Instrucțiunea cu permite ca obiectele ca fișierele să fie utilizate într-un mod care să asigure că acestea sunt întotdeauna curățate prompt și corect.
După executarea instrucțiunii, fișierul f este întotdeauna închis, chiar dacă a apărut o problemă în timpul procesării liniilor. Obiectele care, ca fișiere, oferă acțiuni de curățare predefinite, vor indica acest lucru în documentația lor.
9. Clasele
În comparație cu alte limbi de programare, mecanismul clasei Python adaugă clase cu un minim de sintaxă și semantică noi. Este un amestec al mecanismelor de clasă descoperite în C ++ și Modula-3. Clasele Python oferă toate caracteristicile standard ale programării orientate pe obiecte: mecanismul de moștenire a clasei permite mai multor clase de bază, o clasă derivată poate suprascrie orice metodă a clasei sau clasei sale de bază și o metodă poate apela metoda unei clase de bază cu același nume . Obiectele pot conține cantități arbitrare și tipuri de date. Așa cum este valabil și pentru module, clasele participă la natura dinamică a Python: ele sunt create în timpul execuției și pot fi modificate ulterior după crearea.
În terminologia C ++, în mod normal, membrii clasei (inclusiv membrii de date) sunt publici (cu excepția celor de mai jos , Variabile private ) și toate funcțiile membre sunt virtuale . Ca și în Modula-3, nu există contorii pentru referirea membrilor obiectului din metodele sale: funcția de metodă este declarată cu un prim argument explicit reprezentând obiectul, care este furnizat implicit de apel. Ca și în Smalltalk, clasele sunt obiecte. Aceasta oferă semantică pentru import și redenumire. Spre deosebire de C ++ și Modula-3, tipurile încorporate pot fi folosite ca clase de bază pentru extensie de către utilizator. De asemenea, ca în C ++, majoritatea operatorilor încorporați cu sintaxă specială (operatori aritmetici, subscripting etc.) pot fi redefiniți pentru instanțe de clasă.
(Lipsa terminologiei universal acceptate pentru a vorbi despre clase, voi folosi ocazional termenii Smalltalk și C ++. Aș folosi termeni Modula-3, deoarece semantica orientată pe obiecte este mai apropiată de cea a lui Python decât C ++, dar mă aștept ca puțini cititori au auzit de ea.)
9.1. Un cuvânt despre nume și obiecte
Obiectele au individualitate, iar mai multe nume (în mai multe domenii) pot fi legate la același obiect. Aceasta este cunoscută sub numele de aliasing în alte limbi. Acest lucru nu este de obicei apreciat la prima vedere la Python și poate fi ignorat în siguranță atunci când se ocupă cu tipuri de bază imuabile (numere, șiruri, tuple). Cu toate acestea, aliasingul are un efect surprinzător posibil asupra semanticii codului Python care implică obiecte mutabile, cum ar fi listele, dicționarele și cele mai multe alte tipuri. Aceasta este de obicei folosită în beneficiul programului, deoarece pseudonimurile se comportă ca indicii în anumite privințe. De exemplu, trecerea unui obiect este ieftină, deoarece doar un indicator este trecut prin implementare; și dacă o funcție modifică un obiect trecut ca argument, apelantul va vedea schimbarea - aceasta elimină necesitatea existenței a două mecanisme diferite de trecere a argumentelor ca în Pascal.
9.2. Python Scopes și Namespaces
Înainte de a introduce clase, trebuie să vă spun mai întâi despre regulile de aplicare a Python. Definițiile de clasă joacă niște trucuri îngrijite cu spații de nume și trebuie să știți cum funcționează domeniile și spațiile de nume pentru a înțelege pe deplin ce se întâmplă. De altfel, cunoștințele despre acest subiect sunt utile pentru orice programator Python avansat.
Să începem cu câteva definiții.
Un spațiu de nume este o mapare de la nume la obiecte. Cele mai multe spații de nume sunt implementate în prezent ca dicționare Python, dar în mod normal nu sunt vizibile în nici un fel (cu excepția performanței) și se pot schimba în viitor. Exemple de spații de nume sunt: setul de nume încorporate (care conțin funcții precum abs () și nume excepționale construite); numele globale dintr-un modul; și numele locale într-o invocare a funcțiilor. Într-un sens, mulțimea de atribute ale unui obiect formează și un spațiu de nume. Lucrul important de știut despre spațiile de nume este că nu există absolut nicio legătură între numele în spații de nume diferite; de exemplu, două module diferite pot să definească o funcție maximizată fără confuzie - utilizatorii modulelor trebuie să o prefixeze cu numele modulului.
Apropo, folosesc atributul cuvânt pentru orice nume care urmează un punct - de exemplu, în expresia z.real , real este un atribut al obiectului z . În mod strict vorbind, referințele la nume în module sunt referințe atribute: în expresia modname.funcname , modname este un obiect de module și funcnameeste un atribut al acestuia. În acest caz se întâmplă o mapare simplă între atributele modulului și numele globale definite în modul: aceștia împărtășesc același spațiu de nume! [1]
Atributele pot fi doar pentru citire sau scriere. În ultimul caz, este posibilă alocarea atributelor. Atributele modulului sunt inscriptibile: puteți scrie modname.the_answer = 42 . Atributele de scriere pot fi șterse și cu instrucțiunea del . De exemplu, del modname.the_answer va elimina atributul the_answer de la obiectul numit modname .
Spațiile de nume sunt create în momente diferite și au durate diferite de viață. Spațiul de nume care conține numele încorporate este creat atunci când interpretul Python pornește și nu este șters niciodată. Spațiul de nume global pentru un modul este creat atunci când se citește definiția modulului; în mod normal, numerele de module ale modulelor durează și până când interpretul se oprește. Instrucțiunile executate de invocarea de nivel superior a interpretului, fie citite dintr-un fișier script, fie interactiv, sunt considerate ca parte a unui modul numit __main__ , deci au propriul lor spațiu de nume global. (Numele încorporate trăiesc de asemenea într-un modul, acest lucru se numește builtins .)
Spațiul de nume local pentru o funcție este creat atunci când funcția este apelată și șters atunci când funcția returnează sau ridică o excepție care nu este tratată în cadrul funcției. (De fapt, uitarea ar fi o modalitate mai bună de a descrie ce se întâmplă de fapt). Desigur, invocările recursive au fiecare propriul lor spațiu de nume local.
Un domeniu este o regiune textuală a unui program Python în care un spațiu de nume este direct accesibil. "Direct accesibil" înseamnă că o referință necalificată la un nume încearcă să găsească numele în spațiul de nume.
Deși scopurile sunt determinate static, ele sunt folosite dinamic. În orice moment în timpul execuției, există cel puțin trei domenii imbricate ale căror spații de nume sunt direct accesibile:
- domeniul de aplicare cel mai intim, care este căutat în primul rând, conține numele locale
- domeniile oricărei funcții de închidere, care sunt căutate începând cu cel mai apropiat domeniu de aplicare, conțin nume non-locale, dar și non-globale
- următorul domeniu de aplicare conține numele global al modulului curent
- cel mai îndepărtat domeniu (ultimul căutat) este spațiul de nume care conține numele încorporate
Dacă un nume este declarat global, atunci toate referințele și alocările merg direct la domeniul de aplicare intermediar care conține numele global al modulului. Pentru a rebrand variabilele găsite în afara domeniului interior, se poate folosi declarația nonlocală ; dacă nu sunt declarate nonlocale, acele variabile sunt doar pentru citire (încercarea de a scrie la o astfel de variabilă va crea pur și simplu o nouă variabilă locală în interiorul domeniului interior, lăsând neschimbată variabila externă identică).
De obicei, domeniul de aplicare local se referă la numele local al funcției curente (textuale). În afara funcțiilor, domeniul de aplicare local se referă la același spațiu de nume ca și domeniul global: spațiul de nume al modulului. Definițiile de clasă plasează încă un spațiu de nume în domeniul de aplicare local.
Este important să ne dăm seama că domeniile sunt determinate textual: domeniul global al unei funcții definite într-un modul este spațiul de nume al modulului, indiferent de unde sau prin ce nume se numește funcția. Pe de altă parte, căutarea reală a numelor se face dinamic, la momentul executării - totuși, definiția limbii evoluează spre rezolvarea numelui static, la momentul "compilației", deci nu vă bazați pe rezoluția dinamică a numelui! (De fapt, variabilele locale sunt deja determinate static.)
O chestiune specială a lui Python este că - dacă nu există nicio declarație globală - atribuțiile la nume intră întotdeauna în cel mai luminos scop. Atribuțiile nu copiază date - ci doar leagă numele la obiecte. Același lucru este valabil și pentru ștergeri: instruciunea del x elimină legarea lui x din spațiul de nume referit de domeniul de aplicare local. De fapt, toate operațiunile care introduc nume noi utilizează domeniul de aplicare local: în special, declarațiile de import și definițiile funcțiilor leagă numele modulului sau al funcției în domeniul local.
Declarația globală poate fi utilizată pentru a indica faptul că anumite variabile trăiesc în sfera globală și ar trebui să revină acolo; nelocal declarație indică faptul că anumite variabile trăiesc într - un domeniu de aplicare împrejmuitor și ar trebui să fie de rebound acolo.
9.2.1. Exemple de spații și nume de case
Acesta este un exemplu care demonstrează modul de referință a diferitelor domenii și spații de nume și modul în care influența globală și nelocală afectează variația obligatorie:
Rezultatul exemplului de cod este:
Notă modul în care locale alocare (care este implicit) nu a modificat scope_test e legarea de spam - ului . Nelocal Atribuirea schimbat scope_test e legarea spam - ului , iar la nivel mondial atribuirea schimbat legării la nivel de modul.
De asemenea, puteți vedea că nu a existat nici o obligație anterioară pentru spam înainte de atribuirea globală .
9.3. O primă privire la clase
Clasele introduc un pic de sintaxă nouă, trei tipuri de obiecte noi și câteva semantici noi.
9.3.1. Sintaxa de definire a clasei
Cea mai simplă formă de definire a clasei arată astfel:
Definițiile de clasă, cum ar fi definițiile funcțiilor ( declarațiile def ) trebuie executate înainte de a avea vreun efect. (Ați putea plasa o definiție de clasă într-o ramură a unei declarații if sau în interiorul unei funcții.)
În practică, afirmațiile din interiorul unei definiții de clasă vor fi, de obicei, definiții de funcții, dar alte declarații sunt permise și uneori utile - vom reveni la aceasta mai târziu. Definițiile funcțiilor dintr-o clasă au, în mod normal, o formă specifică de listă de argumente, dictată de convențiile de apel pentru metode - din nou, acest lucru este explicat mai târziu.
Când se introduce o definiție de clasă, se creează un nou spațiu de nume și se utilizează ca domeniu de aplicare local - astfel, toate asignările la variabilele locale intră în acest nou spațiu de nume. În particular, definițiile funcțiilor leagă numele aici pentru noua funcție.
Atunci când o definiție de clasă este lăsată în mod normal (prin capăt), se creează un obiect de clasă . Acesta este, în principiu, un înveliș în jurul conținutului spațiului de nume creat de definiția clasei; vom afla mai multe despre obiectele de clasă în secțiunea următoare. S-a restabilit domeniul original original (cel în vigoare imediat înainte de definirea clasei), iar obiectul de clasă este legat aici de numele clasei din antetul de clasă definit ( ClassName din exemplu).
9.3.2. Obiecte de clasă
Elementele de clasă acceptă două tipuri de operațiuni: referințe atribute și instanțiere.
Referințele atributelor utilizează sintaxa standard folosită pentru toate referințele atributelor din Python: obj.name . Numele de atribute valide sunt toate numele care au fost în spațiul de nume al clasei atunci când a fost creat obiectul de clasă. Deci, dacă definiția clasei arăta astfel:
apoi MyClass.i și MyClass.f sunt referințe de atribute valide, returnând un întreg și un obiect de funcție, respectiv. De asemenea, pot fi atribuite atribute ale clasei, astfel încât să puteți schimba valoarea lui MyClass.i prin alocare. __doc__ este, de asemenea, un atribut valabil, returnând docstring aparținând clasei: "O clasă de exemple simple " .
Inițializarea clasei utilizează notația funcției. Doar pretindeți că obiectul de clasă este o funcție fără parametri care returnează o nouă instanță a clasei. De exemplu (presupunând clasa de mai sus):
creează o nouă instanță a clasei și atribuie acest obiect variabilei locale x .
Operația de instanțiere ("apelarea" unui obiect de clasă) creează un obiect gol. Multe clase le place să creeze obiecte cu instanțe personalizate la o stare inițială specifică. Prin urmare, o clasă poate defini o metodă specială numită __init __ () , după cum urmează:
Atunci când o clasă definește o metodă __init __ () , instanța de clasă invocă automat __init __ () pentru instanța de clasă nou creată. Astfel, în acest exemplu, o nouă instanță inițializată poate fi obținută prin:
Desigur, metoda __init __ () poate avea argumente pentru o mai mare flexibilitate. În acest caz, argumentele date operatorului instanței de clasă sunt transmise __init __ () . De exemplu,
9.3.3. Obiectele de instanță
Acum, ce putem face cu obiectele instanței? Singurele operațiuni înțelese de obiectele instanței sunt referințele atributelor. Există două tipuri de nume de atribute valide, atribute și metode de date.
atributele de date corespund "variabilelor de instanță" din Smalltalk și "membrilor de date" din C ++. Atribuiile de date nu trebuie să fie declarate; cum ar fi variabilele locale, ele apar în momentul în care sunt atribuite pentru prima dată. De exemplu, dacă x este instanța MyClass creată mai sus, următoarea bucată de cod va tipări valoarea 16 , fără a lăsa o urmă:
Alt tip de referință a atributului de instanță este o metodă . O metodă este o funcție care "aparține" unui obiect. (În Python, metoda termenului nu este unică pentru instanțele de clasă: alte tipuri de obiecte pot avea și metode, de exemplu, obiectele din listă au metode numite adăugați, inserați, ștergeți, sortați și așa mai departe.În următoarea discuție, vom folosi metoda termenului exclusiv pentru a înțelege metode ale obiectelor instanțelor de clasă, cu excepția cazului în care se specifică altfel.)
Numele de metode valide ale unui obiect de instanță depind de clasa sa. Prin definiție, toate atributele unei clase care sunt obiecte funcționale definesc metodele corespunzătoare ale instanțelor sale. Deci, în exemplul nostru, xf este o referință validă a metodei, deoarece MyClass.f este o funcție, dar xinu este, deoarece MyClass.i nu este. Dar xf nu este același lucru cu MyClass.f - este un obiect de metodă , nu un obiect de funcție.
9.3.4. Metodă Obiecte
De obicei, o metodă este chemată imediat după ce este obligată:
În exemplul MyClass , aceasta va returna șirul "hello world" . Cu toate acestea, nu este necesar să apelați imediat o metodă: xf este un obiect de metodă și poate fi stocat și apelat ulterior. De exemplu:
va continua să tipărească lumea salutului până la sfârșitul timpului.
Ce se întâmplă exact atunci când se numește o metodă? Este posibil să fi observat că xf () a fost apelat fără un argument de mai sus, chiar dacă definiția funcției pentru f () a specificat un argument. Ce sa întâmplat cu argumentul? Cu siguranță, Python ridică o excepție atunci când o funcție care necesită un argument este numită fără nici un cuvânt - chiar dacă argumentul nu este utilizat efectiv ...
De fapt, poate ați ghicit răspunsul: lucrurile speciale despre metode sunt că obiectul este trecut ca primul argument al funcției. În exemplul nostru, apelul xf () este exact echivalent cu MyClass.f (x). În general, apelarea unei metode cu o listă de argumente n este echivalentă cu apelarea funcției corespunzătoare cu o listă de argumente care este creată prin introducerea obiectului metodei înaintea primului argument.
Dacă încă nu înțelegeți cum funcționează metodele, o privire la implementarea poate clarifica problemele. Atunci când un atribut instanță este referit, care nu este un atribut de date, se caută clasa sa. Dacă numele denumește un atribut de clasă valabil, care este un obiect funcțional, un obiect de metodă este creat prin împachetarea (indicii către) obiectul instanței și obiectul funcției care au fost găsiți împreună într-un obiect abstract: acesta este obiectul metodei. Atunci când obiectul metodei este numit cu o listă de argumente, se construiește o nouă listă de argumente din obiectul instanță și din lista de argumente, iar obiectul funcțional se numește cu această nouă listă de argumente.
9.4. Alegeri aleatorii
Atributele de date suprascriu atributele metodei cu același nume; pentru a evita conflictele de nume accidentale, care pot provoca erori greu de găsit în programele mari, este înțelept să folosiți un fel de convenție care să minimizeze șansele de conflicte. Contractele posibile includ numele de metode de capitalizare, prefixarea numelor de atribute de date cu un șir unic mic (poate doar o subliniere) sau utilizarea verbelor pentru metode și substantive pentru atributele de date.
Atributele de date pot fi menționate prin metode, precum și de către utilizatorii obișnuiți ("clienți") ai unui obiect. Cu alte cuvinte, clasele nu sunt utilizabile pentru a pune în aplicare tipuri pure de date abstracte. De fapt, nimic din Python nu face posibilă ascunderea datelor ascunse - totul se bazează pe convenție. (Pe de altă parte, implementarea Python, scrisă în C, poate să ascundă complet detaliile implementării și să controleze accesul la un obiect, dacă este necesar, acesta putând fi folosit de extensiile Python scrise în C.)
Clienții ar trebui să folosească atributele de date cu grijă - clienții pot provoca daune invarianților întreținute de metode prin ștampilarea pe atributele lor de date. Rețineți că clienții pot adăuga propriile atribute de date unui obiect de instanță fără a afecta valabilitatea metodelor, atâta timp cât sunt evitate conflictele de nume - din nou, o convenție de numire poate salva o mulțime de dureri de cap aici.
Nu există o stenogramă pentru referirea atributelor de date (sau a altor metode!) Din cadrul metodelor. Consider că acest lucru mărește, de fapt, lizibilitatea metodelor: nu există nicio șansă de a confunda variabilele locale și variabilele de instanță atunci când se uită printr-o metodă.
Adesea, primul argument al unei metode se numește sine . Aceasta nu este altceva decât o convenție: numele de sine nu are absolut nici un înțeles special pentru Python. Rețineți însă că, prin nerespectarea convenției, codul dvs. poate fi mai puțin accesibil altor programatori Python și este de asemenea posibil ca un program de browser de clasă să poată fi scris care se bazează pe o astfel de convenție.
Orice obiect de funcții care este un atribut de clasă definește o metodă pentru instanțele acelei clase. Nu este necesar ca definiția funcției să fie inclusă în text în definiția clasei: atribuirea unui obiect funcțional unei variabile locale în clasă este, de asemenea, ok. De exemplu:
Acum , f , g și h sunt atribute ale clasei C care se referă la obiectele funcției și, prin urmare, toate metode ale instanțelor C - h sunt exact echivalente cu g . Rețineți că această practică servește, de obicei, doar la confuzarea cititorului unui program.
Metodele pot apela alte metode prin utilizarea atributelor metode ale argumentului auto :
Metodele pot face referire la nume globale în același mod ca și funcțiile obișnuite. Domeniul de aplicare global asociat unei metode este modulul care conține definiția sa. (O clasă nu este niciodată utilizată ca domeniu de aplicare global). În timp ce unul rareori întâlnește un motiv bun pentru utilizarea datelor globale într-o metodă, există multe utilizări legitime ale domeniului global: pentru un singur lucru, funcțiile și modulele importate în sfera globală pot să fie utilizate prin metode, precum și funcții și clase definite în acesta. De obicei, clasa care conține metoda este ea însăși definită în acest domeniu global, iar în următoarea secțiune vom găsi câteva motive bune pentru care o metodă ar dori să se refere la propria clasă.
Fiecare valoare este un obiect și, prin urmare, are o clasă (de asemenea numită tipul ei ). Este stocat ca obiect .__ class__ .
9.5. Moștenire
Bineînțeles, o caracteristică lingvistică nu ar fi demnă de denumirea "clasă" fără a susține moștenirea. Sintaxa pentru o definiție a clasei derivate arată astfel:
Numele BaseClassName trebuie definit într-un domeniu care conține definiția clasei derivate. În locul unui nume de clasă de bază, sunt permise și alte expresii arbitrare. Acest lucru poate fi util, de exemplu, atunci când clasa de bază este definită într-un alt modul:
Executarea unei definiții de clasă derivată este aceeași ca și pentru o clasă de bază. Atunci când obiectul de clasă este construit, clasa de bază este amintită. Acesta este folosit pentru rezolvarea referințelor atributului: dacă un atribut solicitat nu este găsit în clasă, căutarea continuă să se uite în clasa de bază. Această regulă este aplicată recursiv dacă clasa de bază în sine este derivată dintr-o altă clasă.
Nu este nimic special în instanțierea claselor derivate: DerivedClassName () creează o nouă instanță a clasei. Referințele la metodă sunt rezolvate după cum urmează: se caută atributul de clasă corespunzător, coborând în jos lanțul de clase de bază, dacă este necesar, iar referința metodei este validă dacă aceasta dă un obiect de funcție.
Clasele derivate pot depasi metodele din clasele de baza ale acestora. Deoarece metodele nu au privilegii speciale atunci când apelați alte metode ale aceluiași obiect, o metodă a unei clase de bază care apelează o altă metodă definită în aceeași clasă de bază poate ajunge la a apela o metodă a unei clase derivate care o suprascrie. (Pentru programatori C ++: toate metodele din Python sunt efectiv virtuale .)
O metodă suprapusă într-o clasă derivată poate, de fapt, să dorească să se extindă mai degrabă decât să înlocuiască pur și simplu metoda de clasă de bază cu același nume. Există o modalitate simplă de a apela metoda directă a clasei de bază: trebuie doar să apelați BaseClassName.methodname (auto, argumente) . Acest lucru este uneori util și pentru clienți. (Rețineți că acest lucru funcționează numai dacă clasa de bază este accesibilă ca BaseClassName în domeniul global.)
Python are două funcții încorporate care funcționează cu moștenire:
- Utilizați instanța () pentru a verifica tipul instanței: isinstance (obj, int) va fi Adevăratnumai dacă obj .__ class__ este int sau o anumită clasă derivată din int .
- Utilizați issubclass () pentru a verifica moștenirea claselor: issubclass (bool, int) este True deoarece bool este o subclasă de int . Cu toate acestea, issubclass (float, int)este False, deoarece float nu este o subclasă de int .
9.5.1. Moștenire multiplă
Python susține și o formă de moștenire multiplă. O definiție de clasă cu mai multe clase de bază arată astfel:
În cele mai multe scopuri, în cele mai simple cazuri, vă puteți gândi la căutarea atributelor moștenite de la o clasă părinte ca adâncime în primul rând, de la stânga la dreapta, fără a căuta de două ori în aceeași clasă în care există o suprapunere în ierarhie. Astfel, dacă un atribut nu este găsit în DerivedClassName , acesta este căutat în Base1 , apoi (recursiv) în clasele de bază ale Base1 și, dacă nu a fost găsit acolo, a fost căutat în Base2 și așa mai departe.
De fapt, este puțin mai complexă decât asta; ordinea de rezoluție a metodei se modifică dinamic pentru a sprijini apelurile de tip cooperativ către super () . Această abordare este cunoscută în alte limbi cu mai multe moșteniri ca metodă call-next și este mai puternică decât super-apelul găsit în limbi de mostenire singulară.
Comanda dinamică este necesară deoarece toate cazurile de moștenire multiplă prezintă una sau mai multe relații diamante (unde cel puțin una dintre clasele părinte poate fi accesată prin mai multe căi din clasa de jos). De exemplu, toate clasele moștenesc de la obiect , astfel încât orice caz de moștenire multiplă oferă mai mult de o cale pentru a ajunge la obiect. Pentru a păstra clasele de bază accesate de mai multe ori, algoritmul dinamic linearizează ordinea de căutare într-un mod care păstrează ordonarea de la stânga la dreapta specificată în fiecare clasă, care apelează fiecare părinte o singură dată și care este monotonică o clasă poate fi subclasată fără a afecta ordinea de prioritate a părinților săi). Luate împreună, aceste proprietăți fac posibilă proiectarea unor clase fiabile și extensibile cu moștenire multiplă. Pentru mai multe detalii, consultațihttp://www.python.org/download/releases/2.3/mro/ .
9.6. Variabile private
Variabilele de instanță "private" care nu pot fi accesate decât în interiorul unui obiect nu există în Python. Cu toate acestea, există o convenție care este urmată de majoritatea codului Python: un nume prefixat cu o subliniere (de exemplu, _spam ) trebuie tratat ca o parte non-publică a API-ului (indiferent dacă este o funcție, o metodă sau un membru de date) . Ar trebui să fie considerat un detaliu al implementării și să se schimbe fără notificare.
Deoarece există un caz de utilizare valabil pentru membrii de clasă privat (și anume pentru a evita ciocnirile de nume cu nume definite de subclase), există un sprijin limitat pentru un astfel de mecanism, denumit mangling nume . Orice identificator al formularului __spam (cel puțin două subliniere de frunte, cel mult un subliniere finală ) este înlocuit textual cu _classname__spam , unde classname este numele curent al clasei cu underscorele principale dezbatete . Acest mangling se face fără să se țină cont de poziția sintactică a identificatorului, atâta timp cât apare în definiția unei clase.
Numele mangling este util pentru a permite subclasele să înlocuiască metodele fără a rupe apelurile prin metoda intraclass. De exemplu:
Rețineți că regulile de mangling sunt concepute în principal pentru a evita accidentele; este încă posibil să accesați sau să modificați o variabilă considerată privată. Acest lucru poate fi util chiar și în circumstanțe speciale, cum ar fi în programul de depanare.
Observați că codul trecut la exec () sau eval () nu consideră că clasa clasei invocate este clasa curentă; acest lucru este similar cu efectul instrucțiunii globale , efectul căruia este limitat la codul care este compilat împreună. Aceeași restricție se aplică și pentru getattr () , setattr () și delattr () , precum și atunci când se face referire directă __dict__ .
9.7. Cote și sfârșituri
Uneori este util să aveți un tip de date similar cu "înregistrarea" Pascal sau C "struct", grupând împreună câteva elemente de date numite. O definiție a clasei goale va face bine:
O bucată de cod Python care așteaptă un anumit tip de date abstract poate fi adesea trecută de o clasă care emulează în schimb metodele acelui tip de date. De exemplu, dacă aveți o funcție care formatează anumite date dintr-un obiect de fișier, puteți defini o clasă cu metode read () și readline () care obțin datele dintr-un tampon șir și o transmit ca un argument.
Metoda instanței are și atribute: m .__ self__ este obiectul instanței cu metoda m () , iar m .__ func__ este obiectul funcției corespunzător metodei.
9.8. Excepțiile sunt prea
Excepțiile definite de utilizator sunt identificate și pe clase. Folosind acest mecanism este posibil să se creeze ierarhii extensibile de excepții.
În prima formă, Clasa trebuie să fie o instanță de tip sau de o clasă derivată din ea. Prima formă este o stenogramă pentru:
O clasă în afară de clauză este compatibilă cu o excepție în cazul în care este aceeași clasă sau o clasă de bază ale acestora (dar nu și invers - o clauză cu excepția listarea o clasă derivată nu este compatibilă cu o clasă de bază). De exemplu, următorul cod va imprima B, C, D în ordinea următoare:
Rețineți că dacă clauzele excepționale au fost inversate (cu excepția lui B în prealabil), ar fi imprimat B, B, B - prima potrivire cu excepția clauzei este declanșată.
Atunci când se imprimă un mesaj de eroare pentru o excepție nefolosită, este imprimat numele clasei excepției, apoi un colon și un spațiu și, în final, instanța a fost convertită la un șir folosind funcția built-in str () .
9.9. Iteratori
Până acum ați observat probabil că majoritatea obiectelor container pot fi bucle cu ajutorul unei instrucțiuni pentru :
Acest stil de acces este clar, concis și convenabil. Utilizarea iteratoarelor pătrunde și unifică Python. În spatele scenei, instrucțiunea for solicită iter () pe obiectul containerului. Funcția returnează un obiect iterator care definește metoda __next __ () care accesează elementele din container unul câte unul. Atunci când nu mai există elemente, __next __ () ridică o excepție StopIteration, care indică faptul că buclă for va termina. Puteți apela metoda __next __ () folosind următoarea () funcție încorporată; acest exemplu arată cum funcționează toate:
După ce am văzut mecanica din spatele protocolului iterator, este ușor să adăugați comportamentul iterator la clasele tale. Definiți o metodă __iter __ () care returnează un obiect cu o metodă __next __ () . Dacă clasa definește __next __ () , atunci __iter __ () se poate întoarce singură :
9.10. Generatoare
Generatoarele sunt un instrument simplu și puternic pentru crearea iteratorilor. Ele sunt scrise ca funcții obișnuite, dar utilizeazăinstrucțiunea de randament ori de câte ori doresc să returneze date. De fiecare dată cândse cheamă următorul () , generatorul revine la locul unde a rămas (își amintește toate valorile datelor și care a fost executată ultima dată). Un exemplu arată că generatoarele pot fi ușor de creat:
Orice lucru care se poate face cu generatoarele se poate face, de asemenea, cu iteratori de clasă, așa cum este descris în secțiunea anterioară. Ceea ce face generatoarele atât de compacte este că metodele __iter __ () și __next __ () sunt create automat.
O altă caracteristică cheie este aceea că variabilele locale și starea de execuție sunt salvate automat între apeluri. Acest lucru a făcut ca funcția să fie mai ușor de scris și mult mai clară decât o abordare utilizând variabile de exemplu, cum ar fi self.index și auto.data .
În plus față de crearea automată a metodei și de salvarea stării programului, atunci când generatoarele termină, ele cresc automat StopIteration . În combinație, aceste caracteristici ușurează crearea iteratorilor fără efort mai mult decât scrierea unei funcții obișnuite.
9.11. Expresii generatoare
Unele generatoare simple pot fi codificate succint ca expresii utilizând o sintaxă similară înțelegerii listei, dar cu paranteze în loc de paranteze. Aceste expresii sunt concepute pentru situațiile în care generatorul este utilizat imediat printr-o funcție de închidere. Expresiile generatoare sunt mai compacte, dar mai puțin versatile decât definițiile complete ale generatoarelor și tind să fie mai prietenoase față de memorie decât comprehensiunea echivalentă a listelor.
Exemple:
Note de subsol
[1] | Cu excepția unui singur lucru. Obiectele modulului au un atribut secret read-only numit__dict__ care returnează dicționarul utilizat pentru a implementa spațiul de nume al modulului; numele __dict__ este un atribut, dar nu un nume global. Evident, folosirea acestei metode încalcă abstractizarea implementării spațiului de nume și ar trebui să se limiteze la lucruri ca debuggerii post-mortem. Sursa : https://docs.python.org/3.3/tutorial/classes.html |