Sunteți pe pagina 1din 272

Machine Translated by Google

Început
Piton
De la novice la profesionist

A treia editie

Magnus Lie Hetland


Machine Translated by Google

Începând cu Python
De la novice la profesionist

A treia editie

Magnus Lie Hetland


Machine Translated by Google

Începând cu Python: de la novice la profesionist

Magnus Lie Hetland


Trondheim, Norvegia

ISBN-13 (pbk): 978-1-4842-0029-2 DOI ISBN-13 (electronic): 978-1-4842-0028-5


10.1007/978-1-4842-0028-5

Număr de control al Bibliotecii Congresului: 2017934891

Drepturi de autor © 2017 de către Magnus Lie Hetland

Această lucrare este supusă dreptului de autor. Toate drepturile sunt rezervate de către Editor, indiferent dacă este vorba de materialul
integral sau parțial, în special drepturile de traducere, retipărire, reutilizare a ilustrațiilor, recitare, difuzare, reproducere pe microfilme
sau în orice alt mod fizic și transmitere sau stocare a informațiilor. și regăsire, adaptare electronică, software de calculator sau prin metodologie
similară sau diferită cunoscută acum sau dezvoltată în continuare.

Numele mărcilor comerciale, siglele și imaginile pot apărea în această carte. În loc să folosim un simbol al unei mărci comerciale cu
fiecare apariție a unui nume, siglă sau imagine cu marcă comercială, folosim numele, siglele și imaginile doar într-un mod editorial și în
beneficiul proprietarului mărcii comerciale, fără intenția de a încălca marca comercială.

Utilizarea în această publicație a numelor comerciale, mărcilor comerciale, mărcilor de servicii și termenilor similari, chiar dacă nu
sunt identificați ca atare, nu trebuie să fie considerată o expresie a opiniei cu privire la faptul dacă sunt sau nu supuse drepturilor de
proprietate.

Deși sfaturile și informațiile din această carte sunt considerate a fi adevărate și exacte la data publicării, nici autorii, nici editorii, nici editorul
nu pot accepta nicio responsabilitate legală pentru eventualele erori sau omisiuni care pot fi făcute. Editorul nu oferă nicio garanție, expresă
sau implicită, cu privire la materialul conținut aici.

Director general: Welmoed Spahr


Redactor principal: Steve Anglin
Editor de dezvoltare: Matthew Moodie
Referent tehnic: Michael Thomas

Editor coordonator: Mark Powers


Editor de copiere: Kim Wimpsett
Compozitor: SPi Global
Indexator: SPi Global
Artist: SPi Global

Imagine de copertă concepută de Shutterstock

Distribuit comerțului de cărți din întreaga lume de Springer Science+Business Media New York, 233 Spring Street, 6th Floor, New York, NY
10013. Telefon 1-800-SPRINGER, fax (201) 348-4505, e-mail orders-ny@ springer sbm.com, sau vizitați www.springeronline.com. Apress
Media, LLC este un LLC din California și unicul membru (proprietar) este Springer Science + Business Media Finance Inc (SSBM Finance Inc).
SSBM Finance Inc este o corporație din Delaware.

Pentru informații despre traduceri, vă rugăm să trimiteți un e-mail la rights@apress.com, sau vizitați www.apress.com/us/services/ rights-
permission.

Titlurile Apress pot fi achiziționate în vrac pentru uz academic, corporativ sau promoțional. Versiunile de cărți electronice și licențele sunt,
de asemenea, disponibile pentru majoritatea titlurilor. Pentru mai multe informații, consultați pagina noastră de vânzări în bloc pentru imprimări
și cărți electronice la www.apress.com/bulk-sales.

Orice cod sursă sau alt material suplimentar la care se face referire de către autor în această carte este disponibil pentru cititori pentru
descărcare sau clonare de la Github prin intermediul paginii de produs a cărții, aflată la www.apress.com/9781484200292.
Pentru informații mai detaliate, vă rugăm să vizitați http://www.apress.com/us/services/source-code.

Imprimat pe hârtie fără acid


Machine Translated by Google

Pentru Kjersti și Tor.


Machine Translated by Google

Conținutul dintr-o privire

Despre autor: ����������������������������������6 ��xxv

Despre recenzorul tehnic ������������������������������� XXvii

Prefață ����������������������������������6 ��������������xxix

Introducere ����������������������������������6 �������xxxi

Capitolul 1: Hackingul instantaneu: elementele de bază ������������������������ 1

Capitolul 2: Liste și tupluri �������������������������������������� 25

Capitolul 3: Lucrul cu șiruri de caractere ������������������������������� 45

Capitolul 4: Dicționare: când indicii nu funcționează ����������� 59

Capitolul 5: Condiționale, bucle și alte instrucțiuni ����������������������������� 71

Capitolul 6: Abstracție ������������������������������������������ 101

Capitolul 7: Mai multă abstractizare ��������������������������������� 129

Capitolul 8: Excepții. ������������������������������������������� 149

Capitolul 9: Metode magice, proprietăți și iteratori � 163

Capitolul 10: Bateriile incluse. ������������������������������� 195

Capitolul 11: Fișiere și chestii ������������������������������������� 241

Capitolul 12: Interfețele grafice cu utilizatorul ������������������� 253

Capitolul 13: Suport pentru baze de date �������������������������������� 261

Capitolul 14: Programarea în rețea ����������������������� 273

Capitolul 15: Python și Web-ul ������������������������������ 289

Capitolul 16: Testare, 1-2-3 ������������������������������������������ 307

v
Machine Translated by Google

Conținutul dintr-o privire

Capitolul 17: Extinderea Python-ului �������������������������������� 321

Capitolul 18: Ambalarea programelor dvs. ������������������� 337

Capitolul 19: Programare jucăușă ������������������������� 343

Capitolul 20: Proiectul 1: Markup instantaneu ��������������������� 353

Capitolul 21: Proiectul 2: Pictarea unui tablou drăguț ��������� 373

Capitolul 22: Proiectul 3: XML pentru toate ocaziile. ������������� 383

Capitolul 23: Proiectul 4: În știri ����������������������������� 397

Capitolul 24: Proiectul 5: O petrecere virtuală a ceaiului ������������������ 409

Capitolul 25: Proiectul 6: Editare de la distanță cu CGI ��������� 425

Capitolul 26: Proiectul 7: Propriul tau de anun uri ��������� 435

Capitolul 27: Proiectul 8: Partajarea fișierelor cu XML-RPC ������� 451

Capitolul 28: Proiectul 9: Partajarea fișierelor II — Acum cu GUI! ��� 467

Capitolul 29: Proiectul 10: Jocul arcade pe care o faceți singuri ���� 475

Anexa A: Versiunea scurtă �������������������������������� 495

Anexa B: Referință Python ������������������������������� 503

Index. ����������������������������������6 ������������������ 519

vi
Machine Translated by Google

Cuprins

Despre autor: ����������������������������������6 ��xxv

Despre recenzorul tehnic ������������������������������� XXvii

Prefață ����������������������������������6 ��������������xxix

Introducere ����������������������������������6 �������xxxi

Capitolul 1: Hackingul instantaneu: elementele de bază ������������������������ 1

Interpretul interactiv. ������������������������������������������������� 1

Algo . . . Ce? ����������������������������������6 ����������������������������������6 ������������� 2

Numere și expresii ������������������������������������������������� 3

Octale hexazecimale și binar ����������������������������������6 ����������� 5

Variabile. ����������������������������������6 ������������������������ 5

Declarații ����������������������������������6 �������������������� 6

Obținerea de informații de la utilizator ����������������������������������6

Funcții ����������������������������������6 ����������������������� 8

Module ����������������������������������6 ������������������������� 9

cmath și numere complexe ����������������������������������6 �������������� 10

Înapoi în viitorul ����������������������������������6 ������������������������� 11

Salvarea și execuția programelor dvs. �������������������������������� 11

Rularea scripturilor dvs. Python dintr-un prompt de comandă ������������������������������ 13

Faceți ca scripturile dvs. să se comporte ca programele normale ��������������������������������� 13

Comentarii: ����������������������������������6 ������������������������������������������ 14

iruri de caractere ����������������������������������6 ������������������������� 14

Șiruri de caractere cu ghilimele simple și ghilimele de escape �������������������������������������������� 15

Concatenarea șirurilor de caractere ����������������������������������6 ������������������������� 16

vii
Machine Translated by Google

Cuprins

Reprezentări șiruri, str și repr ����������������������������������6 ������ 16

Șiruri lungi, șiruri brute și octeți ����������������������������������6 ������� 17

Un rezumat rapid. ����������������������������������6 ���������� 22

Funcții noi în acest capitol ����������������������������������6 �������������� 23

Ce acum? ����������������������������������6 ����������������������������������6 ���������������������������������������� 24

Capitolul 2: Liste și tupluri �������������������������������������� 25

Prezentare generală a secvenței ����������������������������������6 ������ 25

Operații de secvență comună ���������������������������������������� 26

Indexare ����������������������������������6 �������������������������������������������� 26

Tăierea felierea ����������������������������������6 ����������������������������������������������� 28

Adăugarea de secvențe ����������������������������������6 ������������������������������� 30

Înmulțirea ����������������������������������6 ������������������������������������� 31

Calitatea de membru. ����������������������������������6 ���������������������������������������� 32

Lungimea, minimă și maximă ����������������������������������6 ����������� 33

Liste: calul de muncă al lui Python ����������������������������������������������� 34

Funcția de listă ����������������������������������6 ���������������������������������� 34

Operațiuni de bază pe listă ����������������������������������6 ���������������������������� 34

Lista metodelor ����������������������������������6 ���������������������������������������� 36

Tupluri: secvențe imuabile. ������������������������������������������ 42

Un rezumat rapid. ����������������������������������6 ���������� 44

Funcții noi în acest capitol ����������������������������������6 �������������� 44

Ce acum? ����������������������������������6 ����������������������������������6 ���������������������������������������� 44

Capitolul 3: Lucrul cu șiruri de caractere ������������������������������� 45

Operații de bază cu șiruri de caractere ����������������������������������6 � 45

Formatarea șirurilor de caractere: Versiunea scurtă ��������������������������������� 45

Formatarea șirurilor de caractere: versiunea lungă ���������������������������������� 47

Nume de câmpuri de înlocuire ����������������������������������6 ������������������� 47

Conversii de bază �����������������& ����������������������������������6 ������������������������������� 48

Separatoare de lățime, precizie și mii �������������������������������������������� 49

Semne, aliniere și zero-căptușeală ����������������������������������6 �������� 50

viii
Machine Translated by Google

Cuprins

Metode de șiruri de caractere ����������������������������������6 ������������� 52

Centrul ����������������������������������6 ����������������������������������������������� 53

găsiți ����������������������������������6 ����������������������������������6 �� 53

inferioară ����������������������������������6 ����������������������������������6 54

inlocuieste ����������������������������������6 ���������������������������������������������� 55

divizat ����������������������������������6 ����������������������������������6 �� 55

fâșie ����������������������������������6 ����������������������������������6 � 56

traduceți ����������������������������������6 �������������������������������������������� 56

Este șirul meu... ����������������������������������6 ������������������������������������� 57

Un rezumat rapid. ����������������������������������6 ���������� 57

Funcții noi în acest capitol ����������������������������������6 �������������� 58

Ce acum? ����������������������������������6 ����������������������������������6 ���������������������������������������� 58

Capitolul 4: Dicționare: când indicii nu funcționează ����������� 59

Utilizări ale dicționarului ����������������������������������6 ������������ 59

Crearea și utilizarea dicționarelor. ����������������������������� 60

Funcția dict ����������������������������������6 ������������������������������ 60

Operații de bază ale dicționarului ����������������������������������6 ���������������� 61

Formatarea șirurilor de caractere cu dicționare. ����������������������������������6 ������ 63

Metode de dicționar ����������������������������������6 ���������������������������� 63

Un rezumat rapid. ����������������������������������6 ���������� 70

Funcții noi în acest capitol ����������������������������������6 �������������� 70

Ce acum? ����������������������������������6 ����������������������������������6 ���������������������������������������� 70

Capitolul 5: Condiționale, bucle și alte instrucțiuni ����������������������������� 71

Mai multe despre imprimare și importare �������������������������������������������� 71

Imprimarea mai multor argumente ����������������������������������6 ���������������� 71

Importarea a ceva ca altceva ����������������������������������������������� 72

Magie de la sarcină ����������������������������������6 �������� 73

Secvență de despachetare ����������������������������������6 �������������������������� 73

Atribuții înlănțuite ����������������������������������6 ������������������������� 75

Atribuții crescute ����������������������������������6 �������������������� 75

ix
Machine Translated by Google

Cuprins

Blocuri: Bucuria de indentare ������������������������������������������� 76

Condiții și declarații condiționale ���������������������������� 76

Deci, pentru asta sunt acele valori booleene �������������������������������������������� 76

Execuția condiționată și Declarația if �������������������������������������������� 78

else clauze ����������������������������������6 ���������������������������������������� 78

elif clauze ����������������������������������6 ������������������������������������������ 79

Blocuri de cuibărire ����������������������������������6 ������������������������������������ 79

Condiții mai complexe ����������������������������������6 �������������������� 79

Afirmații ����������������������������������6 ������������������������������������������ 84

Loops ����������������������������������6 ���������������������������� 85

bucle while ����������������������������������6 ����������������������������������������� 85

for Loops ����������������������������������6 �������������������������������������������� 86

Iterarea peste dicționare. ����������������������������������6 ������������������� 87

Unele utilități de iterație ����������������������������������6 ������������������������ 87

Ieșirea din bucle ����������������������������������6 �������������������������� 89

else clauze în bucle ����������������������������������6 ���������������������������� 92

Înțelegeri—Ușor neînțelegătoare ������������������������������������ 92

Și Trei pentru Drum ����������������������������������6 ���� 94

Nimic nu s-a întâmplat! ����������������������������������6 ������������������������������� 94

Se șterge cu del����������������������������������������������� ����������������������������������6 ���������������������������������� 94

Executarea și evaluarea șirurilor de caractere cu exec și eval �������������������������������� 96

Un rezumat rapid. ����������������������������������6 ���������� 98

Funcții noi în acest capitol ����������������������������������6 �������������� 99

Ce acum? ����������������������������������6 ����������������������������������6 ���������������������������������������� 99

Capitolul 6: Abstracție ������������������������������������������ 101

Lenea este o virtute. ����������������������������������6 ������� 101

Abstracție și structură �������������������������������������������� 102

Crearea propriilor funcții ����������������������������������������� 102

Funcții de documentare ����������������������������������6 �������������������� 103

Funcții care nu sunt într-adevăr funcții ����������������������������������6 � 104

X
Machine Translated by Google

Cuprins

Magia parametrilor ���������������������������������������������� 105

De unde vin valorile? ����������������������������������6 ����������������������������������6 � 105

Pot să modific un parametru? ����������������������������������6 ������������������ 105

De ce aș dori să-mi modific parametrii? ����������������������������������6 ���������������������������������� 107

Ce se întâmplă dacă parametrul meu este imuabil? �������������������������������� 110

Parametrii și valorile implicite ale cuvintelor cheie ����������������������������������6 ����� 111

Colectare parametrii ����������������������������������6 ���������������������� 113

Inversarea procesului ����������������������������������6 ����������������������� 115

Practică parametrii ����������������������������������6 ����� 117

Scoping ����������������������������������6 ��������������������� 118

Recursivă ����������������������������������6 ������������������ 121

Două clasice: factoriale și puterea ����������������������������������6 ������� 122

Un alt clasic: Căutare binară ���������������������������������������� 123

Un rezumat rapid. ����������������������������������6 �������� 126

Funcții noi în acest capitol ����������������������������������������� 127

Ce acum? ����������������������������������6 ������������������ 127

Capitolul 7: Mai multă abstractizare ��������������������������������� 129

Magia obiectelor. ����������������������������������6 ���� 129

Polimorfism. ����������������������������������6 ���������������������������������� 130

Polimorfismul și metodele ����������������������������������6 �������������� 131

Polimorfismul se prezintă sub mai multe forme ����������������������������������6 � 132

Încapsulare ����������������������������������6 ���������������������������������� 133

Moștenirea ����������������������������������6 ���������������������������������������� 134

Clasele ����������������������������������6 ���������������������� 135

Ce este o clasă, mai exact? ����������������������������������6 ����������������������� 135

Efectuați-vă propriile cursuri ����������������������������������6 ������������������ 135

Atribute, Funcții și Metode ����������������������������������6 ������ 137

Confidențialitate revizuită ����������������������������������6 ������������������������������� 137

Spațiul de nume al clasei ����������������������������������6 ����������������������� 139

Specificarea unei superclase �����������������6 ����������������������������������6 �������������������� 140

xi
Machine Translated by Google

Cuprins

Investigarea moștenirii ����������������������������������6 ������������������ 141

Superclasele multiple ����������������������������������6 ���������������������� 142

Interfețe și introspecție ����������������������������������6 �������������� 143

Clasele de bază abstracte �����������6 ����������������������������������6 ����������������������� 144

Câteva gânduri despre design-ul orientat pe obiecte ���������������������� 146

Un rezumat rapid. ����������������������������������6 �������� 147

Funcții noi în acest capitol ����������������������������������6 ������������ 148

Ce acum? ����������������������������������6 ����������������������������������6 ������������������������������������� 148

Capitolul 8: Excepții. ������������������������������������������� 149

Ce este o excepție? ����������������������������������6 ��� 149

A face lucrurile să meargă greșit. . . Calea dvs. ������������������������ 149

Declarația de ridicare ����������������������������������6 �������������������������� 150

Clase personalizate de excepție ����������������������������������6 ����������������� 151

Obțineți excepții ����������������������������������6 ��� 151

Uite, mamă, fără argumente! ����������������������������������6 ����������������������� 152

Mai mult decât unul, cu excepția clauzei ����������������������������������6 ������������� 153

Prinderea a două excepții cu un singur bloc ���������������������������������������������� 154

Prinderea obiectului ����������������������������������6 ���������������������������� 154

Un adevărat catchall ����������������������������������6 ���������������������������������� 155

Când totul este bine ����������������������������������6 ���������������������������������� 155

Și, în sfârșit . . .�������������������������������6 ����������������������������������6 ��������������������������������� 157

Exceptii si functii �������������������������������������������� 158

Zen-ul excepțiilor ����������������������������������6 �� 158

Nu chiar atât de excepțional ����������������������������������6 160

Un rezumat rapid. ����������������������������������6 �������� 161

Funcții noi în acest capitol ����������������������������������6 ������������ 162

Ce acum? ����������������������������������6 ����������������������������������6 ������������������������������������� 162

xii
Machine Translated by Google

Cuprins

Capitolul 9: Metode magice, proprietăți și iteratori � 163

Dacă nu utilizați Python 3 ���������������������������������������������� 163

Constructori ����������������������������������6 �������������� 164

Metode de suprascriere în general și constructorul în particular ������������� 165

Apelarea constructorului de superclasă nelegată ����������������������������������������� 167

Utilizarea Super Funcției ����������������������������������6 �������������������� 168

Acces la articole ����������������������������������6 ���������������� 169

Secvența de bază și protocolul de cartografiere ������������������������������������������� 170

Lista de subclasare, dict și str������������������������������������������� ����������������������������������6 ����������������� 172

Mai multă magie ����������������������������������6 ���������������� 173

Proprietăți ����������������������������������6 ������������������ 174

Proprietatea Funcție ����������������������������������6 ����������������������� 175

Metode statice și metodele clasei ����������������������������������6 ����� 176

__getattr__, __setattr__ și prietenii ����������������������������������6 � 177

Iteratori ����������������������������������6 ��������������������� 178

Protocolul Iterator ����������������������������������6 �������������������������� 178

Realizarea de secvențe din iteratoare ����������������������������������6 ������ 180

Generatoare ����������������������������������6 ����������������� 180

Realizarea unui generator ����������������������������������6 ���������������������������� 181

Un generator recursiv. ����������������������������������6 ����������������������� 182

Generatoare în general ����������������������������������6 ������������������������ 183

Metode de generator ����������������������������������6 ���������������������������� 184

Simularea generatoarelor ����������������������������������6 ���������������������� 185

Cele Opt Regine ����������������������������������6 �������� 186

Generatoare și backtracking ����������������������������������6 ������������ 186

Problema ����������������������������������6 ������������������������������������� 187

Reprezentarea Statului ����������������������������������6 ������������������������ 187

Găsirea conflictelor. ����������������������������������6 �������������������������������� 188

Cazul de bază. ����������������������������������6 ����������������������������������� 188

Cazul recursiv: ����������������������������������6 ���������������������������� 189

Încheierea lui������������������������������������������������ ����������������������������������6 ����������������������������������� 191

xiii
Machine Translated by Google

Cuprins

Un rezumat rapid. ����������������������������������6 �������� 192

Funcții noi în acest capitol ����������������������������������6 ������������ 193

Ce acum? ����������������������������������6 ����������������������������������6 ������������������������������������� 193

Capitolul 10: Bateriile incluse. ������������������������������� 195

Module ����������������������������������6 ��������������������� 195

Modulele sunt programe. ����������������������������������6 ���������������������� 195

Modulele sunt folosite pentru a defini lucrurile ����������������������������������6 ������ 197

Disponibilitatea modulelor dvs. ����������������������������������6 ���������� 199

Pachete ����������������������������������6 ������������������������������������������ 201

Explorarea modulelor. ����������������������������������6 ������ 202

Ce este într-un modul? ����������������������������������6 ����������������������������������6 ����������������������� 202

Obținerea de ajutor cu ajutor ����������������������������������6 ������������������������ 203

Documentație ����������������������������������6 ��������������������������������� 204

Utilizați Sursa ����������������������������������6 ���������������������������������� 204

Biblioteca standard: Câteva preferințe ������������������������������� 205

sis ����������������������������������6 ����������������������������������6 � 205

OS ����������������������������������6 ����������������������������������6 ���207

fisier de intrare ����������������������������������6 �������������������������������������������� 208

Seturi, grămezi și Deques ����������������������������������6 ���������������������� 210

ora ����������������������������������6 ������������������������������������������������� 215

aleatoriu ����������������������������������6 �������������������������������������������� 216

raftul și json ����������������������������������6 ���������������������������������� 219

re ������������������������������������������������� ����������������������������������6 ����������������������������������6 ���223

Alte module standard interesante. ����������������������������������6 ��� 237

Un rezumat rapid. ����������������������������������6 �������� 238

Funcții noi în acest capitol ����������������������������������6 ������������ 239

Ce acum? ����������������������������������6 ����������������������������������6 ������������������������������������� 239

xiv
Machine Translated by Google

Cuprins

Capitolul 11: Fișiere și chestii ������������������������������������� 241

Deschiderea fișierelor. ����������������������������������6 �������������� 241

Moduri de fișiere ����������������������������������6 ����������������������������������������� 241

Metodele de bază ale fișierului ����������������������������������6 242

Citirea și scrierea ����������������������������������6 �������������������������� 243

Ieșire de conducte ����������������������������������6 ������������������������������������ 244

Rânduri de citire și scriere ����������������������������������6 ������������������ 245

Închidere fișiere ����������������������������������6 ������������������������������������� 246

Utilizarea metodelor de bază pentru fișiere ����������������������������������6 �������������� 247

Iterare peste conținutul fișierului �������������������������������������������� 248

Câte un caracter (sau octet) la un moment dat� ����������������������������������6 ������������ 248

O linie la un moment dat ����������������������������������6 �������������������������������� 249

Citind Totul ����������������������������������6 ���������������������������� 249

Iterație de linie leneșă cu introducerea fișierului ����������������������������������6 ��������� 250

Iteratoare de fișiere ����������������������������������6 ������������������������������������� 250

Un rezumat rapid. ����������������������������������6 �������� 251

Funcții noi în acest capitol ����������������������������������6 ������������ 252

Ce acum? ����������������������������������6 ����������������������������������6 ������������������������������������� 252

Capitolul 12: Interfețele grafice cu utilizatorul ������������������� 253

Crearea unui exemplu de aplicație GUI ���������������������������������� 253

Explorare inițială ����������������������������������6 ������������������������������� 254

Aspect ����������������������������������6 ��������������������������������������������� 256

Gestionarea evenimentelor ����������������������������������6 ���������������������������������� 256

Programul final: ����������������������������������6 ������������������������������ 257

Folosind Altceva ����������������������������������6 � 259

Un rezumat rapid. ����������������������������������6 �������� 259

Ce acum? ����������������������������������6 ����������������������������������6 ������������������������������������� 259

xv
Machine Translated by Google

Cuprins

Capitolul 13: Suport pentru baze de date �������������������������������� 261

API-ul pentru baza de date Python ���������������������������������������������� 261

Variabile Globale ����������������������������������6 ��������������������������������� 262

Excepții ����������������������������������6 ���������������������������������������� 263

Conexiuni și cursore ����������������������������������6 ������������������� 263

Tipuri ���������������������6 ����������������������������������6 ���������������������������������������������� 265

SQLite și PySQLite ����������������������������������6 ���� 265

Noțiuni introductive ����������������������������������6 ��������������������������������� 267

Un exemplu de aplicație de bază de date ����������������������������������6 ���������� 267

Crearea și popularea tabelelor ����������������������������������6 ���������� 268

Căutare și tratare cu rezultate ����������������������������������6 ���� 269

Un rezumat rapid. ����������������������������������6 �������� 270

Funcții noi în acest capitol ����������������������������������6 ������������ 271

Ce acum? ����������������������������������6 ����������������������������������6 ������������������������������������� 271

Capitolul 14: Programarea în rețea ����������������������� 273

O mână de module de rețea ����������������������������������� 273

Modulul de priză ����������������������������������6 ����������������������������� 274

Modulele urllib și urllib2 ����������������������������������6 ������������� 275

Deschiderea fișierelor de la distanță ����������������������������������6 ������������������������ 276

Preluarea fișierelor de la distanță ����������������������������������6 ��������������������� 276

Alte module ������������6 ����������������������������������6 ���������������������������������� 277

SocketServer și prietenii �������������������������������������������� 278

Conexiuni multiple. ����������������������������������6 �� 279

Forking și Threading cu SocketServer �������������������������������������������� 280

I/O asincrone cu selectare și sondare ����������������������������������6 ��� 281

Răsucită ����������������������������������6 ���������������������� 283

Descărcare și instalare Twisted ����������������������������������6 ��� 284

Scrierea unui server Twisted ����������������������������������6 ��������������������� 284

xvi
Machine Translated by Google

Cuprins

Un rezumat rapid. ����������������������������������6 �������� 286

Funcții noi în acest capitol ����������������������������������6 ������������ 287

Ce acum? ����������������������������������6 ����������������������������������6 ������������������������������������� 287

Capitolul 15: Python și Web-ul ������������������������������ 289

Razuirea ecranului ����������������������������������6 ��������� 289

Parsare ordonată și XHTML. ����������������������������������6 ��������������������� 290

Ce este Tidy? ����������������������������������6 ����������������������������������6 ����������������������������������� 290

Aranjarea ����������������������������������6 ���������������������������������������� 292

Dar de ce XHTML? ����������������������������������6 ����������������������������������6 ����������������������������� 293

Utilizarea HTMLParser ����������������������������������6 ����������������������������� 293

Frumoasă supă ����������������������������������6 ���������������������������������� 295

Pagini Web dinamice cu CGI����������������6 ������������������������������������������ 296

Pasul 1: Pregătirea serverului web ����������������������������������6 ���������� 296

Pasul 2: Adăugarea liniei Pound Bang ��������������������& ����������������������������������6 ������ 297

Pasul 3: Setarea permisiunilor de fișiere ����������������������������������6 ������ 297

Riscuri de securitate CGI ����������������������������������6 ������������������������������ 298

Un script CGI simplu ����������������������������������6 ������������������������������ 298

Depanare cu cgitb ����������������������������������6 ������������������������ 299

Folosind modulul cgi ����������������������������������6 �������������������������� 300

O formă simplă. ����������������������������������6 ����������������������������������� 301

Utilizarea unui cadru Web ����������������������������������������������� 303

Alte cadre de aplicații web ����������������������������������6 ��� 303

Servicii web: răzuire făcută corect ��������������������������������� 304

RSS și prietenii ����������������������������������6 �������������������������������� 304

Apeluri de procedură de la distanță cu XML-RPC ����������������������������������6 305

SAPUN ����������������������������������6 ����������������������������������������������� 306

Un rezumat rapid. ����������������������������������6 �������� 306

Funcții noi în acest capitol ����������������������������������6 ������������ 306

Ce acum? ����������������������������������6 ����������������������������������6 ������������������������������������� 306

xvii
Machine Translated by Google

Cuprins

Capitolul 16: Testare, 1-2-3 ������������������������������������������ 307

Testați mai întâi, codificați mai târziu. ����������������������������������6 ����� 307

Specificație exactă a cerinței ����������������������������������6 ���� 307

Planificarea schimbării ����������������������������������6 ��������������������������� 309

1-2-3 (și 4) de testare ����������������������������������6 ����������������������� 310

Instrumente pentru testare ����������������������������������6 ���������� 310

doctest���������������������6 ����������������������������������6 �������������������������������������������� 310

test unitar ������������������������������������������������� ����������������������������������6 �������������������������������������������� 312

Dincolo de testele unitare ����������������������������������6 �������� 315

Verificarea codului sursă cu PyChecker și PyLint. ��������������������������������� 315

Profilare ����������������������������������6 ������������������������������������������� 318

Un rezumat rapid. ����������������������������������6 �������� 319

Funcții noi în acest capitol ����������������������������������6 ������������ 320

Ce acum? ����������������������������������6 ����������������������������������6 ������������������������������������� 320

Capitolul 17: Extinderea Python-ului �������������������������������� 321

Cel mai bun din ambele lumi ����������������������������������6 321

Calea cu adevărat ușoară: Jython și IronPython ��������������������� 322

Scrierea extensiilor C ����������������������������������6 ��� 325 O înghițitură de …

SWIG��������������������������������������� ����������������������������������6 ��������������������

Ce face? ����������������������������������6 ����������������������������������6 ����������������������������� 327

Eu prefer Pi ����������������������������������6 ������������������������������������������� 327

Fișierul interfeței ����������������������������������6 �������������������������������� 328

Rularea SWIG������������������������������������������������ ����������������������������������6 ����������������������������������� 328

Compilarea, conectarea și utilizarea ����������������������������������6 ��������������� 329

O scurtătură prin pădurea magică a compilatorilor ���������������������������������� 330

Hacking-o pe cont propriu. ����������������������������������6 ������������������������ 330

Numărătoarea referințelor ����������������������������������6 �������������������������� 331

Un cadru pentru extensii. ����������������������������������6 ��������������� 332

Palindromuri, Detartrate1 pentru plăcerea dvs. ���������������������������������������� 333

xviii
Machine Translated by Google

Cuprins

Un rezumat rapid. ����������������������������������6 �������� 335

Funcții noi în acest capitol ����������������������������������6 ������������ 336

Ce acum? ����������������������������������6 ����������������������������������6 ������������������������������������� 336

Capitolul 18: Ambalarea programelor dvs. ������������������� 337

Noțiuni de bază pentru Setuptools ����������������������������������6 ������� 337

Încheierea lucrurilor ����������������������������������6 ���� 339

Compilarea Extensiilor ����������������������������������6 � 340

Crearea de programe executabile cu py2exe �������������������� 341

Un rezumat rapid. ����������������������������������6 �������� 342

Funcții noi în acest capitol ����������������������������������6 ������������ 342

Ce acum? ����������������������������������6 ����������������������������������6 ������������������������������������� 342

Capitolul 19: Programare jucăușă ������������������������� 343

De ce Jucăuș? ����������������������������������6 ���������������� 343

Jujitsu-ul de programare ������������������������������������������� 343

Crearea de prototipuri ����������������������������������6 ���������������� 344

Configurație ����������������������������������6 ������������� 345

Extragerea constantelor. ����������������������������������6 ������������������������� 345

Fișiere de configurare ����������������������������������6 ����������������������������� 346

Înregistrare în jurnal ����������������������������������6 ���������������������� 348

Dacă nu poți fi deranjat ����������������������������������6 �� 349

Dacă doriți să aflați mai multe 350

Un rezumat rapid. ����������������������������������6 �������� 350

Ce acum? ����������������������������������6 ����������������������������������6 ������������������������������������� 351

Capitolul 20: Proiectul 1: Markup instantaneu ��������������������� 353

Care este problema? ����������������������������������6 ����������������������������������6 353

Instrumente utile ����������������������������������6 ���������������� 354

Pregătiri ����������������������������������6 �������������� 354

xix
Machine Translated by Google

Cuprins

Prima punere în aplicare ����������������������������������6 �� 355

Găsirea de blocuri de text ����������������������������������6 ������������������������� 355

Adăugarea unor markupuri ����������������������������������6 ������������������������ 356

A doua punere în aplicare ���������������������������������������������� 358

Handlers ����������������������������������6 ������������������������������������������ 358

O Superclasă de Handler ����������������������������������6 ������������������������ 359

Regulile ����������������������������������6 ����������������������������������������������� 360

O superclasă de regulă ����������������������������������6 ������������������������������� 361

Filtre. ����������������������������������6 ���������������������������������������������� 361

Analizorul ����������������������������������6 ����������������������������������������� 362

Construirea regulilor și filtrelor ����������������������������������6 ������ 363

Pune totul laolaltă ����������������������������������6 �������������������������� 366

Explorare suplimentară ����������������������������������6 ����� 370

Ce acum? ����������������������������������6 ����������������������������������6 ������������������������������������� 371

Capitolul 21: Proiectul 2: Pictarea unui tablou drăguț ��������� 373

Care este problema? ����������������������������������6 ����������������������������������6 373

Instrumente utile ����������������������������������6 ���������������� 374

Pregătiri ����������������������������������6 �������������� 374

Prima punere în aplicare ����������������������������������6 �� 375

Desenare cu ReportLab ����������������������������������6 �������������������� 375

Construirea unor polilinii. ����������������������������������6 ������������ 376

Scrierea Prototipului ����������������������������������6 ������������������������� 377

A doua punere în aplicare ���������������������������������������������� 379

Obținerea datelor. ����������������������������������6 ��������������������������������� 379

Utilizarea clasei LinePlot ����������������������������������6 ��������������������� 380

Explorare suplimentară ����������������������������������6 ����� 382

Ce acum? ����������������������������������6 ����������������������������������6 ������������������������������������� 382

xx
Machine Translated by Google

Cuprins

Capitolul 22: Proiectul 3: XML pentru toate ocaziile. ������������� 383

Care este problema? ����������������������������������6 ����������������������������������6 383

Instrumente utile ����������������������������������6 ���������������� 384

Pregătiri ����������������������������������6 �������������� 385

Prima punere în aplicare ����������������������������������6 �� 386

Crearea unui handler simplu de conținut ����������������������������������6 ����� 386

Crearea paginilor HTML ����������������������������������6 ������������������������ 388

A doua punere în aplicare ���������������������������������������������� 391

O clasă de amestecare a dispecerului ����������������������������������6 ��������������������� 391

Factorizarea antetului, a subsolului și a gestionării implicite ������������������������������ 392

Suport pentru directoare ����������������������������������6 ���������������������� 393

Managerii de evenimente ����������������������������������6 ����������������������������� 394

Explorare suplimentară ����������������������������������6 ����� 396

Ce acum? ����������������������������������6 ����������������������������������6 ������������������������������������� 396

Capitolul 23: Proiectul 4: În știri ����������������������������� 397

Care este problema? ����������������������������������6 ����������������������������������6 397

Instrumente utile ����������������������������������6 ���������������� 398

Pregătiri ����������������������������������6 �������������� 398

Prima punere în aplicare ����������������������������������6 �� 398

A doua punere în aplicare ���������������������������������������������� 400

Explorare suplimentară ����������������������������������6 ����� 407

Ce acum? ����������������������������������6 ����������������������������������6 ������������������������������������� 408

Capitolul 24: Proiectul 5: O petrecere virtuală a ceaiului ������������������ 409

Care este problema? ����������������������������������6 ����������������������������������6 409

Instrumente utile ����������������������������������6 ���������������� 409

Pregătiri ����������������������������������6 �������������� 410

xxi
Machine Translated by Google

Cuprins

Prima punere în aplicare ����������������������������������6 �� 411

Clasa ChatServer ����������������������������������6 ������������������������ 411

Clasa ChatSession ����������������������������������6 ���������������������� 412

Împreună ����������������������������������6 ������������������������������ 414

A doua punere în aplicare ���������������������������������������������� 416

Interpretarea de bază a comenzii ����������������������������������6 ���������� 416

Camere����������������������6 ����������������������������������6 ��������������������������������������������� 417

Camere de autentificare și deconectare ����������������������������������6 �������������������� 417

Sala de chat principală ����������������������������������6 ��������������������������� 418

Noul Server ����������������������������������6 ��������������������������������� 418

Explorare suplimentară ����������������������������������6 ����� 424

Ce acum? ����������������������������������6 ����������������������������������6 ������������������������������������� 424

Capitolul 25: Proiectul 6: Editare de la distanță cu CGI ��������� 425

Care este problema? ����������������������������������6 ����������������������������������6 425

Instrumente utile ����������������������������������6 ���������������� 425

Pregătiri ����������������������������������6 �������������� 426

Prima punere în aplicare ����������������������������������6 �� 426

A doua punere în aplicare ���������������������������������������������� 427

Crearea formularului de nume de fișier ����������������������������������6 ��������������� 428

Scrierea script-ului editorului ����������������������������������6 ��������������������� 428

Scrierea script-ului de salvare ����������������������������������6 ����������������������� 429

Rularea Editorului ����������������������������������6 ������������������������������ 431

Explorare suplimentară ����������������������������������6 ����� 432

Ce acum? ����������������������������������6 ����������������������������������6 ������������������������������������� 433

Capitolul 26: Proiectul 7: Propriul tau de anun uri ��������� 435

Care este problema? ����������������������������������6 ����������������������������������6 435

Instrumente utile ����������������������������������6 ���������������� 436

Pregătiri ����������������������������������6 �������������� 436

Prima punere în aplicare ����������������������������������6 �� 437

A doua punere în aplicare ���������������������������������������������� 442

xxii
Machine Translated by Google

Cuprins

Scrierea scriptului principal ����������������������������������6 ����������������������� 442

Scrierea script-ului de vizualizare ����������������������������������6 ����������������������� 443

Scrierea Script-ului de editare ����������������������������������6 ������������������������� 445

Scrierea script-ului de salvare ����������������������������������6 ����������������������� 446

Încercați-l ����������������������������������6 ���������������������������������������� 447

Explorare suplimentară ����������������������������������6 ����� 449

Ce acum? ����������������������������������6 ����������������������������������6 ������������������������������������� 449

Capitolul 27: Proiectul 8: Partajarea fișierelor cu XML-RPC ������� 451

Care este problema? ����������������������������������6 ����������������������������������6 451

Instrumente utile ����������������������������������6 ���������������� 452

Pregătiri ����������������������������������6 �������������� 453

Prima punere în aplicare ����������������������������������6 �� 453

Implementarea unui Nod Simplu ����������������������������������6 ������������� 453

Încercarea primei implementări ����������������������������������6 ���� 458

A doua punere în aplicare ���������������������������������������������� 459

Crearea interfeței client ����������������������������������6 ��������������� 460

Strângerea de excepții ����������������������������������6 ���������������������������� 461

Validarea numelor de fișiere ����������������������������������6 ������������������������ 461

Încercarea celei de-a doua implementări ������������������������������������������������� 466

Explorare suplimentară ����������������������������������6 ����� 466

Ce acum? ����������������������������������6 ����������������������������������6 ������������������������������������� 466

Capitolul 28: Proiectul 9: Partajarea fișierelor II — Acum cu GUI! ��� 467

Care este problema? ����������������������������������6 ����������������������������������6 467

Instrumente utile ����������������������������������6 ���������������� 467

Pregătiri ����������������������������������6 �������������� 467

Prima punere în aplicare ����������������������������������6 �� 468

A doua punere în aplicare ���������������������������������������������� 470

Explorare suplimentară ����������������������������������6 ����� 472

Ce acum? ����������������������������������6 ����������������������������������6 ������������������������������������� 473

xxiii
Machine Translated by Google

Cuprins

Capitolul 29: Proiectul 10: Jocul arcade pe care o faceți singuri ���� 475

Care este problema? ����������������������������������6 ����������������������������������6 475

Instrumente utile ����������������������������������6 ���������������� 476

joc de piață ����������������������������������6 �������������������������������������������� 476

pygame.locals. ����������������������������������6 ������������������������������������ 476

pygame.display. ����������������������������������6 ���������������������������������� 476 pygame.font������������

����������������������������������6 ����������������������������������6 ������������������������� 477

pygame.sprite���������������������� ����������������������������������6 ����������������������������������6 ����������� 477

pygame.mouse �������������������6 ����������������������������������6 ���������������������������������� 477

pygame.event �����������������6 ����������������������������������6 ������������������������������������ 478

pygame.image ����������6 ����������������������������������6 ����������������������������������� 478

Pregătiri ����������������������������������6 �������������� 478

Prima punere în aplicare ����������������������������������6 �� 479

A doua punere în aplicare ���������������������������������������������� 482

Explorare suplimentară ����������������������������������6 ����� 493

Ce acum? ����������������������������������6 ����������������������������������6 ������������������������������������� 493

Anexa A: Versiunea scurtă �������������������������������� 495

Elementele de bază ����������������������������������6 ������������������ 495

Funcții ����������������������������������6 ������������������� 497

Obiecte și chestii. . .�������������������������������6 ����������������������������������6 � 498

Unele capete libere ����������������������������������6 �������� 501

Anexa B: Referință Python ������������������������������� 503

Expresii: ����������������������������������6 ��������������� 503

Declarații ����������������������������������6 ���������������� 513

Declarații simple ����������������������������������6 ���������������������������� 513

Declarații compuse ����������������������������������6 ��������������������� 516

Index. ����������������������������������6 ������������������ 519

xxiv
Machine Translated by Google

Despre autor

Magnus Lie Hetland este un programator Python cu experiență, care a folosit


limbajul de la sfârșitul anilor 1990. De asemenea, este profesor asociat de informatică
la Universitatea Norvegiană de Știință și Tehnologie, unde este specializat în analiza
și proiectarea algoritmilor. Hetland este autorul cărții Python Algorithms.

xxv
Machine Translated by Google

Despre examinatorul tehnic

Michael Thomas a lucrat în dezvoltarea de software de mai bine de 20


de ani ca colaborator individual, lider de echipă, manager de program și
vicepreședinte de inginerie. Michael are peste 10 ani de experiență în
lucrul cu dispozitive mobile. Accentul său actual este în sectorul medical,
folosind dispozitive mobile pentru a accelera transferul de informații între
pacienți și furnizorii de servicii medicale.

xxvii
Machine Translated by Google

Prefa ă

Ca să citez vechea melodie a lui Monty Python: „Aici vine altul / Aici vine din nou / Aici vine altul / Când se va termina
vreodată?” De la ediția anterioară, Python 3 a devenit mult mai răspândit, așa că această ediție a trecut complet în lumea
Py3. Au existat și alte schimbări, cu pachete din ecosistemul Python care vin și pleacă și practicile de codificare au intrat și
demodat. Acolo unde a fost necesar sau util, cartea a fost rescrisă, dar originile ei sunt încă vizibile. De exemplu, când
originalul Practical Python a apărut la începutul mileniului, Usenet era încă în uz semi-răspândit, deși în zilele noastre
majoritatea utilizatorilor de internet probabil nici nu au auzit de el. Deci, când al patrulea proiect de cod (Capitolul 23) implică
conectarea la un server NNTP, aceasta este mai mult o curiozitate istorică decât abilități pe care probabil să le aplicați direct
într-o carieră de programare mainstream. Totuși, am păstrat unele dintre aceste părți mai ciudate ale cărții, deoarece încă
funcționează bine ca exemple de programare și fac parte din istoria cărții.

Toți cei care au ajutat edițiile anterioare să vadă lumina zilei merită în continuare la fel de multe mulțumiri ca înainte.
De data aceasta, aș dori în special să-mi mulțumesc lui Mark Powers, care a fost un model al răbdării atunci când progresul
meu a zguduit. De asemenea, aș dori să-i mulțumesc lui Michael Thomas, care a făcut o treabă grozavă verificând
aspectele tehnice ale cărții (… și subliniind toate declarațiile tipărite în stil Python 2 pe care le-am ratat; sper că le-am primit
pe toate). Sper să vă placă această ediție actualizată, chiar dacă, așa cum spune Terry Jones despre melodia menționată
inițial, „Evident că ar fi mai bine cu o orchestră completă”.

Prefață la a doua ediție


Iată-o — o nouă ediție strălucitoare a Beginning Python. Dacă luați în considerare predecesorul său, Practical Python,
aceasta este de fapt a treia ediție și o carte în care am fost implicat în cea mai mare parte a unui deceniu. În acest timp,
Python a cunoscut multe schimbări interesante și am făcut tot posibilul să-mi actualizez introducerea în limbaj. În acest
moment, Python se confruntă, poate, cu cea mai marcată tranziție dintr-un timp foarte lung: introducerea versiunii 3. În
timp ce scriu asta, versiunea finală nu a ieșit încă, dar caracteristicile sunt clar definite și sunt disponibile versiuni de
lucru. O provocare interesantă legată de această revizuire a limbii este că nu este compatibilă cu versiunea inversă. Cu alte
cuvinte, nu adaugă pur și simplu funcții pe care le-aș putea alege în scrisul meu. De asemenea, schimbă limbajul existent,
astfel încât anumite lucruri care sunt adevărate pentru Python 2.5 nu mai sunt valabile.

Dacă ar fi fost clar că întreaga comunitate Python va trece instantaneu la noua versiune și va actualiza tot codul
moștenit, aceasta nu ar fi o problemă. Pur și simplu descrieți noua limbă! Cu toate acestea, există o mulțime de coduri scrise
pentru versiuni mai vechi și probabil că se vor mai scrie multe, până când versiunea 3 va fi acceptată universal ca The Way
To Go™.
Deci, cum am scăpat eu din murătura asta? În primul rând, deși există schimbări incompatibile,
cea mai mare parte a limbii rămâne aceeași. Prin urmare, dacă aș scrie în întregime despre Python 2.5, ar fi în mare parte
corect pentru Python 3 (și chiar mai mult pentru lansarea însoțitoare, 2.6). În ceea ce privește părțile care nu vor mai fi
corecte, am fost puțin conservator și am presupus că adoptarea completă a versiunii 3 va dura ceva timp. Am bazat cartea
în primul rând pe 2.5 și am notat lucruri care se vor schimba de-a lungul textului. În plus, am inclus Anexa D, care vă oferă o
imagine de ansamblu asupra principalelor modificări. Cred că acest lucru va funcționa pentru majoritatea cititorilor.

xxix
Machine Translated by Google

Prefa ă

În scrierea acestei a doua ediții, am avut mult ajutor de la mai multe persoane. La fel ca și în cazul celor două
versiuni anterioare (prima ediție și, înainte de ea, Practical Python), Jason Gilmore m-a făcut să încep și a jucat un rol
important în derularea proiectului. Pe măsură ce a evoluat, Richard Dal Porto, Frank Pohlmann și Dominic Shakeshaft
au jucat un rol esențial în menținerea acesteia. Richard Taylor a jucat cu siguranță un rol crucial în a se asigura că codul
este corect (și dacă tot nu este, eu sunt cel de vină), iar Marilyn Smith a făcut o treabă grozavă în reglarea scrisului meu.
Mulțumirile mele se adresează și altor membri ai personalului Apress, inclusiv Liz Berry, Beth Christmas, Steve Anglin și
Tina Nielsen, precum și diverși cititori care au oferit errate și sugestii utile, inclusiv Bob Helmbold și Waclaw Kusnierczyk.
De asemenea, sunt, desigur, încă recunoscător tuturor celor care au ajutat la obținerea primelor două încarnări ale
acestei cărți pe rafturi.

Prefață la prima ediție


Acum câțiva ani, Jason Gilmore m-a abordat pentru a scrie o carte pentru Apress. Mi-a citit tutorialele online
Python și a vrut să scriu o carte într-un stil similar. Eram flatat, entuziasmat și doar puțin nervos. Singurul lucru
care m-a îngrijorat cel mai mult a fost cât timp va dura și cât de mult ar interfera cu studiile mele (eram doctorand la
acea vreme). Sa dovedit a fi o întreprindere destul de mare și mi-a luat mult mai mult decât mă așteptam să termin.

Din fericire, nu a interferat prea mult cu munca mea de la școală și am reușit să-mi iau diploma fără întârzieri.

Anul trecut, Jason m-a contactat din nou. Apress dorea o versiune extinsă și revizuită a cărții mele. A fost
ma intereseaza? La acea vreme, eram ocupat să mă stabilesc într-o nouă poziție de procesor asociat, în timp ce îmi
petreceam tot timpul liber interpretând Peer Gynt, așa că din nou timpul a devenit problema majoră. În cele din
urmă (după ce lucrurile s-au aranjat puțin și am avut ceva mai mult timp de liber), am fost de acord să fac cartea și
acesta (după cum sunt sigur că ați adunat) este rezultatul. Cea mai mare parte a materialului este preluată din prima
versiune a cărții, Practical Python (Apress, 2002). Materialul existent a fost complet revizuit, pe baza modificărilor
recente din limbajul Python, și au fost adăugate câteva capitole noi. O parte din materialul vechi a fost, de asemenea,
redistribuit pentru a găzdui noua structură. Am primit o mulțime de feedback pozitiv de la cititori despre prima
versiune. Sper că am reușit să păstrez ceea ce le-a plăcut oamenilor și să adaug mai mult la fel.
Fără ajutorul și încurajarea persistentă din partea mai multor persoane, această carte nu ar fi fost niciodată
scrisă. Le mulțumesc din suflet tuturor. În special, aș dori să mulțumesc echipei care a lucrat direct cu mine în procesul
de scriere a cărții: Jason Gilmore, pentru că a demarat proiectul și l-a îndreptat în direcția corectă; Beckie Stones,
pentru că ține totul împreună; Jeremy Jones și Matt Moodie pentru comentariile și perspectivele lor tehnice; și Lindei
Marousek pentru că a fost atât de răbdătoare cu mine. De asemenea, sunt recunoscător restului echipei pentru că a
făcut procesul la fel de simplu cum a fost. Dar această carte nu ar fi ceea ce este fără mai mulți oameni care au lucrat
cu mine la versiunea anterioară: aș dori să le mulțumesc lui Jason Gilmore și Alex Martelli pentru editarea lor tehnică
excelentă (Jason pentru întreaga carte și Alex pentru prima jumătate) și pentru a depăși obligația de a oferi sfaturi și
sugestii; Erin Mulligan și Tory McLearn pentru că m-au ținut de mână în timpul procesului și pentru că m-au înghiontat
atunci când a fost nevoie; Nancy Rapoport pentru ajutorul ei la lustruirea prozei mele; și Grace Wong pentru că a
oferit răspunsuri atunci când nimeni altcineva nu putea. Pete Shinners mi-a oferit câteva sugestii utile cu privire la
jocul din Project 10, pentru care sunt foarte recunoscător. Moralul meu a fost, de asemenea, puternic stimulat de
câteva e-mailuri încurajatoare de la cititori mulțumiți — mulțumesc! În cele din urmă, aș dori să mulțumesc familiei și
prietenilor mei, precum și iubitei mele, Ranveig, pentru că m-au suportat în timp ce scriam această carte.

xxx
Machine Translated by Google

Introducere

Programul AC este ca un dans rapid pe un ring de dans nou epilat cu ceară de către oameni care poartă aparate de ras.

— Waldi Ravens

C++: Greu de învățat și construit pentru a rămâne așa.

-Anonim

Java este, în multe privințe, C++ – –.

— Michael Feldman

Și acum pentru ceva complet diferit. . .

— Circul zburător al lui Monty Python

Am început această introducere cu câteva citate pentru a da tonul cărții, care este destul de informală. În speranța
de a face o lectură ușoară, am încercat să abordez tema programării Python cu o doză sănătoasă de umor și fidel
tradițiilor comunității Python, o mare parte din acest umor este legat de schițele Monty Python. În consecință, unele
dintre exemplele mele pot părea puțin stupide; Sper că mă vei îndura. (Și, da, numele Python este derivat din Monty
Python, nu de la șerpii aparținând familiei Pythonidae.) În această introducere, vă dau o privire rapidă la ce este
Python, de ce ar trebui să-l folosiți, cine îl folosește, cine publicul vizat al acestei cărți este și cum este organizată cartea.

Deci, ce este Python și de ce ar trebui să-l folosești? Pentru a cita o veche informație oficială, este „un limbaj
de programare de nivel înalt interpretat, orientat pe obiecte, cu semantică dinamică”. Mulți dintre acești termeni
vor deveni clari pe măsură ce citiți această carte, dar esențialul este că Python este un limbaj de programare care știe
să nu vă îndepărteze atunci când vă scrieți programele. Vă permite să implementați funcționalitatea dorită fără nicio
bătaie de cap și vă permite să scrieți programe care sunt clare și lizibile (mult mai mult decât programele din majoritatea
celorlalte limbaje de programare populare în prezent).
Chiar dacă Python ar putea să nu fie la fel de rapid ca limbajele compilate, cum ar fi C sau C++, ceea ce
economisiți în timpul de programare va merita probabil să îl utilizați și, în majoritatea programelor, diferența de
viteză nu va fi oricum vizibilă. Dacă sunteți un programator C, puteți implementa cu ușurință părțile critice ale
programului dvs. în C la o dată ulterioară și le puteți interopera cu părțile Python. Dacă nu ați făcut nicio programare
înainte (și poate că sunteți puțin confuz de referințele mele la C și C++), combinația de simplitate și putere a lui Python
îl face o alegere ideală ca punct de plecare.
Deci, cine folosește Python? De când Guido van Rossum a creat limba la începutul anilor 1990, urmărirea
acesteia a crescut constant, iar interesul a crescut considerabil în ultimii câțiva ani. Python este utilizat pe scară largă
pentru sarcini de administrare a sistemului (este, de exemplu, o componentă vitală a mai multor distribuții Linux), dar
este folosit și pentru a preda programarea pentru începători. Administrația Națională pentru Aeronautică și Spațiu
(NASA) din SUA utilizează Python atât pentru dezvoltare, cât și ca limbaj de scripting în mai multe dintre sistemele sale. Industrial

xxxi
Machine Translated by Google

Introducere

Light & Magic folosește Python în producția sa de efecte speciale pentru lungmetraje cu buget mare. Yahoo! îl folosește (printre
altele) pentru a-și gestiona grupurile de discuții. Google l-a folosit pentru a implementa multe componente ale crawler-ului și
motorului său de căutare. Python este folosit în domenii atât de diverse precum jocurile pe calculator și bioinformatica. În curând
s-ar putea la fel de bine să se întrebe: „Cine nu folosește Python?”
Această carte este pentru cei dintre voi care doresc să învețe cum să programeze în Python. Este destinat să se potrivească
unui public larg, de la programator neofit până la expertiză avansată a computerelor. Dacă nu ați mai programat niciodată, ar
trebui să începeți prin a citi capitolul 1 și continuă până când descoperi că lucrurile devin prea avansate pentru tine (dacă, într-
adevăr, o fac). Atunci ar trebui să începi să exersezi și să scrii niște programe proprii. Când este momentul potrivit, vă puteți
întoarce la carte și puteți continua cu lucrurile mai complicate.
Dacă știți deja să programați, o parte din materialul introductiv ar putea să nu fie nou pentru dvs. (deși probabil vor
fi câteva detalii surprinzătoare ici și colo). Puteți parcurge primele capitole pentru a vă face o idee despre cum funcționează
Python sau poate citiți Anexa A, care se bazează pe tutorialul meu online Python „Instant Python”. Vă va aduce la curent cu cele
mai importante concepte Python.
După ce obțineți imaginea de ansamblu, puteți sări direct la capitolul 10 (care descrie bibliotecile standard Python).

Ultimele zece capitole prezintă zece proiecte de programare, care prezintă diferite capacități ale limbajului Python.
Aceste proiecte ar trebui să fie de interes atât pentru începători, cât și pentru experți. Deși unele dintre materialele din proiectele
ulterioare pot fi puțin dificile pentru un programator fără experiență, ar trebui să fie posibilă urmărirea în ordine a proiectelor
(după citirea materialului din prima parte a cărții).
Proiectele abordează o gamă largă de subiecte, dintre care majoritatea vă vor fi foarte utile atunci când scrieți programe
proprii. Veți învăța cum să faceți lucruri care vă pot părea complet inaccesibile în acest moment, cum ar fi crearea unui server de
chat, a unui sistem de partajare de fișiere peer-to-peer sau a unui joc grafic pe computer cu drepturi depline. Deși o mare parte din
material poate părea greu la prima vedere, cred că vei fi surprins de cât de ușor este cu adevărat majoritatea. Dacă doriți să
descărcați codul sursă, acesta este disponibil din secțiunea Cod sursă/Descărcare a site-ului web Apress (http://www.apress.com).

Ei bine, asta este. Întotdeauna mi se pare că prezentările lungi sunt puțin plictisitoare, așa că vă las să continuați cu dvs
Pythoneering, fie în capitolul 1 sau în Anexa A. Mult noroc și hacking fericit.

xxxii
Machine Translated by Google

CAPITOLUL 1

Hacking instantaneu: elementele de bază

Este timpul să începeți hacking-ul.1 În acest capitol, învățați cum să preluați controlul asupra computerului dvs. vorbind
o limbă pe care o înțelege: Python. Nimic aici nu este deosebit de dificil, așa că dacă cunoașteți principiile de bază ale
modului în care funcționează computerul dvs., ar trebui să puteți urma exemplele și să le încercați singur. Voi trece prin
elementele de bază, începând cu cele extrem de simple, dar pentru că Python este un limbaj atât de puternic, veți putea în
curând să faceți lucruri destul de avansate.
Pentru a începe, trebuie să instalați Python sau să verificați că îl aveți deja instalat. Dacă rulați macOS sau Linux/
UNIX, deschideți un terminal (aplicația Terminal pe un Mac), introduceți python și apăsați Enter. Ar trebui să primiți un
mesaj de bun venit, care se termină cu următoarea solicitare:

>>>

Dacă o faceți, puteți începe să introduceți comenzile Python imediat. Rețineți, totuși, că este posibil să aveți o versiune veche
de Python. Dacă prima linie începe cu Python 2 mai degrabă decât cu Python 3, s-ar putea să doriți să instalați oricum o
versiune mai nouă, deoarece Python 3 introduce mai multe modificări nerespective.
Detaliile procesului de instalare vor varia, desigur, în funcție de sistemul de operare și mecanismul de instalare
preferat, dar cea mai simplă abordare este să vizitați www.python.org, unde ar trebui să găsiți un link către o pagină de
descărcare. Totul se explică de la sine - trebuie doar să urmați linkul către cea mai recentă versiune pentru platforma dvs., fie
că este Windows, macOS, Linux/UNIX sau altceva. Pentru Windows și Mac, veți descărca un program de instalare pe care îl
puteți rula pentru a instala efectiv Python. Pentru Linux/UNIX, există tarball-uri de cod sursă pe care va trebui să le compilați
singur, urmând instrucțiunile incluse. Dacă utilizați un manager de pachete, cum ar fi Homebrew sau APT, îl puteți utiliza
pentru a simplifica procesul.
După ce ați instalat Python, încercați să porniți interpretul interactiv. Dacă utilizați linia de comandă, puteți utiliza pur
și simplu comanda python, sau poate python3 dacă aveți instalată și o versiune mai veche. Dacă preferați să utilizați o
interfață grafică, puteți porni aplicația IDLE care vine cu instalarea Python.

Interpretul interactiv
Când porniți Python, primiți un prompt similar cu următorul:

Python 3.5.0 (implicit, 5 decembrie 2015, 15:03:35)


[GCC 4.2.1 Compatibil Apple LLVM 7.0.0 (clang-700.1.76)] pe darwin Tastați „ajutor”, „drept
de autor”, „credite” sau „licență” pentru mai multe informații.
>>>

1
Hackingul nu este același lucru cu cracking, care este un termen care descrie criminalitatea informatică. Cele două sunt adesea
confundate, iar utilizarea se schimbă treptat. Hacking, așa cum îl folosesc aici, înseamnă practic „a te distra în timp ce programez”.

© Magnus Lie Hetland 2017 ML 1


Hetland, Beginning Python, DOI 10.1007/978-1-4842-0028-5_1
Machine Translated by Google

Capitolul 1 Hacking instant: elementele de bază

Aspectul exact al interpretului și mesajele de eroare ale acestuia vor depinde de versiunea pe care o utilizați.
Poate că nu pare foarte interesant, dar crede-mă, așa este. Aceasta este poarta ta de acces către hackerdom - primul tău pas în
preluarea controlului asupra computerului tău. În termeni mai pragmatici, este un interpret Python interactiv. Doar pentru a vedea
dacă funcționează, încercați următoarele:

>>> print("Bună, lume!")

Când apăsați tasta Enter, apare următoarea ieșire:

Salut Lume!
>>>

Dacă sunteți familiarizat cu alte limbaje de computer, este posibil să fiți obișnuit să terminați fiecare linie cu punct și
virgulă. Nu este nevoie să faceți acest lucru în Python. O linie este o linie, mai mult sau mai puțin. Puteți adăuga un punct și virgulă
dacă doriți, dar nu va avea niciun efect (cu excepția cazului în care urmează mai mult cod pe aceeași linie) și nu este un lucru obișnuit
de făcut.
Deci ce sa întâmplat aici? Lucrul >>> este promptul. Puteți scrie ceva în acest spațiu, cum ar fi tipărirea „Bună, lume!”.
Dacă apăsați Enter, interpretul Python tipărește șirul „Hello, world!” și veți primi o nouă solicitare mai jos.

Dacă ai scrie ceva complet diferit? Încearcă:

>>> Inchiziția spaniolă


SyntaxError: sintaxă nevalidă
>>>

Evident, interpretul nu a înțeles asta.2 (Dacă rulați un alt interpret decât IDLE, cum ar fi versiunea de linie de comandă
pentru Linux, mesajul de eroare va fi ușor diferit.) De asemenea, interpretul indică ce este greșit: va evidențiați cuvântul
spaniol dându-i un fundal roșu (sau, în versiunea cu linia de comandă, utilizând un accent, ^).

Dacă ai chef, mai joacă-te cu interpretul. Pentru îndrumare, încercați să introduceți comanda help() la prompt și
apăsați Enter. Puteți apăsa F1 pentru ajutor despre IDLE. În caz contrar, să continuăm. La urma urmei, interpretul nu e
prea distractiv când nu știi ce să-i spui.

Algo . . . Ce?
Înainte de a începe să programăm serios, voi încerca să vă dau o idee despre ce este programarea computerelor. Mai simplu
spus, îi spune unui computer ce să facă. Calculatoarele pot face o mulțime de lucruri, dar nu sunt foarte bune să gândească
singure. Chiar trebuie să fie hrăniți cu lingurița cu detaliile. Trebuie să alimentați computerul cu un algoritm într-o limbă pe
care o înțelege. Algoritm este doar un cuvânt elegant pentru o procedură sau o rețetă - o descriere detaliată a modului de a
face ceva. Luați în considerare următoarele:

SPAM cu SPAM, SPAM, Ouă și SPAM: În primul rând, luați niște SPAM.
Apoi adăugați niște SPAM, SPAM și ouă.
Dacă se dorește un SPAM deosebit de picant, adăugați niște SPAM.
Gatiti pana este gata -- Verificati la fiecare 10 minute.

2 La urma urmei, nimeni nu se așteaptă la Inchiziția spaniolă. . .

2
Machine Translated by Google

Capitolul 1 Hacking instant: elementele de bază

Nu este cea mai elegantă dintre rețete, dar structura sa poate fi destul de iluminatoare. Constă într-o serie de instrucțiuni
care trebuie urmate în ordine. Unele instrucțiuni pot fi făcute direct („luați niște SPAM”), în timp ce unele necesită o anumită
deliberare („Dacă se dorește un SPAM deosebit de picant”), iar altele trebuie repetate de mai multe ori („Verificați la fiecare
10 minute”).
Rețetele și algoritmii constau din ingrediente (obiecte, lucruri) și instrucțiuni (afirmații). În acest exemplu, SPAM-ul
și ouăle sunt ingredientele, în timp ce instrucțiunile constau în adăugarea SPAM, gătirea pentru o anumită perioadă de
timp și așa mai departe. Să începem cu câteva ingrediente Python relativ simple și să vedem ce poți face cu ele.

Numere și expresii
Interpretul interactiv Python poate fi folosit ca un calculator puternic. Încercați următoarele:

>>> 2 + 2

Acest lucru ar trebui să vă dea răspunsul 4. Nu a fost prea greu. Ei bine, ce zici de asta:

>>> 53672 + 235253


288925

Inca nu esti impresionat? Desigur, acestea sunt lucruri destul de standard. (Voi presupune că ați folosit un calculator
3 și (1 +așteptărilor.
pentru a cunoaște diferența dintre 1 + 2 * funcționează conform 2) * 3.) Toți operatorii
Împărțireaaritmetici
produce obișnuiți suficient
numere zecimale,
numite floats (sau numere în virgulă mobilă).

>>> 1/2
0,5
>>> 1 / 1
1.0

Dacă preferați să renunțați la partea fracțională și să faceți împărțirea întregului, puteți utiliza o bară oblică dublă.

>>> 1 // 2

0 >>> 1 // 1

1 >>> 5.0 // 2.4


2.0

În versiunile mai vechi de Python, diviziunea obișnuită pe numere întregi funcționau ca această dublă bară oblică. Dacă
utilizați Python 2.x, puteți obține o împărțire adecvată adăugând următoarea instrucțiune la începutul programului dvs.
(scrierea programelor complete este descrisă mai târziu) sau pur și simplu executând-o în interpretul interactiv:

>>> de la __future__ divizia de import

Notă În cazul în care nu este complet clar, viitorul din instrucțiune este înconjurat de două caractere de subliniere pe
ambele părți: _ _viitor_ _.

3
Machine Translated by Google

Capitolul 1 Hacking instant: elementele de bază

O altă alternativă, dacă rulați un vechi Python din linia de comandă, este să furnizați comutatorul de linie de
comandă -Qnew. Există o explicație mai detaliată a lucrurilor __viitorului__ în secțiunea „Înapoi la __viitor__” mai târziu
în acest capitol.
Acum ați văzut operatorii aritmetici de bază (adunare, scădere, înmulțire și împărțire),
dar am omis o rudă apropiată a diviziunii întregi.

>>> 1 % 2
1

Acesta este operatorul de rest (modul). x % y dă restul lui x împărțit la y. Cu alte cuvinte, este partea care rămâne
atunci când utilizați diviziunea întregi. Adică, x % y este același cu x - ((x // y) * y).

>>> 10 // 3

3 >>> 10 % 3

1 >>> 9 // 3

3 >>> 9 % 3

0 >>> 2,75 % 0,5


0,25

Aici 10 // 3 este 3 deoarece rezultatul este rotunjit în jos. Dar 3 × 3 este 9, deci obțineți un rest de 1. Când împărțiți 9 la
3, rezultatul este exact 3, fără rotunjire. Prin urmare, restul este 0. Acest lucru poate fi util dacă doriți să verificați ceva
„la fiecare 10 minute”, ca în rețeta de mai devreme în capitol. Puteți verifica pur și simplu dacă minutul % 10 este 0.
(Pentru o descriere despre cum să faceți acest lucru, consultați bara laterală „Sneak Peek: The if Statement” mai târziu
în acest capitol.) După cum puteți vedea din exemplul final, operatorul rest funcționează bine și cu flotoare.
Funcționează chiar și cu numere negative, iar acest lucru poate fi puțin confuz.

>>> 10 % 3
1
>>> 10 % -3
-2
>>> -10 % 3
2
>>> -10 % -3
-1

Privind aceste exemple, s-ar putea să nu fie imediat evident cum funcționează. Probabil că este mai ușor de
înțeles dacă te uiți la operația însoțitoare a diviziunii întregi.

>>> 10 // 3

3 >>> 10 // -3
-4
>>> -10 // 3
-4
>>> -10 // -3
3

4
Machine Translated by Google

Capitolul 1 Hacking instant: elementele de bază

Având în vedere modul în care funcționează diviziunea, nu este atât de greu de înțeles care trebuie să fie restul. Lucrul
important de înțeles despre diviziunea întregului este că este rotunjit în jos, ceea ce pentru numerele negative este departe
de zero. Asta înseamnă că -10 // 3 este rotunjit la -4, nu până la -3.
Ultimul operator la care ne vom uita este operatorul de exponențiere (sau putere).

>>> 2 ** 8 3

>>> -3 ** 2
-9
>>> (-3) ** 2
9

Rețineți că operatorul de exponențiere se leagă mai strâns decât negația (minus unar), deci -3**2 este de fapt același cu -
(3**2). Dacă doriți să calculați (-3)**2, trebuie să spuneți acest lucru în mod explicit.

Hexazecimale Octale și Binare


Pentru a încheia această secțiune, ar trebui să menționez că numerele hexazecimale, octale și binare sunt scrise astfel:

>>> 0xAF
175
>>> 010
8 >>>
0b1011010010
722

Prima cifră din ambele este zero. (Dacă nu știți despre ce este vorba, probabil că încă nu aveți nevoie de acest lucru. Arhivați-l
pentru o utilizare ulterioară.)

Variabile
Un alt concept care v-ar putea fi familiar este variabilele. Dacă algebra este doar o memorie îndepărtată, nu vă faceți griji:
variabilele din Python sunt ușor de înțeles. O variabilă este un nume care reprezintă (sau se referă la) o anumită valoare. De
exemplu, ați putea dori ca numele x să reprezinte 3. Pentru a face acest lucru, executați pur și simplu următoarele:

>>> x = 3

Aceasta se numește o misiune. Atribuim valoarea 3 variabilei x. Un alt mod de a pune acest lucru este să spunem că legăm
variabila x la valoarea (sau obiectul) 3. După ce ați atribuit o valoare unei variabile, puteți utiliza variabila în expresii.

>>> x 6 * 2

Spre deosebire de alte limbi, nu puteți folosi o variabilă înainte de a o lega de ceva. Nu există o „valoare implicită”.

5
Machine Translated by Google

Capitolul 1 Hacking instant: elementele de bază

Notă Povestea simplă este că numele sau identificatorii în Python constau din litere, cifre și caractere de
subliniere (_). Ele nu pot începe cu o cifră, așa că Plan9 este un nume de variabilă valid, în timp ce 9Plan nu este.3

Declarații
Până acum am lucrat (aproape) exclusiv cu expresii, ingredientele rețetei. Dar cum rămâne cu declarațiile – instrucțiunile?

De fapt, am înșelat. Am introdus deja două tipuri de declarații: instrucțiunea de tipărire și sarcini. Care
este diferența dintre o afirmație și o expresie? Te-ai putea gândi astfel: o expresie este ceva, în timp ce o declarație face
ceva. De exemplu, 2 * 2 este 4, în timp ce print(2 * 2) afișează 4. Cele două se comportă destul de similar, așa că diferența
dintre ele ar putea să nu fie chiar atât de clară.

>>> 2 * 2
4
>>> print(2 * 2)
4

Atâta timp cât executați acest lucru în interpretul interactiv, nu există nicio diferență, dar asta se datorează faptului
că interpretul imprimă întotdeauna valorile tuturor expresiilor (folosind aceeași reprezentare ca repr - vezi secțiunea
„Reprezentări șiruri, str și repr” mai târziu in acest capitol). Acest lucru nu este valabil pentru Python în general.
Mai târziu în acest capitol, veți vedea cum să creați programe care rulează fără acest prompt interactiv; pur și
simplu introducerea unei expresii precum 2 * 2 în programul dvs. nu va face nimic interesant.4 Totuși, introducerea
print(2 * 2) acolo va imprima în continuare 4.

Notă De fapt, print este o funcție (mai multe despre cele mai târziu în capitol), așa că ceea ce mă refer la o
declarație print este pur și simplu un apel de funcție. În Python 2.x, print avea un tip de instrucțiune propriu și nu
folosea paranteze în jurul argumentelor sale.

Diferența dintre declarații și expresii este mai evidentă atunci când se ocupă de sarcini.
Deoarece nu sunt expresii, nu au valori care să poată fi tipărite de interpretul interactiv.

>>> x = 3
>>>

Pur și simplu primiți imediat o nouă solicitare. Ceva s- a schimbat, însă. Acum avem o nouă variabilă x, care este acum
legată de valoarea 3. Într-o oarecare măsură, aceasta este o calitate definitorie a afirmațiilor în general: ele schimbă
lucrurile. De exemplu, sarcinile modifică variabilele, iar instrucțiunile tipărite schimbă modul în care arată ecranul tău.

3 Povestea puțin mai puțin simplă este că regulile pentru numele identificatorilor se bazează parțial pe standardul Unicode, așa cum
este documentat în Python Language Reference la https://docs.python.org/3/reference/lexical_analysis.html.
4
În cazul în care vă întrebați - da, face ceva. Acesta calculează produsul dintre 2 și 2. Cu toate acestea, rezultatul nu este păstrat nicăieri
sau afișat utilizatorului; nu are efecte secundare, dincolo de calculul in sine.

6
Machine Translated by Google

Capitolul 1 Hacking instant: elementele de bază

Atribuțiile sunt probabil cel mai important tip de declarație din orice limbaj de programare, deși poate
fi dificil să le înțelegem importanța chiar acum. Variabilele pot părea doar ca „depozitare” temporară (cum ar fi
oalele și tigăile unei rețete de gătit), dar puterea reală a variabilelor este că nu trebuie să știi ce valori dețin pentru a le
manipula.5
*
De exemplu, știți că x nu știe ce y se evaluează la produsul dintre x și y, chiar dacă este posibil să aveți
sunt x și y. Deci, puteți scrie programe care utilizează variabile în diferite moduri fără a ști valorile pe care le vor
păstra (sau la care se vor referi) în cele din urmă atunci când programul este rulat.

Obținerea de date de la utilizator


Ați văzut că puteți scrie programe cu variabile fără să le cunoașteți valorile. Desigur, interpretul trebuie să
cunoască valorile în cele din urmă. Deci, cum se poate să nu o facem? Interpretul știe doar ce îi spunem, nu? Nu
neaparat.
Este posibil să fi scris un program și altcineva îl poate folosi. Nu poți prezice ce valori valorează utilizatorii
va furniza programului. Să aruncăm o privire la funcția de intrare utilă. (Voi avea mai multe de spus despre
funcții într-un minut.)

>>> input("Sensul vieții: ")


Sensul vieții: 42 '42'

Ceea ce se întâmplă aici este că prima linie (input(...)) este executată în interpretul interactiv. Tipărește șirul „Sensul
"
vieții: ca o nouă solicitare. Tastesc 42 și apăs
de fragment
pe Enter. de
Valoarea
text sau
rezultată
șir), care
a intrării
este imprimat
este chiar
automat
acel număr
în ultima
(sublinie.
formă

Convertind șirurile de caractere în numere întregi folosind int, putem construi un exemplu puțin mai interesant:

>>> x = input("x: ")


x: 34
>>> y = input("y: ") y: 42
>>> print(int(x) * int(y))
1428

Aici, instrucțiunile de la prompturile Python (>>>) ar putea face parte dintr-un program terminat, iar valorile introduse
(34 și 42) ar fi furnizate de un utilizator. Programul dvs. ar tipări apoi valoarea 1428, care este produsul celor două. Și
nu trebuia să cunoști aceste valori când ai scris programul, nu?

Notă Obținerea unei astfel de intrări este mult mai utilă atunci când salvați programele într-un fișier separat, astfel încât alți

utilizatori să le poată executa. Învățați cum să faceți asta mai târziu în acest capitol, în secțiunea „Salvarea și executarea programelor”.

5
Observați ghilimelele din jurul stocării. Valorile nu sunt stocate în variabile – ele sunt stocate în unele adâncimi tulburi ale
memoriei computerului și se referă la variabile. După cum va deveni foarte clar pe măsură ce citiți mai departe, mai multe variabile
se pot referi la aceeași valoare.

7
Machine Translated by Google

Capitolul 1 Hacking instant: elementele de bază

SNEAK PEEK: DECLARAȚIA IF

Pentru a condimenta puțin lucrurile, vă voi oferi o scurtă privire despre ceva despre care nu ar trebui să aflați
până la capitolul 5: declarația if . Instrucțiunea if vă permite să efectuați o acțiune (o altă declarație) dacă o
anumită condiție este adevărată. Un tip de condiție este un test de egalitate, folosind operatorul de egalitate,
==. Da, este un semn de egalitate dublu. (Celul singur este folosit pentru teme, vă amintiți?)

Puneți această condiție după cuvântul if și apoi o despărțiți de următoarea afirmație cu două puncte.

>>> dacă 1 == 2: print('Unul este egal cu doi')


...
>>> dacă 1 == 1: print('Unul este egal cu unul')
...
Unul este egal cu unul
>>>

Nu se întâmplă nimic când condiția este falsă. Când este adevărat, totuși, instrucțiunea care urmează două puncte
(în acest caz, o instrucțiune print ) este executată. Rețineți, de asemenea, că atunci când utilizați instrucțiuni if în
interpretul interactiv, trebuie să apăsați Enter de două ori înainte de a fi executat. (Motivul pentru aceasta va deveni
clar în capitolul 5.)

Deci, dacă ora variabilă este legată de ora curentă în minute, puteți verifica dacă sunteți „la oră” cu următoarea
afirmație:

if time % 60 == 0: print('La ora!')

Funcții
În secțiunea „Numere și expresii”, am folosit operatorul de exponențiere (**) pentru a calcula puteri. Faptul este că poți
folosi în schimb o funcție , numită pow.

>>> 2 ** 8 3

>>> pow(2, 3)
8

O funcție este ca un mic program pe care îl puteți folosi pentru a efectua o anumită acțiune. Python are o mulțime de
funcții care pot face multe lucruri minunate. De fapt, vă puteți crea și propriile funcții (mai multe despre asta mai târziu);
prin urmare, ne referim adesea la funcții standard, cum ar fi pow, ca funcții încorporate .
Folosirea unei funcții așa cum am făcut-o în exemplul precedent se numește apelarea funcției. Îi oferiți
argumente (în acest caz, 2 și 3) și vă returnează o valoare. Deoarece returnează o valoare, un apel de funcție este
pur și simplu un alt tip de expresie, precum expresiile aritmetice discutate mai devreme în acest capitol.6 De fapt, puteți
combina apeluri de funcție și operatori pentru a crea expresii mai complicate (cum am făcut cu int, mai devreme). ).

6 Apelurile de funcții pot fi folosite și ca instrucțiuni dacă pur și simplu ignorați valoarea returnată.

8
Machine Translated by Google

Capitolul 1 Hacking instant: elementele de bază

>>> 10 + pow(2, 3 * 5) / 3,0


10932,6666666666666

Mai multe funcții încorporate pot fi utilizate în expresii numerice ca aceasta. De exemplu, abs oferă valoarea absolută
a unui număr și rotunjește numerele în virgulă mobilă la cel mai apropiat număr întreg.

>>> abs(-10) 10
>>> 2 // 3 0

>>> rotund(2 / 3) 1.0

Observați diferența dintre ultimele două expresii. Împărțirea întregului se rotunjește întotdeauna în jos, în timp ce
rotunjirea se rotunjește la cel mai apropiat număr întreg, cu legăturile rotunjite către numărul par. Dar ce se întâmplă
dacă doriți să rotunjiți un anumit număr în jos? De exemplu, s-ar putea să știți că o persoană are 32,9 ani, dar ați dori să
o rotunjiți la 32, deoarece nu are încă 33 de ani. Python are o funcție pentru aceasta (numită floor) - pur și simplu nu
este disponibilă direct. Ca și în cazul multor funcții utile, se găsește într-un modul.

Module
Vă puteți gândi la module ca la extensii care pot fi importate în Python pentru a-și extinde capacitățile. Importați
module cu o comandă specială numită (în mod firesc) import. Funcția menționată în secțiunea anterioară, floor, se află
într-un modul numit matematică.

>>> import math


>>> math.floor(32.9) 32

Observați cum funcționează: importăm un modul cu import și apoi folosim funcțiile din acel modul scriind
module.function. În special pentru această operație, ați putea converti numărul într-un număr întreg, așa cum am făcut
mai devreme, cu rezultatele de la intrare.

>>> int(32.9) 32

Notă Există funcții similare pentru a converti în alte tipuri (de exemplu, str și float). De fapt, acestea nu sunt
chiar funcții – sunt clase. Voi avea mai multe de spus despre cursuri mai târziu.

Modulul de matematică are, totuși, câteva alte funcții utile. De exemplu, opusul podelei este plafonul (prescurtarea
de la „tavan”), care găsește cea mai mică valoare integrală mai mare sau egală cu numărul dat.

>>> math.ceil(32.3) 33
>>> math.ceil(32) 32

9
Machine Translated by Google

Capitolul 1 Hacking instant: elementele de bază

Dacă sunteți sigur că nu veți importa mai mult de o funcție cu un nume dat (din module diferite), este posibil să nu
doriți să scrieți numele modulului de fiecare dată când apelați funcția. Apoi puteți utiliza o variantă a comenzii de import.

>>> din matematică import sqrt


>>> sqrt(9)
3.0

După ce utilizați funcția de import de la modul, puteți utiliza funcția fără prefixul de modul.

Sfat Puteți, de fapt, să utilizați variabile pentru a vă referi la funcții (și la majoritatea altor lucruri în Python). Prin efectuarea sarcinii

foo = math.sqrt, puteți începe să utilizați foo pentru a calcula rădăcini pătrate; de exemplu, foo(4) dă 2,0.

cmath și numere complexe Funcția sqrt este folosită

pentru a calcula rădăcina pătrată a unui număr. Să vedem ce se întâmplă dacă îi furnizăm un număr negativ:

>>> din matematică import sqrt


>>> sqrt(-1)
Traceback (cel mai recent apel ultimul):
...
ValueError: eroare în domeniul matematicii

sau, pe unele platforme:

>>> sqrt(-1)
nan

Notă nan este pur și simplu o valoare specială care înseamnă „nu un număr”.

Dacă ne limităm la numere reale și la implementarea lor aproximativă sub formă de float, nu putem lua rădăcina pătrată
a unui număr negativ. Rădăcina pătrată a unui număr negativ este un așa-numit număr imaginar, iar numerele care sunt
suma unei părți reale și a unei părți imaginare sunt numite complexe. Biblioteca standard Python are un modul separat
pentru tratarea numerelor complexe.

>>> import cmath


>>> cmath.sqrt(-1) 1j

Observați că nu am folosit din ... import ... aici. Dacă aș fi avut, mi-aș fi pierdut sqrt-ul obișnuit. Nume
ciocniri ca acestea pot fi furtive, așa că dacă nu doriți cu adevărat să utilizați versiunea de la, probabil că ar trebui să
rămâneți cu un import simplu.
Valoarea 1j este un exemplu de număr imaginar. Aceste numere sunt scrise cu j (sau J).
Aritmetica complexă rezultă în esență din definirea lui 1j ca rădăcină pătrată a lui -1. Fără să aprofundez prea mult
subiectul, permiteți-mi să vă arăt un ultim exemplu:

10
Machine Translated by Google

Capitolul 1 Hacking instant: elementele de bază

>>> (1 + 3j) * (9 + 4j) (-3 + 31j)

După cum puteți vedea, suportul pentru numere complexe este încorporat în limbaj.

Notă Nu există un tip separat pentru numerele imaginare în Python. Sunt tratate ca numere complexe a
căror componentă reală este zero.

Inapoi in viitor__
S-a zvonit că Guido van Rossum (creatorul lui Python) are o mașină a timpului – de mai multe ori, când oamenii au
solicitat funcții în limbaj, au descoperit că caracteristicile erau deja implementate. Desigur, nu avem voie cu toții să intrăm în
această mașină a timpului, dar Guido a fost destul de amabil să construiască o parte din ea în Python, sub forma modulului
magic __future__. Din acesta, putem importa funcții care vor fi standard în Python în viitor, dar care nu fac încă parte din
limbaj. Ați văzut asta în secțiunea „Numere și expresii” și vă veți întâlni din când în când în această carte.

Salvarea și executarea programelor dvs


Interpretul interactiv este unul dintre marile puncte forte ale lui Python. Face posibilă testarea soluțiilor și experimentarea
limbajului în timp real. Dacă vrei să știi cum funcționează ceva, încearcă doar! Cu toate acestea, tot ce scrieți în interpretul
interactiv se pierde atunci când renunțați. Ceea ce vrei cu adevărat să faci este să scrii programe pe care atât tu, cât și
ceilalți oameni le poți rula. În această secțiune, înveți cum să faci exact asta.
În primul rând, aveți nevoie de un editor de text, de preferință unul destinat programării. (Dacă folosești ceva de genul
Microsoft Word, pe care chiar nu-l recomand, asigurați-vă că salvați codul ca text simplu.) Dacă utilizați deja IDLE, aveți
noroc. Cu IDLE, puteți crea pur și simplu o nouă fereastră de editor cu Fișier › Fișier nou. Apare o altă fereastră, fără un
prompt interactiv. Uf! Începeți prin a introduce următoarele:

print("Bună, lume!")

Acum selectați Fișier › Salvare pentru a vă salva programul (care este, de fapt, un fișier text simplu). Asigurați-vă că îl puneți
undeva unde îl puteți găsi mai târziu și dați fișierului dvs. orice nume rezonabil, cum ar fi hello.py. (Terminația .py este
semnificativă.)
Am inteles? Nu închide fereastra cu programul tău în ea. Dacă ați făcut-o, deschideți-l din nou (Fișier › Deschide).
Acum îl puteți rula cu Run › Run Module. (Dacă nu utilizați IDLE, consultați următoarea secțiune despre rularea
programelor din promptul de comandă.)
Ce se întâmplă? Salut Lume! este tipărit în fereastra interpretului, care este exact ceea ce ne-am dorit.
Promptul interpretului poate dispărea (în funcție de versiunea pe care o utilizați), dar îl puteți recupera apăsând Enter
(în fereastra interpretului).
Să extindem scriptul la următoarele:

nume = input("Care este numele tău?")


print("Bună ziua, " + nume + "!")

11
Machine Translated by Google

Capitolul 1 Hacking instant: elementele de bază

Dacă rulați acest lucru (nu uitați să îl salvați mai întâi), ar trebui să vedeți următorul prompt în fereastra interpretului:

Cum te numești?

Introduceți numele dvs. (de exemplu, Gumby) și apăsați Enter. Ar trebui să obțineți ceva de genul acesta:

Bună, Gumby!

PUTEREA ȚESTOSTĂ!

Declarația de imprimare este utilă pentru exemplele de bază, deoarece funcționează practic peste tot. Dacă doriți să
experimentați cu rezultate mai interesante din punct de vedere vizual, ar trebui să aruncați o privire la modulul
țestoasă , care implementează așa-numita grafică țestoasă. Dacă aveți IDLE în funcțiune, modulul țestoasă ar trebui
să funcționeze foarte bine și vă permite să desenați figuri în loc să imprimați text. Deși este o practică de care ar
trebui să fiți atenți în general, în timp ce vă jucați cu grafica țestoasă, poate fi convenabil să importați pur și simplu
toate numele din modul.

*
din importul de broasca testoasa

După ce v-ați dat seama de ce funcții aveți nevoie, puteți reveni doar la importarea acestora.

Ideea de grafică a țestoasei provine de la roboți asemănătoare broaștelor țestoase care se pot mișca înainte și
înapoi și se pot întoarce un anumit număr de grade la stânga sau la dreapta. În plus, au purtat un pix, pe care îl
puteau muta în sus sau în jos pentru a stabili dacă atingea bucata de hârtie pe care o mutau. Modulul broasca
testoasa iti ofera o simulare a unui astfel de robot. De exemplu, iată cum ați desena un triunghi:

înainte(100)
stânga(120)
înainte(100)
stânga(120)
înainte(100)

Dacă rulați acest lucru, ar trebui să apară o nouă fereastră, cu o mică „țestoasă” în formă de săgeată care se mișcă,
cu o linie în spatele ei. Pentru a-i cere să ridice stiloul, utilizați penup(), iar pentru a-l lăsa din nou jos, pendown().
Pentru mai multe comenzi, consultați secțiunea relevantă din Python Library Reference (https://docs.python.org/3/
library/turtle.html ), iar pentru idei de desen, încercați o căutare pe web pentru grafica țestoasă. Pe măsură ce învățați
concepte suplimentare, ați putea dori să experimentați alternative cu broaște țestoase la exemplele de tipărire mai
banale. Și jocul cu grafica țestoasă demonstrează rapid necesitatea unora dintre constructele de programare de bază
pe care vi le voi arăta. (De exemplu, cum ați evita să repetați comenzile înainte și stânga din exemplul anterior? Cum
ați desena, să zicem, un octogon în loc de un triunghi? Sau mai multe poligoane regulate cu un număr diferit de
laturi, cu cât mai puține linii de cod posibil?)

12
Machine Translated by Google

Capitolul 1 Hacking instant: elementele de bază

Rularea scripturilor dvs. Python dintr-un prompt de comandă


De fapt, există mai multe moduri de a rula programele. În primul rând, să presupunem că aveți o fereastră DOS sau un
prompt de shell UNIX în fața dvs. și că directorul care conține executabilul Python (numit python.exe în Windows și python
în UNIX) sau directorul care conține executabilul (în Windows) a fost pus. în variabila de mediu PATH.7 De asemenea, să
presupunem că scriptul din secțiunea anterioară (hello.py) se află în directorul curent. Apoi, puteți executa scriptul cu
următoarea comandă în Windows:

C:\>python hello.py

sau UNIX:

$ python hello.py

După cum puteți vedea, comanda este aceeași. Se modifică doar promptul de sistem.

Faceți ca scripturile dvs. să se comporte ca programele normale Uneori doriți să executați

un program Python (numit și script) în același mod în care executați alte programe (cum ar fi browserul web sau
editorul de text), mai degrabă decât să utilizați explicit interpretul Python.
În UNIX, există o modalitate standard de a face acest lucru: prima linie a scriptului dumneavoastră începe cu secvența
de caractere #! (numit pound bang sau shebang) urmat de calea absolută către programul care interpretează scriptul (în
cazul nostru Python). Chiar dacă nu ați înțeles prea bine acest lucru, puneți următoarele în prima linie a scriptului dacă doriți
să ruleze ușor pe UNIX:

#!/usr/bin/env python

Acesta ar trebui să ruleze scriptul, indiferent de locul în care se află binarul Python. Dacă aveți mai multe versiuni de
Python instalate, puteți utiliza un nume executabil mai specific, cum ar fi python3, mai degrabă decât pur și simplu
python.
Înainte de a putea rula efectiv scriptul, trebuie să îl faceți executabil.

$ chmod a+x hello.py

Acum poate fi rulat astfel (presupunând că aveți directorul curent în cale):

$ hello.py

Dacă acest lucru nu funcționează, încercați să utilizați ./hello.py în schimb, care va funcționa chiar dacă directorul curent (.) nu face
parte din calea dvs. de execuție (ceea ce un administrator de sistem responsabil v-ar spune probabil că nu ar trebui să fie).
Dacă doriți, puteți redenumi fișierul și elimina sufixul py pentru a-l face să arate mai mult ca un normal
program.

7 Dacă nu înțelegeți această propoziție, poate ar trebui să săriți peste secțiune. Nu prea ai nevoie de el.

13
Machine Translated by Google

Capitolul 1 Hacking instant: elementele de bază

Dar dublu clic?


În Windows, sufixul (.py) este cheia pentru ca scriptul să se comporte ca un program. Încercați să faceți dublu clic pe fișierul
hello.py pe care l-ați salvat în secțiunea anterioară. Dacă Python a fost instalat corect, va apărea o fereastră DOS cu promptul
„Care este numele tău?”8 Există totuși o problemă cu rularea programului în acest fel.
După ce ați introdus numele, fereastra programului se închide înainte de a putea citi rezultatul. Fereastra se închide când
programul este terminat. Încercați să schimbați scriptul adăugând următoarea linie la sfârșit:

input("Apăsați <Enter>")

Acum, după ce ați rulat programul și ați introdus numele, ar trebui să aveți o fereastră DOS cu următoarele
continut:

Cum te numești? Gumby Bună,


Gumby!
Apăsați <enter>

Odată ce apăsați tasta Enter, fereastra se închide (pentru că programul este terminat).

Comentarii
Semnul hash (#) este puțin special în Python. Când îl introduci în codul tău, totul din dreapta este ignorat (de aceea
interpretul Python nu s-a sufocat cu chestiile /usr/bin/env folosite mai devreme). Iată un exemplu:

# Tipăriți circumferința cercului: print(2 * pi * rază)

Prima linie de aici se numește comentariu, care poate fi util pentru a face programele mai ușor de înțeles, atât pentru
alți oameni, cât și pentru dvs. când reveniți la codul vechi. S-a spus că prima poruncă a programatorilor este „Thou Shalt
Coment” (deși unii programatori mai puțin caritabili jură pe motto-ul „Dacă a fost greu de scris, ar trebui să fie greu de
citit”). Asigurați-vă că comentariile dvs. spun lucruri semnificative și nu pur și simplu reafirmați ceea ce este deja evident
din cod. Comentariile inutile, redundante pot fi mai rele decât deloc. De exemplu, în cele ce urmează, un comentariu nu
este cu adevărat solicitat:

# Obțineți numele utilizatorului:


user_name = input ("Care este numele dvs.?")

Este întotdeauna o idee bună să vă faceți codul lizibil și singur, chiar și fără comentarii. Din fericire, Python este un limbaj
excelent pentru scrierea de programe care pot fi citite.

Siruri de caractere

Acum, ce a fost tot acel "Bună ziua," + nume + "!" chestii despre? Primul program din acest capitol a fost simplu

print("Bună, lume!")

8 Acest comportament depinde de sistemul dumneavoastră de operare și de interpretul Python instalat. Dacă ați salvat fișierul
folosind IDLE în macOS, de exemplu, dublu clic pe fișier îl va deschide pur și simplu în editorul de cod IDLE.

14
Machine Translated by Google

Capitolul 1 Hacking instant: elementele de bază

Este obișnuit să începeți cu un astfel de program în tutorialele de programare. Problema este că încă nu am explicat
cum funcționează. Știți elementele de bază ale declarației tipărite (voi avea mai multe de spus despre asta mai târziu),
dar ce este „Bună, lume!”? Se numește șir (ca în „un șir de caractere”). Șirurile de caractere se găsesc în aproape fiecare
program util Python din lumea reală și au multe utilizări. Utilizarea lor principală este de a reprezenta fragmente de text,
cum ar fi exclamația „Bună, lume!”

Șiruri de caractere cu ghilimele simple și ghilimele care scapă

Șirurile sunt valori, la fel ca numerele:

>>> "Bună, lume!"


'Salut Lume!'

Există totuși un lucru care poate fi puțin surprinzător în acest exemplu: când Python a tipărit șirul nostru, a folosit
ghilimele simple, în timp ce noi am folosit ghilimele duble. Care este diferența? De fapt, nu există nicio diferență.

>>> „Bună, lume!”


'Salut Lume!'

Aici, folosim ghilimele simple, iar rezultatul este același. Deci de ce le permiteți pe amândouă? Pentru că în unele cazuri
poate fi util.

>>> "Hai să mergem!"


"Sa mergem!"
>>> '"Bună, lume!" ea a spus'
'"Salut Lume!" ea a spus'

În codul precedent, primul șir conține un singur ghilimele (sau un apostrof, așa cum ar trebui probabil să-l numim în
acest context) și, prin urmare, nu putem folosi ghilimele simple pentru a include șirul. Dacă am face-o, interpretul s-ar
plânge (și pe bună dreptate).

>>> 'Hai să mergem!'


SyntaxError: sintaxă nevalidă

Aici, șirul este „Let”, iar Python nu prea știe ce să facă cu următoarele s (sau restul liniei, de altfel).

În al doilea șir, folosim ghilimele duble ca parte a propoziției noastre. Prin urmare, trebuie să folosim ghilimele
simple pentru a include șirul nostru, din aceleași motive ca cele menționate anterior. Sau, de fapt, nu trebuie . Este doar
convenabil. O alternativă este să utilizați caracterul backslash (\) pentru a scăpa de ghilimele din șir, astfel:

>>> „Să mergem!”


"Sa mergem!"

Python înțelege că ghilimelele din mijloc sunt un caracter din șir și nu sfârșitul șirului.
(Chiar și așa, Python alege să folosească ghilimele duble atunci când tipăriți șirul.) Același lucru funcționează și cu
ghilimele duble, așa cum v-ați aștepta.

>>> "\"Bună, lume!\" a spus ea"


'"Salut Lume!" ea a spus'

15
Machine Translated by Google

Capitolul 1 Hacking instant: elementele de bază

Evadarea citatelor ca acesta poate fi utilă și uneori necesară. De exemplu, ce ați face fără bara oblică inversă dacă șirul
dvs. ar conține atât ghilimele simple, cât și ghilimele duble, ca în șirul „Să spunem „Bună ziua, lume!””?

Notă V-ați săturat de barele oblice inverse? După cum veți vedea mai târziu în acest capitol, puteți evita cele mai multe dintre

ele folosind șiruri lungi și șiruri brute (care pot fi combinate).

Concatenarea șirurilor
Doar pentru a continua să biciuiesc acest exemplu ușor chinuit, permiteți-mi să vă arăt un alt mod de a scrie același șir:

>>> "Să spunem """Bună ziua, lume!"'


„Hai să spunem „Bună, lume!”

Pur și simplu am scris două șiruri, unul după altul, iar Python le concatenează automat (le transformă într-un șir).
Acest mecanism nu este folosit foarte des, dar poate fi util uneori. Cu toate acestea, funcționează numai atunci când
scrieți ambele șiruri în același timp, direct unul după altul.

>>> x = "Bună ziua,"


>>> y = "lume!" >>>
xy SyntaxError:
sintaxă nevalidă

Cu alte cuvinte, acesta este doar un mod special de a scrie șiruri, nu o metodă generală de concatenare a acestora.
Cum, atunci, concatenezi șiruri? La fel cum adaugi numere:

>>> "Bună ziua," + "lume!"


'Salut Lume!' >>>
x = "Bună ziua," >>>
y = "lume!" >>> x + y
„Bună, lume!”

Reprezentări de șiruri, str și repr De-a lungul acestor

exemple, probabil ați observat că toate șirurile tipărite de Python sunt încă citate. Asta pentru că imprimă valoarea
așa cum ar putea fi scrisă în codul Python, nu cum ați dori să caute utilizatorul. Dacă utilizați imprimare, totuși, rezultatul
este diferit.

>>> "Bună, lume!"


'Salut Lume!' >>>
print("Bună, lume!")
Salut Lume!

Diferența este și mai evidentă dacă introducem pe furiș codul special de caractere de alimentare de linie \n.

16
Machine Translated by Google

Capitolul 1 Hacking instant: elementele de bază

>>> "Bună ziua,\nlume!"


„Bună ziua,\nlume!”
>>> print("Bună ziua,\nlume!")
Salut
Lume!

Valorile sunt convertite în șiruri de caractere prin două mecanisme diferite. Puteți accesa singuri ambele
mecanisme, folosind funcțiile str și repr. 9 Cu str, convertiți o valoare într-un șir într-un mod rezonabil care va fi
probabil înțeles de un utilizator, de exemplu, conversia oricăror coduri speciale de caractere în caracterele corespunzătoare,
acolo unde este posibil. Dacă utilizați repr, totuși, veți obține în general o reprezentare a valorii ca expresie legală Python.

>>> print(repr("Bună ziua,\nlume!"))


„Bună ziua,\nlume!”
>>> print(str("Bună ziua,\nlume!"))
Salut
Lume!

Șiruri lungi, șiruri brute și octeți Există câteva moduri utile,

puțin specializate de a scrie șiruri. De exemplu, există o sintaxă personalizată pentru scrierea șirurilor care includ linii
noi (șiruri lungi) sau bare oblice inverse (șiruri brute). În Python 2, exista și o sintaxă separată pentru scrierea șirurilor de
caractere cu simboluri speciale de diferite tipuri, producând obiecte de tip unicode. Sintaxa încă funcționează, dar acum
este redundantă, deoarece toate șirurile din Python 3 sunt șiruri Unicode.
În schimb, a fost introdusă o nouă sintaxă pentru a specifica un obiect octeți, care corespunde aproximativ șirurilor
de caractere vechi. După cum vom vedea, acestea joacă încă un rol important în gestionarea codificărilor Unicode.

Coarde lungi
Dacă doriți să scrieți un șir foarte lung, unul care se întinde pe mai multe rânduri, puteți utiliza ghilimele triple în loc de
ghilimele obișnuite.

print('''Acesta este un șir foarte lung. Continuă aici.


Și încă nu sa terminat. "Salut Lume!"
Inca aici.''')

Puteți folosi, de asemenea, ghilimele duble triple, """ așa """. Rețineți că, din cauza ghilimelelor distinctive, atât
ghilimele simple, cât și cele duble sunt permise înăuntru, fără a fi scapat de bara oblică inversă.

Sfat Șirurile obișnuite se pot întinde și pe mai multe linii. Dacă ultimul caracter de pe o linie este o bară oblică inversă,

ruptura de linie în sine este „scăpată” și ignorată. De exemplu:

print("Bună ziua, \ lume!")

De fapt, str este o clasă, la fel ca int. repr, totuși, este o funcție.
9

17
Machine Translated by Google

Capitolul 1 Hacking instant: elementele de bază

ar tipări Bună, lume!. Același lucru este valabil și pentru expresii și enunțuri în general.

>>> 1 + 2 + \

4+5
12

>>> printeaza \
('Salut Lume')
Salut Lume

Coarde brute
Șirurile brute nu sunt prea pretențioase în privința barelor oblice inverse, ceea ce poate fi foarte util uneori.10 În șirurile
obișnuite, bara oblică inversă are un rol special: scapă de lucruri, permițându-vă să puneți în șir lucruri pe care în mod normal
nu le-ați putea scrie direct. De exemplu, după cum am văzut, o nouă linie este scrisă \n și poate fi pusă într-un șir ca acesta:

>>> print('Bună ziua,\nlume!')


Salut
Lume!

În mod normal, acesta este doar dandy, dar în unele cazuri, nu este ceea ce îți dorești. Ce se întâmplă dacă ai vrea
ca șirul să includă o bară oblică inversă urmată de un n? Poate doriți să puneți calea DOS C:\nowhere într-un șir.

>>> cale = 'C:\nowhere' >>>


cale
„C:\nicăieri”

Acesta pare corect, până când îl imprimați și descoperiți defectul.

>>> print(cale)
C:
undeva

Nu este exact ceea ce căutam, nu-i așa? Deci ce facem? Putem scăpa de backslash în sine.

>>> print('C:\\nicăieri')
C:\nicăieri

Asta e bine. Dar pentru căi lungi, ajungi cu o mulțime de bare oblice inverse.

cale = 'C:\\Program Files\\fnord\\foo\\bar\\baz\\frozz\\bozz'

Corzile brute sunt utile în astfel de cazuri. Ei nu tratează deloc backslash ca pe un personaj special. Fiecare caracter
pe care îl puneți într-un șir brut rămâne așa cum l-ați scris.

10 Șirurile brute pot fi deosebit de utile atunci când scrieți expresii regulate. Aflați mai multe despre acestea în capitolul 10.

18
Machine Translated by Google

Capitolul 1 Hacking instant: elementele de bază

>>> print(r'C:\nowhere')
C:\nowhere
>>> print(r'C:\Program Files\fnord\foo\bar\baz\frozz\bozz')
C:\Program Files\fnord\foo\bar\baz\frozz\bozz

După cum puteți vedea, șirurile brute sunt prefixate cu un r. S-ar părea că puteți pune orice în interiorul unui șir brut și
asta este aproape adevărat. Citatele trebuie să fie eliminate ca de obicei, deși asta înseamnă că veți primi o bară oblică inversă
și în șirul final.

>>> print(r'Hai!')

Sa mergem!

Singurul lucru pe care nu-l poți avea într-un șir brut este o bară oblică inversă finală. Cu alte cuvinte, ultimul caracter dintr-
un șir brut nu poate fi o bară oblică inversă decât dacă îl scăpați (și apoi bara oblică inversă pe care o utilizați pentru a-l
scăpa va fi, de asemenea, parte din șir). Având în vedere exemplul anterior, asta ar trebui să fie evident. Dacă ultimul caracter
(înainte de ghilimele finale) este o bară oblică inversă fără escape, Python nu va ști dacă să încheie sau nu șirul.

>>> print(r"Acest lucru este ilegal\")


SyntaxError: EOL în timpul scanării șirului literal

Bine, deci este rezonabil, dar ce se întâmplă dacă vrei ca ultimul caracter din șirul tău brut să fie o bară oblică inversă? (Poate
că este sfârșitul unei căi DOS, de exemplu.) Ei bine, v-am oferit o serie întreagă de trucuri în această secțiune care ar trebui să
vă ajute să rezolvați această problemă, dar practic trebuie să puneți bara oblică inversă într-un șir separat. Un mod simplu de a
face acest lucru este următorul:

>>> print(r'C:\Program Files\foo\bar' '\\')


C:\Program Files\foo\bar\

Rețineți că puteți utiliza atât ghilimele simple, cât și duble cu șiruri brute. Chiar și șirurile cu ghilimele triple pot fi brute.

Unicode, bytes și bytearray


Șirurile Python reprezintă text folosind o schemă cunoscută sub numele de Unicode. Modul în care funcționează pentru majoritatea programelor
de bază este destul de transparent, așa că, dacă doriți, puteți sări peste această secțiune pentru moment și citiți subiectul după cum este necesar.
Cu toate acestea, deoarece manipularea șirurilor de caractere și a fișierelor text este una dintre principalele utilizări ale codului Python, probabil că nu ar strica cel
puțin să treceți peste această secțiune.

În mod abstract, fiecare caracter Unicode este reprezentat de un așa-numit punct de cod, care este pur și simplu numărul
său în standardul Unicode. Acest lucru vă permite să vă referiți la mai mult de 120.000 de caractere în 129 de sisteme de scriere
într-un mod care ar trebui să fie recunoscut de orice software modern. Desigur, tastatura dvs. nu va avea sute de mii de taste,
așa că există mecanisme generale pentru specificarea caracterelor Unicode, fie prin literale hexazecimale de 16 sau 32 de biți
(prefixându-le cu \u sau \U, respectiv) sau după Nume Unicode (folosind \N{nume}).

>>> „\u00C6” „Æ”

>>> „\U0001F60A” „ ”

>>> „Aceasta este o pisică: \N{Pisică}”


'
'Aceasta este o pisică:

Puteți găsi diferitele puncte de cod și nume căutând pe Web, folosind o descriere a caracterului de care aveți nevoie, sau puteți utiliza un anumit
site, cum ar fi http://unicode-table.com.

19
Machine Translated by Google

Capitolul 1 Hacking instant: elementele de bază

Ideea de Unicode este destul de simplă, dar vine cu câteva provocări, dintre care una este problema codificării.
Toate obiectele sunt reprezentate în memorie sau pe disc ca o serie de cifre binare - zerouri și unu - grupate în bucăți
de opt sau octeți, iar șirurile nu fac excepție. În limbaje de programare, cum ar fi C, acești octeți sunt complet în aer liber.
Șirurile sunt pur și simplu secvențe de octeți. Pentru a interopera cu C, de exemplu, și pentru a scrie text în fișiere sau a-l
trimite prin prize de rețea, Python are două tipuri similare, octeții imutabili și bytearray-ul mutabil. Dacă doriți, puteți produce
direct un obiect octeți, în loc de un șir, folosind prefixul b:

>>> b'Bună, lume!' b'Bună,


lume!'

Cu toate acestea, un octet poate conține doar 256 de valori, destul de mai puțin decât ceea ce cere standardul Unicode.
Literale de octeți Python permit doar cele 128 de caractere ale standardului ASCII, cu valorile rămase de 128 de octeți
necesitând secvențe de escape precum \xf0 pentru valoarea hexazecimală 0xf0 (adică 240).
S-ar putea părea că singura diferență aici este dimensiunea alfabetului disponibil pentru noi. Asta nu este cu
adevărat exact, totuși. La o privire, ar putea părea că atât ASCII, cât și Unicode se referă la o mapare între numere întregi
nenegative și caractere, dar există o diferență subtilă: acolo unde punctele de cod Unicode sunt definite ca numere întregi,
caracterele ASCII sunt definite atât prin numărul lor, cât și prin codificare binară. Un motiv pentru care acest lucru pare
complet neremarcabil este că maparea dintre numerele întregi 0–255 și un număr binar de opt cifre este complet standard
și există puțin spațiu de manevră. Chestia este că, odată ce trecem dincolo de un singur octet, lucrurile nu sunt atât de
simple. Generalizarea directă a reprezentării pur și simplu a fiecărui punct de cod ca numeral binar corespunzător poate să
nu fie calea de urmat. Nu numai că există problema ordinii octeților, cu care se confruntă chiar și atunci când se codifică
valori întregi, mai există și problema spațiului irosit: dacă folosim același număr de octeți pentru codificarea fiecărui punct de
cod, tot textul va trebui să se potrivească. faptul că s- ar putea să doriți să includeți câteva hieroglife anatoliene sau un pumn
de aramaică imperială. Există un standard pentru o astfel de codificare a Unicode, care se numește UTF-32 (pentru formatul de
transformare Unicode pe 32 de biți), dar dacă manipulați în principal text într-una dintre cele mai comune limbi ale internetului,
de exemplu, acesta este destul de risipitor.
Există totuși o alternativă absolut genială, concepută în mare parte de pionierul informaticii Kenneth Thompson.
În loc să folosească cei 32 de biți complet, folosește o codificare variabilă , cu mai puțini octeți pentru unele scripturi decât
pentru altele. Presupunând că veți folosi aceste scripturi mai des, acest lucru vă va economisi spațiu în general, în mod
similar cu modul în care codul Morse vă scutește de efort prin utilizarea mai puține puncte și liniuțe pentru literele mai
comune.11 În special, codificarea ASCII este încă folosită pentru un singur -codare byte, păstrând compatibilitatea cu
sistemele mai vechi. Cu toate acestea, caracterele din afara acestui interval folosesc mai mulți octeți (până la șase). Să
încercăm să codificăm un șir în octeți, folosind codificările ASCII, UTF-8 și UTF-32.

>>> "Bună, lume!".encode("ASCII") b'Bună, lume!'

>>> "Bună, lume!".encode("UTF-8") b'Bună,


lume!'
>>> "Bună, lume!".encode("UTF-32")
b'\xff\xfe\x00\x00H\x00\x00\x00e\x00\x00\x00l\x00\x00\x00l\x00\x00 \x00o\x00\x00\x00,\x00\ x00\x00
\x00\x00\x00w\x00\x00\x00o\x00\x00\x00r\x00\x00\x00l\x00\x00\x00\x00\x00 x00!\x00\ x00\x00'

După cum puteți vedea, primele două sunt echivalente, în timp ce ultima este destul de mai lungă. Iată un alt exemplu:

>>> len(„Cât durează asta?”.encode(„UTF-8”))


17

11Aceasta este o metodă importantă de compresie în general, folosită de exemplu în codarea Huffman, o componentă a mai multor
instrumente moderne de compresie.

20
Machine Translated by Google

Capitolul 1 Hacking instant: elementele de bază

>>> len(„Cât durează asta?”.encode(„UTF-32”)) 72

Diferența dintre ASCII și UTF-8 apare odată ce folosim câteva caractere puțin mai exotice:

>>> "Hællå, lume!".encode("ASCII")


Traceback (cel mai recent apel ultimul):
...
UnicodeEncodeError: codecul „ascii” nu poate codifica caracterul „\xe6” în poziția 1: ordinal nu se află în interval (128)

Literele scandinave de aici nu au codificare în ASCII. Dacă într-adevăr avem nevoie de codificare ASCII (ceea ce cu
siguranță se poate întâmpla), putem furniza un alt argument pentru a codifica, spunându-i ce să facă cu erorile. Modul
normal de aici este „strict”, dar există altele pe care le puteți utiliza pentru a ignora sau înlocui caracterele ofensatoare.

>>> "Hællå, lume!".encode("ASCII", "ignora") b'Hll, wrld!'

>>> "Hællå, lume!".encode("ASCII", "înlocuiește") b'H?ll?, w?


rld!'
>>> "Hællå, world!".encode("ASCII", "backslashreplace") b'H\\xe6ll\\xe5,
w\\xf8rld!'
>>> "Hællå, world!".encode("ASCII", "xmlcharrereplace") b'H&#230;ll&#229;,
w&#248;rld!'

În aproape toate cazurile, totuși, va fi mai bine să utilizați UTF-8, care este de fapt chiar și codificarea implicită.

>>> „Hællå, lume!”.encode()


b'H\xc3\xa6ll\xc3\xa5, w\xc3\xb8rld!'

Acesta este puțin mai lung decât pentru „Bună, lume!” de exemplu, în timp ce codificarea UTF-32 ar avea exact aceeași
lungime în ambele cazuri.
La fel cum șirurile pot fi codificate în octeți, octeții pot fi decodați în șiruri.

>>> b'H\xc3\xa6ll\xc3\xa5, w\xc3\xb8rld!'.decode()


— Hællå, lume!

Ca și înainte, codificarea implicită este UTF-8. Putem specifica o codificare diferită, dar dacă o folosim pe cea greșită, fie
vom primi un mesaj de eroare, fie vom ajunge cu un șir deformat. Obiectul octeți în sine nu știe despre codificare, așa că
este responsabilitatea dvs. să urmăriți pe care l-ați folosit.
În loc să utilizați metodele de codificare și decodare, este posibil să doriți să construiți pur și simplu octeții și
str (adică șir) obiecte, după cum urmează:

>>> octeți("Hællå, lume!", codificare="utf-8")


b'H\xc3\xa6ll\xc3\xa5, w\xc3\xb8rld!' >>>
str(b'H\xc3\xa6ll\xc3\xa5, w\xc3\xb8rld!', encoding="utf-8")
— Hællå, lume!

Utilizarea acestei abordări este puțin mai generală și funcționează mai bine dacă nu cunoașteți exact clasa obiectelor
de tip șir sau octeți cu care lucrați - și, ca regulă generală, nu ar trebui să fiți prea strict în acest sens .

21
Machine Translated by Google

Capitolul 1 Hacking instant: elementele de bază

Una dintre cele mai importante utilizări pentru codificare și decodare este atunci când stocați text în fișiere de pe disc.
Cu toate acestea, mecanismele Python pentru citirea și scrierea fișierelor fac în mod normal treaba pentru tine! Atâta timp cât sunteți
de acord să aveți fișierele în codificare UTF-8, nu trebuie să vă faceți griji pentru asta. Dar dacă ajungeți să vedeți galimă acolo unde
vă așteptați text, poate că fișierul a fost de fapt într-o altă codificare și atunci poate fi util să știți puțin despre ce se întâmplă. Dacă
doriți să aflați mai multe despre Unicode în Python, consultați HOWTO-ul despre acest subiect.12

Notă Codul dumneavoastră sursă este, de asemenea, codificat, iar implicit este UTF-8. Dacă doriți să utilizați
o altă codificare (de exemplu, dacă editorul dvs. de text insistă să salveze ca altceva decât UTF-8), puteți
specifica codificarea cu un comentariu special.

# -*- codificare: nume de codificare -*-

Înlocuiți numele de codificare cu orice codificare pe care o utilizați (majuscule sau minuscule), cum ar fi utf-8
sau, poate mai probabil, latin-1, de exemplu.

În cele din urmă, avem bytearray, o versiune mutabilă a octeților. Într-un fel, este ca un șir în care poți modifica caracterele, ceea ce nu
poți face cu un șir normal. Cu toate acestea, este într-adevăr proiectat mai mult pentru a fi folosit în culise și nu este tocmai ușor de
utilizat dacă este folosit ca șir asemănător. De exemplu, pentru a înlocui un caracter, trebuie să îi alocați un int în intervalul 0...255.
Deci, dacă doriți să introduceți un caracter, trebuie să obțineți valoarea lui ordinală, folosind ord.

>>> x = bytearray(b"Bună ziua!") >>> x[1]


= ord(b"u")
>>> x
bytearray(b'Bună!')

Un rezumat rapid
Acest capitol a acoperit destul de mult material. Să aruncăm o privire la ceea ce ai învățat înainte de a merge mai departe.

Algoritmi: un algoritm este o rețetă care vă spune exact cum să efectuați o sarcină.
Când programați un computer, descrieți în esență un algoritm într-o limbă pe care computerul o
poate înțelege, cum ar fi Python. O astfel de descriere prietenoasă cu mașinile se numește
program și constă în principal din expresii și instrucțiuni.

Expresii: O expresie este o parte a unui program de calculator care reprezintă o valoare. De
exemplu, 2 + 2 este o expresie, reprezentând valoarea 4. Expresiile simple sunt construite din
valori literale (cum ar fi 2 sau „Bună ziua”) utilizând operatori (cum ar fi + sau %) și funcții (cum ar fi
pow). Expresii mai complicate pot fi create prin combinarea expresiilor mai simple (de exemplu, (2 +
2) * (3 - 1)).
Expresiile pot conține și variabile.

Variabile: o variabilă este un nume care reprezintă o valoare. Noile valori pot fi atribuite variabilelor
prin atribuiri precum x = 2. O atribuire este un fel de declarație.

12Consultați https://docs.python.org/3/howto/unicode.html.

22
Machine Translated by Google

Capitolul 1 Hacking instant: elementele de bază

Instrucțiuni: o instrucțiune este o instrucțiune care îi spune computerului să facă ceva.


Aceasta poate implica schimbarea variabilelor (prin atribuiri), imprimarea lucrurilor pe ecran (cum ar
fi print(„Bună ziua, lume!”)), importarea modulelor sau realizarea unei multitudini de alte lucruri.

Funcții: Funcțiile din Python funcționează la fel ca funcțiile din matematică: pot lua unele
argumente și returnează un rezultat. (De fapt, ei pot face o mulțime de lucruri interesante înainte
de a se întoarce, așa cum veți afla când veți învăța să vă scrieți propriile funcții în Capitolul 6.)

Module: modulele sunt extensii care pot fi importate în Python pentru a-și extinde capacitățile. De
exemplu, mai multe funcții matematice utile sunt disponibile în modulul de matematică.

Programe: v-ați uitat la aspectele practice ale scrierii, salvării și rulării programelor Python.

Șiruri de caractere: șirurile de caractere sunt foarte simple - sunt doar bucăți de text, cu
caractere reprezentate ca puncte de cod Unicode. Și totuși sunt multe de știut despre ei.
În acest capitol, ați văzut multe moduri de a le scrie, iar în Capitolul 3 înveți multe moduri de a le
folosi.

Funcții noi în acest capitol


Funcții Descriere

abs(număr) Returnează valoarea absolută a unui număr.

octeți(șir, codificare[, erori]) cmath.sqrt(număr) Codifică un șir dat, cu comportamentul specificat pentru erori.

float(obiect) ajutor([obiect]) input(prompt) Returnează rădăcina pătrată; funcționează cu numere negative.

int(obiect) math.ceil(număr) math.floor(număr) Convertește un șir sau un număr într-un număr în virgulă mobilă.

math.sqrt(număr) Oferă ajutor interactiv.

Obține intrare de la utilizator ca șir.

Convertește un șir sau un număr într-un număr întreg.

Returnează plafonul unui număr ca un float.


Returnează etajul unui număr ca un float.

Returnează rădăcina pătrată; nu funcționează cu numere


negative.

pow(x, y[, z]) Returnează x la puterea lui y (modulo z).

print(obiect, ...) repr(obiect) Tipărește argumentele, separate prin spații.

rotund(număr[, ncifre]) Returnează o reprezentare șir a unei valori.

Rotunjește un număr la o precizie dată, cu legături rotunjite la numărul


par.

str(obiect) Convertește o valoare într-un șir. Dacă faceți conversia din octeți, puteți
specifica codificarea și comportamentul de eroare.

Argumentele date între paranteze drepte sunt opționale.

23
Machine Translated by Google

Capitolul 1 Hacking instant: elementele de bază

Ce acum?
Acum că știți elementele de bază ale expresiilor, să trecem la ceva mai avansat: structurile de date. În loc să
vă ocupați de valori simple (cum ar fi numere), veți vedea cum să le grupați în structuri mai complexe, cum ar fi liste și
dicționare. În plus, veți arunca o altă privire atentă asupra șirurilor. În capitolul 5, înveți mai multe despre declarații,
iar după aceea vei fi gata să scrii niște programe cu adevărat ingenioase.

24
Machine Translated by Google

CAPITOLUL 2

Liste și tupluri

Acest capitol introduce un nou concept: structurile de date. O structură de date este o colecție de elemente de date (cum ar fi
numere sau caractere, sau chiar alte structuri de date) care este structurată într-un fel, cum ar fi numerotarea elementelor. Cea
mai elementară structură de date din Python este secvența. Fiecărui element al unei secvențe i se atribuie un număr - poziția sau
indexul acestuia. Primul indice este zero, al doilea indice este unul și așa mai departe. Unele limbaje de programare își numerotează
elementele secvenței începând cu unu, dar convenția de indexare zero are o interpretare naturală a unui offset de la începutul
secvenței, cu indici negativi înfășurându-se până la sfârșit. Dacă numerotarea vi se pare puțin ciudată, vă asigur că cel mai probabil
vă veți obișnui destul de repede.

Acest capitol începe cu o prezentare generală a secvențelor și apoi acoperă unele operații care sunt comune tuturor
secvențelor, inclusiv liste și tupluri. Aceste operații vor funcționa și cu șiruri, care vor fi folosite în unele dintre exemple, deși pentru
un tratament complet al operațiunilor cu șiruri, trebuie să așteptați până la capitolul următor.
După ce ne ocupăm de aceste elemente de bază, începem să lucrăm cu liste și vedem ce este special la ele. Și după liste, ajungem la
tupluri, un tip special de secvență similară listelor, cu excepția faptului că nu le puteți modifica.

Prezentare generală a secvenței

Python are încorporate mai multe tipuri de secvențe. Acest capitol se concentrează pe două dintre cele mai comune: liste și tupluri.
Corzile sunt un alt tip important, pe care îl revăd în capitolul următor.
Principala diferență dintre liste și tuplu este că puteți schimba o listă, dar nu puteți schimba un tuplu.
Aceasta înseamnă că o listă ar putea fi utilă dacă trebuie să adăugați elemente pe măsură ce mergeți, în timp ce un tuplu poate fi util
dacă, dintr-un motiv oarecare, nu puteți permite ca secvența să se schimbe. Motivele pentru aceasta din urmă sunt de obicei mai
degrabă tehnice, având de-a face cu modul în care lucrurile funcționează intern în Python. De aceea este posibil să vedeți funcții
încorporate care returnează tupluri. Pentru propriile programe, sunt șanse să puteți folosi liste în loc de tupluri în aproape toate circumstanțele.
(O excepție notabilă, așa cum este descrisă în capitolul 4, este utilizarea tuplurilor ca chei de dicționar. Listele nu sunt permise,
deoarece nu aveți voie să modificați cheile.)
Secvențele sunt utile atunci când doriți să lucrați cu o colecție de valori. S-ar putea să aveți o secvență
reprezentând o persoană într-o bază de date, primul element fiind numele acesteia și al doilea vârsta.
Scris ca o listă (articolele unei liste sunt separate prin virgule și cuprinse între paranteze drepte), care ar arăta astfel:

>>> edward = ['Edward Gumby', 42]

Dar secvențele pot conține și alte secvențe, așa că ai putea face o listă cu astfel de persoane, care ar fi baza ta de date.

>>> edward = ['Edward Gumby', 42] >>> john =


['John Smith', 50]

© Magnus Lie Hetland 2017 25


ML Hetland, Beginning Python, DOI 10.1007/978-1-4842-0028-5_2
Machine Translated by Google

Capitolul 2 Liste și tupluri

>>> baza de date = [edward, john]


>>> baza de date
[['Edward Gumby', 42], ['John Smith', 50]]

Notă Python are o noțiune de bază a unui fel de structură de date numită container, care este practic orice obiect care poate

conține alte obiecte. Cele două tipuri principale de containere sunt secvențele (cum ar fi listele și tuplurile) și mapările (cum ar fi

dicționarele). În timp ce elementele unei secvențe sunt numerotate, fiecare element dintr-o mapare are un nume (numit și

cheie). Aflați mai multe despre mapări în Capitolul 4. Pentru un exemplu de tip de container care nu este nici o secvență, nici o

mapare, vedeți discuția despre seturi din Capitolul 10.

Operații de secvență comună


Există anumite lucruri pe care le puteți face cu toate tipurile de secvențe. Aceste operațiuni includ indexarea, tăierea,
adăugarea, înmulțirea și verificarea apartenenței. În plus, Python are funcții încorporate pentru a găsi lungimea unei
secvențe și pentru a găsi elementele sale cele mai mari și cele mai mici.

Notă O opera ie importantă care nu este acoperită aici este itera ia. A itera peste o secvență înseamnă a efectua anumite

acțiuni în mod repetat, o dată pe element din secvență. Pentru a afla mai multe despre acest lucru, consultați secțiunea „Bucle”

din Capitolul 5.

Indexare

Toate elementele dintr-o secvență sunt numerotate — de la zero și în sus. Le puteți accesa individual cu un
număr, astfel:

>>> salut = „Bună ziua”


>>> salut[0]
'H'

Notă Un șir este doar o secvență de caractere. Indexul 0 se referă la primul element, în acest caz litera H. Spre deosebire

de alte limbi, nu există totuși un tip de caracter separat. Un caracter este doar un singur șir de element.

Aceasta se numește indexare. Folosiți un index pentru a prelua un element. Toate secvențele pot fi indexate în acest fel.
Când utilizați un index negativ, Python numără de la dreapta, adică de la ultimul element. Ultimul element se află
în poziția –1.

>>> salut[-1] „o”

26
Machine Translated by Google

Capitolul 2 Liste și tupluri

Literale de șir (și alte literale de secvență, de altfel) pot fi indexate direct, fără a utiliza o variabilă pentru a se referi la
ele. Efectul este exact același.

>>> „Bună ziua”[1]


„e”

Dacă un apel de funcție returnează o secvență, o puteți indexa direct. De exemplu, dacă sunteți pur și simplu interesat
de a patra cifră dintr-un an introdusă de utilizator, puteți face ceva de genul acesta:

>>> a patra = input('Anul: ')[3]


Anul: 2005
>>> al
patrulea „5”

Lista 2-1 conține un exemplu de program care vă solicită un an, o lună (ca număr de la 1 la 12) și o zi (de la 1 la 31),
apoi tipărește data cu numele corect al lunii și așa mai departe .

Lista 2-1. Exemplu de indexare

# Imprimați o dată, dat anul, luna și ziua ca numere

luni = [
'Ianuarie',
'Februarie',
'Martie',
'Aprilie',
'Mai',
'Iunie',
'Iulie',
'August',
'Septembrie',
'Octombrie',
'Noiembrie',
'Decembrie'
]

# O listă cu câte o terminație pentru fiecare număr de la 1 la 31 de


terminații = ['st', 'nd', 'rd'] + 17 * ['th'] \ + ['st',['st']
'nd', 'rd' ] + 7 * ['th'] \ +

= input('Anul:
luna = input('Luna (1-12):')')an
zi =
input('Ziua (1-31): ')

luna_numar = int(luna)
day_number = int(zi)

# Nu uitați să scădeți 1 din lună și zi pentru a obține un index corect month_name =


months[month_number-1] ordinal = day + endings[day_number-1]

' '
print(nume_lună + + ordinal + ', ' + an)

27
Machine Translated by Google

Capitolul 2 Liste și tupluri

Un exemplu de sesiune cu acest program ar putea fi următorul:

Anul: 1974
Luna (1-12): 8
Ziua (1-31): 16
16 august 1974

Ultima linie este rezultatul din program.

Tăierea Așa

cum utilizați indexarea pentru a accesa elemente individuale, puteți utiliza tăierea pentru a accesa intervale de elemente.
Faceți acest lucru folosind doi indici, separați prin două puncte.

>>> tag = „<a href="http://www.python.org">Site web Python</a>' >>> etichetă[9:30]


„http://www.python.org” >>> etichetă[32:-4]

„Site web Python”

După cum puteți vedea, tăierea este utilă pentru extragerea unor părți dintr-o secvență. Numerotarea aici este foarte importantă.
Primul index este numărul primului element pe care doriți să-l includeți . Cu toate acestea, ultimul index este numărul primului
element după felia dvs. Luați în considerare următoarele:

>>> numere = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10] >>> numere[3:6] [4, 5,


6] >>> numere[0:1 ] [1]

Pe scurt, furnizați doi indici ca limite pentru felia dvs., unde primul este inclusiv , iar al doilea este exclusiv.

O scurtătură ingenioasă

Să presupunem că doriți să accesați ultimele trei elemente ale numerelor (din exemplul anterior). Ai putea să o faci în mod
explicit, desigur.

>>> numere[7:10] [8,


9, 10]

Acum, indicele 10 se referă la elementul 11 — care nu există, dar este la un pas după ultimul element pe care îl doriți.
Am în eles? Dacă vrei să numeri de la final, folosești indici negativi.

>>> numere[-3:-1] [8, 9]

Cu toate acestea, se pare că nu puteți accesa ultimul element în acest fel. Ce zici de a folosi 0 ca element „un pas dincolo” de
sfârșit?

>>> numere[-3:0] []

28
Machine Translated by Google

Capitolul 2 Liste și tupluri

Nu este tocmai rezultatul dorit. De fapt, ori de câte ori indexul cel mai din stânga dintr-o felie apare mai târziu în secvență decât
al doilea (în acest caz, al treilea până ultimul vine mai târziu decât primul), rezultatul este întotdeauna o secvență goală. Din
fericire, puteți folosi o comandă rapidă: dacă felia continuă până la sfârșitul secvenței, puteți pur și simplu să omiteți ultimul
index.

>>> numere[-3:] [8, 9,


10]

Același lucru funcționează de la început.

>>> numere[:3] [1,


2, 3]

De fapt, dacă doriți să copiați întreaga secvență, puteți omite ambii indici.

>>> numere[:] [1,


2, 3, 4, 5, 6, 7, 8, 9, 10]

Lista 2-2 conține un mic program care vă solicită o adresă URL și (presupunând că este de forma destul de limitată http://
www.somedomainname.com) extrage numele domeniului.

Lista 2-2. Exemplu de feliere

# Împărțiți o adresă URL de forma http://www.something.com

url = input('Vă rugăm să introduceți adresa URL:')


domain = url[11:-4]

"
print("Nume domeniu: + domeniu)

Iată un exemplu de rulare a programului:

Vă rugăm să introduceți adresa URL: http://www.python.org


Nume de domeniu: python

Pași mai lungi


Când tăiați, specificați (fie explicit, fie implicit) punctele de început și de sfârșit ale feliei. Un alt parametru, care în mod normal
este lăsat implicit, este lungimea pasului. Într-o felie obișnuită, lungimea pasului este una, ceea ce înseamnă că felia „se mișcă”
de la un element la altul, returnând toate elementele între început și sfârșit.

>>> numere[0:10:1] [1, 2,


3, 4, 5, 6, 7, 8, 9, 10]

În acest exemplu, puteți vedea că felia include un alt număr. Aceasta este, după cum probabil ați ghicit, dimensiunea pasului,
făcută explicită. Dacă dimensiunea pasului este setată la un număr mai mare de unu, elementele vor fi sărite. De exemplu, o
dimensiune a pasului de doi va include numai fiecare alt element al intervalului dintre început și sfârșit.

>>> numere[0:10:2] [1, 3,


5, 7, 9] numere[3:6:3] [4]

29
Machine Translated by Google

Capitolul 2 Liste și tupluri

Puteți folosi în continuare comenzile rapide menționate mai devreme. De exemplu, dacă doriți fiecare al patrulea element al unei
secvențe, trebuie să furnizați doar o dimensiune a pasului de patru.

>>> numere[::4] [1,


5, 9]

Desigur, dimensiunea pasului nu poate fi zero - asta nu te-ar duce nicăieri - dar poate fi negativă, ceea ce înseamnă
extragerea elementelor de la dreapta la stânga.

>>> numere[8:3:-1] [9,


8, 7, 6, 5] >>>
numere[10:0:-2] [10, 8, 6,
4, 2] >>> numere [0:10:-2]
[] >>> numere[::-2] [10, 8,
6, 4, 2] >>> numere[5::-2]
[6, 4, 2] > >> numere[:5:-2]
[10, 8]

A face lucrurile corect aici poate implica un pic de gândire. După cum puteți vedea, prima limită (cea din stânga)
este încă inclusivă, în timp ce a doua (cea din dreapta) este exclusivă. Când utilizați o dimensiune negativă a pasului,
trebuie să aveți o primă limită (indice de pornire) care este mai mare decât a doua. Ceea ce poate fi puțin confuz este că,
atunci când lăsați indicii de început și de sfârșit impliciti, Python face „lucru corect” - pentru o dimensiune pozitivă a
pasului, se mișcă de la început spre sfârșit, iar pentru o dimensiune negativă a pasului, se mișcă de la sfâr it spre început.

Adăugarea de secvențe

Secvențele pot fi concatenate cu operatorul de adăugare (plus).

>>> [1, 2, 3] + [4, 5, 6] [1, 2, 3, 4,


5, 6]
>>> „Bună,” + „lume!”
'Salut Lume!' >>>
[1, 2, 3] + „lumea!”
Traceback (ultimul interior): fișierul
„<pyshell>”, linia 1, în ? [1, 2, 3] +
„lume!”
TypeError: poate concatena doar lista (nu „șir”) la listă

După cum puteți vedea din mesajul de eroare, nu puteți concatena o listă și un șir, deși ambele sunt secvențe.
În general, nu puteți concatena secvențe de diferite tipuri.

30
Machine Translated by Google

Capitolul 2 Liste și tupluri

Multiplicare
Înmulțirea unei secvențe cu un număr x creează o nouă secvență în care secvența originală se repetă de x ori:

>>> „python” * 5
„pythonpythonpythonpythonpython” >>>
[42] * 10 [42, 42, 42, 42, 42, 42, 42, 42, 42,
42]

Niciunul, listele goale și inițializarea O listă goală

este pur și simplu scrisă ca două paranteze ([]) — nu există nimic în ea. Dacă doriți să aveți o listă cu
spațiu pentru zece elemente, dar fără nimic util în ea, puteți folosi [42]*10, ca înainte, sau poate mai
realist [0]*10. Acum aveți o listă cu zece zerouri în ea. Uneori, totuși, ați dori o valoare care să însemne
cumva „nimic”, cum ar fi „nu am pus încă nimic aici”. Atunci folosești Niciunul. Nimic nu este o valoare
Python și înseamnă exact asta: „nimic aici”. Deci, dacă doriți să inițializați o listă cu lungimea 10, puteți face următoarele

>>> secvență = [Niciuna] * 10 >>>


secvență
[Niciuna, Niciuna, Niciuna, Niciuna, Niciuna, Niciuna, Niciuna, Niciuna, Niciuna, Niciuna]

Lista 2-3 conține un program care tipărește (pe ecran) o „cutie” formată din caractere, care este centrată pe ecran și
adaptată la dimensiunea unei propoziții furnizate de utilizator. Codul poate părea complicat, dar în esență este doar
aritmetică - să știți de câte spații, liniuțe și așa mai departe aveți nevoie pentru a plasa lucrurile corect.

Lista 2-3. Exemplu de multiplicare secvență (șir).

# Tipărește o propoziție într-o „casetă” centrată de lățime corectă

propoziție = input("Propoziție: ")

screen_width = 80
text_width = len(propoziție) box_width
= text_width + 6 left_margin
(screen_width
= -
box_width) // 2

print()
print(' ' * margine_stânga + '+' + '-' * (lățime_cutie-2) + print(' ' * '+') '
'
margine_stânga + '| ' ' * text_width print(' ' *+margine_stânga
print( ' ' * margine_stânga + |') ' |')
+ '| ' propoziție
+ '| ' ' * text_width print(' ' * margine_stânga++ '+' + '-' * (box_width-2) + print()
+ ' |') '+')
'
+ +

31
Machine Translated by Google

Capitolul 2 Liste și tupluri

Următorul este un exemplu de rulare:

Propoziție: Este un băiat foarte obraznic!

+-----------------------------+

| | | Este un băiat foarte obraznic! | | |

+-----------------------------+

Calitatea de membru

Pentru a verifica dacă o valoare poate fi găsită într-o secvență, utilizați operatorul in. Acest operator este puțin diferit de cei discutați
până acum (cum ar fi înmulțirea sau adunarea). Verifică dacă ceva este adevărat și returnează o valoare în consecință: Adevărat pentru
adevărat și Fals pentru fals. Astfel de operatori se numesc operatori booleeni, iar valorile de adevăr sunt numite valori booleene. Aflați
mai multe despre expresiile booleene în secțiunea privind instrucțiunile condiționate din Capitolul 5.

Iată câteva exemple care folosesc operatorul in:

>>> permisiuni = 'rw' >>> 'w' în


permisiuni
Adevărat

>>> „x” în permisiuni


False
>>> utilizatori = ['mlh', 'foo', 'bar'] >>>
input('Introduceți numele dvs. de utilizator: ') în utilizatori
Introduceți numele dvs. de utilizator: mlh
Adevărat

>>> subiect = '$$$ Imbogateste-te acum!!! $$$' >>> '$$$'


în subiectul Adevărat

Primele două exemple folosesc testul de apartenență pentru a verifica dacă „w” și, respectiv, „x” se găsesc în permisiunile șirului.
Acesta ar putea fi un script pe o mașină UNIX care verifică permisiunile de scriere și execuție pe un fișier. Următorul exemplu
verifică dacă un nume de utilizator furnizat (mlh) este găsit într-o listă de utilizatori.
Acest lucru ar putea fi util dacă programul dvs. impune o anumită politică de securitate. (În acest caz, probabil că ați dori să utilizați și
parole.) Ultimul exemplu verifică dacă subiectul șirului conține șirul „$$$”.
Acesta poate fi folosit ca parte a unui filtru de spam, de exemplu.

32
Machine Translated by Google

Capitolul 2 Liste și tupluri

Notă Exemplul care verifică dacă un șir conține „$$$” este puțin diferit de celelalte. În general, operatorul in verifică dacă un

obiect este membru (adică un element) al unei secvențe (sau al unei alte colecții).

Cu toate acestea, singurii membri sau elemente ale unui șir sunt caracterele acestuia. Deci, următoarele au sens perfect:

>>> „P” în „Python”


Adevărat

De fapt, în versiunile anterioare de Python, aceasta a fost singura verificare de apartenență care a funcționat cu șiruri de

caractere - a afla dacă un caracter este într-un șir. În zilele noastre, puteți utiliza operatorul in pentru a verifica dacă orice șir

este un subșir al altuia.

Lista 2-4 arată un program care citește un nume de utilizator și verifică codul PIN introdus cu o bază de date (o listă, de fapt)
care conține perechi (mai multe liste) de nume și coduri PIN. Dacă perechea nume/PIN este găsită în baza de date, este tipărit
șirul „Acces acordat”. (Declarația if a fost menționată în capitolul 1 și va fi explicat pe deplin în capitolul 5.)

Lista 2-4. Exemplu de apartenență la secvență

# Verificați un nume de utilizator și un cod PIN

baza de date =
[ ['albert', '1234'], ['dilbert',
'4242'], ['smith', '7524'],
['jones', '9843']

username = input('Nume utilizator: ') pin =


input('Cod PIN: ')

dacă [nume utilizator, pin] în baza de date: print('Acces acordat')

Lungime, minimă și maximă


Funcțiile încorporate len, min și max pot fi destul de utile. Funcția len returnează numărul de elemente pe care le conține o
secvență. min și max returnează cele mai mici și, respectiv, cele mai mari elemente ale secvenței. (Aflați mai multe despre
compararea obiectelor în Capitolul 5, în secțiunea „Operatori de comparație.”)

>>> numere = [100, 34, 678] >>>


len(numere) 3

>>> max(numere) 678


>>> min(numere) 34

>>> max(2, 3) 3

>>> min(9, 3, 2, 5)
2

33
Machine Translated by Google

Capitolul 2 Liste și tupluri

Cum funcționează acest lucru ar trebui să fie clar din explicația anterioară, cu excepția, eventual, ultimelor două
expresii. În acelea, max și min nu sunt apelate cu un argument de secvență; numerele sunt furnizate direct ca argumente.

Liste: Python's Workhorse


În exemplele anterioare, am folosit destul de mult liste. Ați văzut cât de utile sunt, dar această secțiune tratează ce le
face diferite de tupluri și șiruri de caractere: listele sunt mutabile - adică le puteți schimba conținutul - și au multe
metode specializate utile.

Funcția listă
Deoarece șirurile nu pot fi modificate în același mod ca și listele, uneori poate fi util să creați o listă dintr-un șir. Puteți
face acest lucru cu funcția listă.1

>>> list('Bună ziua')


['Buna ziua']

Rețineți că lista funcționează cu tot felul de secvențe, nu doar cu șiruri.

Sfat Pentru a converti o listă de caractere, cum ar fi codul precedent, înapoi într-un șir, veți folosi următoarea expresie:

''.join(somelist)

unde listă este lista ta. Pentru o explicație a ceea ce înseamnă acest lucru cu adevărat, consultați secțiunea despre alăturare din Capitolul 3.

Operații de bază pe liste Puteți

efectua toate operațiunile standard de secvență pe liste, cum ar fi indexarea, tăierea, concatenarea și
înmulțirea. Dar lucrul interesant despre liste este că pot fi modificate. În această secțiune, veți vedea câteva dintre
modalitățile prin care puteți modifica o listă: alocarea articolelor, ștergerea articolelor, atribuirea secțiunilor și metodele de listă.
(Rețineți că nu toate metodele de listă își schimbă lista.)

Schimbarea listelor: Atribuții de articole


Schimbarea unei liste este ușoară. Folosiți doar atribuirea obișnuită, așa cum este explicată în Capitolul 1. Cu toate
acestea, în loc să scrieți ceva de genul x = 2, utilizați notația de indexare pentru a atribui o anumită poziție existentă, cum
ar fi x[1] = 2.

>>> x = [1, 1, 1] >>>


x[1] = 2
>>> x
[1, 2, 1]

Este de fapt o clasă, nu o funcție, dar diferența nu este importantă acum.


1

34
Machine Translated by Google

Capitolul 2 Liste și tupluri

Notă Nu puteți atribui o poziție care nu există; dacă lista dumneavoastră are lungimea 2, nu puteți atribui o valoare
indexului 100. Pentru a face asta, ar trebui să faceți o listă cu lungimea 101 (sau mai mult). Consultați secțiunea „Niciuna,
liste goale și inițializare” mai devreme în acest capitol.

Ștergerea elementelor

Ștergerea elementelor dintr-o listă este, de asemenea, ușoară. Puteți utiliza pur și simplu declarația del.

>>> nume = ['Alice', 'Beth', 'Cecil', 'Dee-Dee', 'Earl'] >>> del names[2]

>>> nume
[„Alice”, „Beth”, „Dee-Dee”, „Earl”]

Observați cum Cecil a dispărut complet, iar lungimea listei s-a micșorat de la cinci la patru. Declarația del
poate fi folosită pentru a șterge alte lucruri decât elementele listei. Poate fi folosit cu dicționare (vezi
capitolul 4) sau chiar cu variabile. Pentru mai multe informații, consultați capitolul 5.

Atribuirea la felii
Tăierea este o caracteristică foarte puternică și este și mai puternică prin faptul că puteți aloca felii.

>>> nume = list('Perl')


>>> nume
['P', 'e', 'r', 'l'] >>>
nume[2:] = list('ar')
>>> nume
['Pară']

Deci, puteți atribui mai multe poziții simultan. S-ar putea să vă întrebați care este marea problemă. Nu le-
ai fi putut atribui pe rând? Sigur, dar atunci când utilizați atribuiri de felii, puteți înlocui, de asemenea,
felia cu o secvență a cărei lungime este diferită de cea a originalului.

>>> nume = list('Perl') >>>


nume[1:] = list('ython')
>>> nume
[„P”, „y”, „t”, „h”, „o”, „n”]

Atribuțiile de felii pot fi chiar folosite pentru a insera elemente fără a le înlocui pe cele originale.

>>> numere = [1, 5]


>>> numere[1:1] = [2, 3, 4] >>>
numere [1, 2, 3, 4, 5]

35
Machine Translated by Google

Capitolul 2 Liste și tupluri

Aici, practic, am „înlocuit” o felie goală, inserând astfel o secvență. Puteți face invers pentru a șterge o felie.

>>> numere
[1, 2, 3, 4, 5] >>>
numere[1:4] = [] >>> numere
[1, 5]

După cum probabil ați ghicit, acest ultim exemplu este echivalent cu del numbers[1:4]. (Acum, de ce nu încercați o
atribuire a feliei cu o dimensiune a pasului diferită de 1? Poate chiar una negativă?)

Lista metode
O metodă este o funcție care este strâns cuplată cu un obiect, fie că este o listă, un număr, un șir sau orice altceva. În
general, o metodă se numește astfel:

obiect.metoda(argumente)

Un apel de metodă arată exact ca un apel de funcție, cu excepția faptului că obiectul este pus înaintea numelui metodei, cu
un punct care le separă. (Aveți o explicație mult mai detaliată despre metodele cu adevărat în Capitolul 7.) Listele au mai
multe metode care vă permit să le examinați sau să le modificați conținutul.

adăuga
Metoda append este folosită pentru a adăuga un obiect la sfârșitul unei liste.

>>> lst = [1, 2, 3] >>>


lst.append(4) >>> lst [1,
2, 3, 4]

S-ar putea să vă întrebați de ce am ales un nume atât de urât ca primul pentru lista mea. De ce să nu-i numești listă? Aș
putea face asta, dar după cum vă amintiți, list este o funcție încorporată.2 Dacă folosesc în schimb numele unei liste, nu
voi mai putea apela funcția. În general, puteți găsi nume mai bune pentru o anumită aplicație. Un nume precum lst chiar
nu-ți spune nimic. Deci, dacă lista dvs. este o listă de prețuri, de exemplu, probabil că ar trebui să o numiți așa cum ar fi
prețuri, prețuri_de_ouă sau prețuri de ouă.
De asemenea, este important să rețineți că anexarea, ca și câteva metode similare, modifică lista în loc. Aceasta
înseamnă că nu returnează pur și simplu o listă nouă, modificată; în schimb, îl modifică pe cel vechi în mod direct. Acesta
este de obicei ceea ce doriți, dar uneori poate cauza probleme. Voi reveni la această discuție când voi descrie sortarea
mai târziu în capitol.

2
De fapt, din versiunea 2.2 a Python, lista este un tip, nu o funcție. (Acesta este și cazul tuplelor și str.) Pentru povestea
completă despre aceasta, consultați secțiunea „Lista de subclasare, dict și str” din Capitolul 9.

36
Machine Translated by Google

Capitolul 2 Liste și tupluri

clar
Metoda clară șterge conținutul unei liste, în loc.

>>> lst = [1, 2, 3] >>>


lst.clear() >>> lst []

Este similar cu atribuirea secțiunii lst[:] = [].

copie
Metoda de copiere copiază o listă. Amintiți-vă că o atribuire normală leagă pur și simplu un alt nume de aceeași listă.

>>> a = [1, 2, 3] >>> b


= a >>> b[1] = 4

>>> a
[1, 4, 3]

Dacă doriți ca a și b să fie liste separate, trebuie să legați b la o copie a lui a.

>>> a = [1, 2, 3] >>> b


= a.copy() >>> b[1] =
4
>>> a
[1, 2, 3]

Este similar cu utilizarea unui [:] sau list(a), ambele vor copia și a.

numara

Metoda de numărare numără aparițiile unui element dintr-o listă.

>>> ['la', 'fii', 'sau', 'nu', 'la', 'fii'].count('la') 2

>>> x = [[1, 2], 1, 1, [2, 1, [1, 2]]] >>> x.count(1) 2

>>> x.count([1, 2])


1

extinde
Metoda extinde vă permite să adăugați mai multe valori simultan, furnizând o secvență a valorilor pe care doriți să
le adăugați. Cu alte cuvinte, lista dumneavoastră originală a fost extinsă de cealaltă.

>>> a = [1, 2, 3] >>> b


= [4, 5, 6] >>>
a.extend(b)

37
Machine Translated by Google

Capitolul 2 Liste și tupluri

>>> a
[1, 2, 3, 4, 5, 6]

Acest lucru poate părea similar cu concatenarea, dar diferența importantă este că secvența extinsă (în acest caz, a)
este modificată. Acesta nu este cazul în concatenarea obișnuită, în care este returnată o secvență complet nouă.

>>> a = [1, 2, 3] >>>


b = [4, 5, 6] >>> a + b
[1, 2, 3, 4, 5, 6]

>>> a
[1, 2, 3]

După cum puteți vedea, lista concatenată arată exact la fel cu cea extinsă din exemplul anterior, dar nu s-a schimbat
de data aceasta. Deoarece concatenarea obișnuită trebuie să facă o nouă listă care să conțină copii ale lui a și b, nu
este la fel de eficientă ca utilizarea extinderii dacă ceea ce doriți este ceva de genul acesta:

>>> a = a + b

De asemenea, aceasta nu este o operațiune în loc — nu va modifica originalul. Efectul extinderii poate fi atins prin
atribuirea la felii, după cum urmează:

>>> a = [1, 2, 3] >>>


b = [4, 5, 6] >>>
a[len(a):] = b
>>> a
[1, 2, 3, 4, 5, 6]

Deși funcționează, nu este chiar la fel de lizibil.

index
Metoda indexului este utilizată pentru căutarea listelor pentru a găsi indexul primei apariții a unei valori.

>>> cavaleri = [„Noi”, „suntem”, „cei”, „cavalerii”, „cine”, „spune”, „ni”] >>>
cavaleri.index(„cine”) 4

>>> knights.index('hering')
Traceback (ultimul interior):
Fișierul „<pyshell>”, linia 1, în ?
knights.index('hering')
ValueError: list.index(x): x nu este în listă

Când căutați cuvântul „cine”, descoperiți că se află la indexul 4.

>>> cavaleri[4]
„cine”

Cu toate acestea, când căutați „hering”, obțineți o excepție, deoarece cuvântul nu este găsit deloc.

38
Machine Translated by Google

Capitolul 2 Liste și tupluri

introduce

Metoda de inserare este folosită pentru a insera un obiect într-o listă.

>>> numere = [1, 2, 3, 5, 6, 7] >>>


numere.inserat(3, 'patru') >>> numere
[1, 2, 3, 'patru', 5, 6, 7]

Ca și în cazul extinderii, puteți implementa inserarea cu atribuiri de felii.

>>> numere = [1, 2, 3, 5, 6, 7] >>>


numere[3:3] = ['patru'] >>> numere [1, 2,
3, 'patru', 5, 6, 7]

Acest lucru poate fi fantezist, dar nu este la fel de lizibil ca utilizarea inserției.

pop

Metoda pop elimină un element (în mod implicit, ultimul) din listă și îl returnează.

>>> x = [1, 2, 3] >>>


x.pop() 3

>>> x
[1, 2]
>>> x.pop(0)
1
>>> x
[2]

Notă Metoda pop este singura metodă listă care modifică lista și returnează o valoare (alta decât None).

Folosind pop, puteți implementa o structură de date comună numită stivă. Un astfel de teanc funcționează la fel ca un
teanc de farfurii. Puteți pune farfurii deasupra și puteți scoate farfurii de sus. Ultimul pe care îl puneți în stivă este
primul care va fi eliminat. (Acest principiu se numește ultimul intrat, primul ieșit sau LIFO.)
Numele general acceptate pentru cele două operațiuni de stivă (punerea lucrurilor și scoaterea lor) sunt
push și pop. Python nu are push, dar puteți folosi append în schimb. Metodele pop și append inversează reciproc
rezultatele, așa că dacă împingeți (sau adăugați) valoarea pe care tocmai ați apărut, ajungeți cu aceeași stivă.

>>> x = [1, 2, 3] >>>


x.append(x.pop())
>>> x
[1, 2, 3]

Sfat Dacă dori i o coadă FIFO (primul intrat, primul ie it), pute i utiliza insert(0, ...) în loc de append.
Alternativ, puteți continua să utilizați append , dar să înlocuiți pop(0) cu pop(). O soluție și mai bună ar fi folosirea
unui deque din modulul de colecții. Vezi capitolul 10 pentru mai multe informatii.

39
Machine Translated by Google

Capitolul 2 Liste și tupluri

elimina
Metoda de eliminare este utilizată pentru a elimina prima apariție a unei valori.

>>> x = ['la', 'fi', 'sau', 'nu', 'la', 'fi'] >>> x.remove('fi')

>>> x
['la', 'sau', 'nu', 'la', 'fii'] >>>
x.remove('bee')
Traceback (ultimul interior):
fișierul „<pyshell>”, linia 1, în ?
x.remove('albină')
ValueError: list.remove(x): x nu este în listă

După cum puteți vedea, numai prima apariție este eliminată și nu puteți elimina ceva (în acest caz, șirul
„albină”) dacă nu este în listă pentru început.
Este important să rețineți că aceasta este una dintre metodele de „schimbare la locul nereturnării”. Modifică
list, dar nu returnează nimic (spre deosebire de pop).

verso
Metoda inversă inversează elementele din listă. (Nu este foarte surprinzător, cred.)

>>> x = [1, 2, 3] >>>


x.reverse()
>>> x
[3, 2, 1]

Rețineți că reverse modifică lista și nu returnează nimic (la fel ca eliminarea și sortarea, de exemplu).

Sfat Dacă doriți să repetați o secvență în sens invers, puteți utiliza funcția inversă . Această funcție nu
returnează totuși o listă; returnează un iterator. (Aflați mai multe despre iteratoare în Capitolul 9.) Puteți
converti obiectul returnat cu listă.

>>> x = [1, 2, 3] >>>


listă(inversată(x)) [3, 2, 1]

fel
Metoda de sortare este folosită pentru a sorta listele la locul lor.3 Sortarea „la loc” înseamnă schimbarea listei originale astfel încât
elementele acesteia să fie în ordine sortată, mai degrabă decât pur și simplu returnarea unei copii sortate a listei.

>>> x = [4, 6, 2, 1, 7, 9] >>>


x.sort()
>>> x
[1, 2, 4, 6, 7, 9]

3 În cazul în care sunteți interesat, începând cu Python 2.3, metoda de sortare folosește un algoritm de sortare stabil.

40
Machine Translated by Google

Capitolul 2 Liste și tupluri

Ați întâlnit deja câteva metode care modifică lista fără a returna nimic și, în cele mai multe cazuri, acest comportament este
destul de natural (ca și în cazul append, de exemplu). Dar vreau să subliniez acest comportament în cazul unui fel, deoarece
atât de mulți oameni par să fie confuzi de el. Confuzia apare de obicei atunci când utilizatorii doresc o copie sortată a unei
liste, lăsând în pace originalul. Un mod intuitiv (dar greșit) de a face acest lucru este următorul:

>>> x = [4, 6, 2, 1, 7, 9] >>> y =


x.sort() # Nu face asta! >>> print(y)

Nici unul

Deoarece sortarea modifică x, dar nu returnează nimic, ajungeți să obțineți un x și ay sortați care conțin None. O modalitate
corectă de a face acest lucru ar fi să legați mai întâi y la o copie a lui x și apoi să sortați y, după cum urmează:

>>> x = [4, 6, 2, 1, 7, 9] >>> y =


x.copy() >>> y.sort()

>>> x
[4, 6, 2, 1, 7, 9] >>> y [1,
2, 4, 6, 7, 9]

Pur și simplu atribuirea lui x lui y nu ar funcționa, deoarece atât x, cât și y s-ar referi la aceeași listă. O altă modalitate de
a obține o copie sortată a unei liste este utilizarea funcției sortate.

>>> x = [4, 6, 2, 1, 7, 9] >>> y =


sortat(x)
>>> x
[4, 6, 2, 1, 7, 9] >>> y [1,
2, 4, 6, 7, 9]

Această funcție poate fi folosită de fapt pe orice secvență, dar va returna întotdeauna o listă.4

>>> sortat('Python')
[„P”, „h”, „n”, „o”, „t”, „y”]

Dacă doriți să sortați elementele în ordine inversă, puteți utiliza sortarea (sau sortarea), urmată de un apel la metoda
inversă, sau puteți utiliza argumentul invers, descris în secțiunea următoare.

Sortare avansată
Metoda de sortare ia două argumente opționale: cheie și inversă. Dacă doriți să le utilizați, de obicei le specificați după
nume (așa-numitele argumente ale cuvintelor cheie; aflați mai multe despre acestea în Capitolul 6). Argumentul cheie
este similar cu argumentul cmp: furnizați o funcție și este folosită în procesul de sortare.
Cu toate acestea, în loc să fie utilizată direct pentru a determina dacă un element este mai mic decât altul, funcția este
folosită pentru a crea o cheie pentru fiecare element, iar elementele sunt sortate în funcție de aceste chei. Deci, de exemplu,
dacă doriți să sortați elementele în funcție de lungimile lor, utilizați len ca funcție cheie.

4 Funcția sortată poate fi, de fapt, utilizată pe orice obiect iterabil. Aflați mai multe despre obiectele iterabile în Capitolul 9.

41
Machine Translated by Google

Capitolul 2 Liste și tupluri

>>> x = ['aardvark', 'abalone', 'acme', 'add', 'aerate'] >>> x.sort(key=len)

>>> x
[„adăugați”, „acme”, „aerați”, „abalon”, „aardvark”]

Celălalt argument al cuvântului cheie, invers, este pur și simplu o valoare de adevăr (adevărat sau fals; veți afla mai multe
despre acestea în Capitolul 5) care indică dacă lista trebuie sortată invers.

>>> x = [4, 6, 2, 1, 7, 9] >>>


x.sort(revers=Adevărat)
>>> x
[9, 7, 6, 4, 2, 1]

Argumentele cheie și inverse sunt disponibile și în funcția sortată. În multe cazuri, folosirea unei funcții personalizate
pentru tastă va fi utilă. Înveți cum să-ți definești propriile funcții în Capitolul 6.

Sfat Dacă doriți să citiți mai multe despre sortare, vă recomandăm să consultați „Sorting Mini-HOW
TO”, aflat la https://wiki.python.org/moin/HowTo/Sorting.

Tupluri: secvențe imuabile


Tuplurile sunt secvențe, la fel ca listele. Singura diferență este că tuplurile nu pot fi modificate. (După cum probabil
ați observat, acest lucru este valabil și pentru șiruri de caractere.) Sintaxa tuplului este simplă - dacă separați unele
valori cu virgule, aveți automat un tuplu.

>>> 1, 2, 3 (1,
2, 3)

După cum puteți vedea, tuplurile pot fi, de asemenea, (și adesea sunt) incluse în paranteze.

>>> (1, 2, 3) (1, 2,


3)

Tuplu gol este scris ca două paranteze care nu con in nimic.

>>> () ()

Deci, s-ar putea să vă întrebați cum să scrieți un tuplu care conține o singură valoare. Acest lucru este puțin ciudat - trebuie să
includeți o virgulă, chiar dacă există o singură valoare.

>>> 42
42
>>> 42,
(42,) >>>
(42,) (42,)

42
Machine Translated by Google

Capitolul 2 Liste și tupluri

Ultimele două exemple produc tupluri de lungime unu, în timp ce primul nu este deloc un tuplu. Virgula este crucială.
Pur și simplu adăugarea de paranteze nu va ajuta: (42) este exact la fel cu 42. Cu toate acestea, o singură virgulă poate
schimba complet valoarea unei expresii.

>>> 3 * (40 + 2)
126
>>> 3 * (40 + 2,) (42,
42, 42)

Funcția tuplu funcționează aproape în același mod ca listă: ia un argument de secvență și îl convertește într-un tuplu.5
Dacă argumentul este deja un tuplu, este returnat neschimbat.

>>> tuplu([1, 2, 3]) (1, 2, 3)


>>> tuplu('abc') ('a', 'b', 'c')
>>> tuplu((1, 2, 3)) (1, 2, 3)

După cum probabil ați înțeles, tuplurile nu sunt foarte complicate - și nu puteți face mare lucru cu ele, decât să le creați
și să accesați elementele lor, și faceți acest lucru la fel ca și în cazul altor secvențe.

>>> x = 1, 2, 3 >>>
x[1] 2 >>> x[0:2] (1,
2)

Feliile unui tuplu sunt, de asemenea, tupluri, la fel cum feliile de listă sunt ele însele liste.

Există două motive importante pentru care trebuie să știți despre tupluri.

•Ele pot fi folosite ca chei în mapări (și membri ai seturilor); listele nu pot fi folosite asta
cale. Veți afla mai multe mapări în capitolul 4.

•Sunt returnate de unele funcții și metode încorporate, ceea ce înseamnă că dvs


trebuie să se ocupe de ei. Atâta timp cât nu încerci să le schimbi, „a te descurca” cu ele înseamnă
cel mai adesea să le tratezi ca niște liste (cu excepția cazului în care ai nevoie de metode precum
indexare și numărare, pe care tuplurile nu le au).

În general, listele vor fi probabil adecvate nevoilor dvs. de secvențiere.

5 Ca și lista, tuplu nu este cu adevărat o funcție – este un tip. Și, ca și în cazul listei, puteți ignora acest lucru în siguranță pentru moment.

43
Machine Translated by Google

Capitolul 2 Liste și tupluri

Un rezumat rapid
Să trecem în revistă câteva dintre cele mai importante concepte abordate în acest capitol.

Secvențe: O secvență este o structură de date în care elementele sunt numerotate (începând cu
zero). Exemple de tipuri de secvențe sunt liste, șiruri și tupluri.
Dintre acestea, listele sunt modificabile (le puteți schimba), în timp ce tuplurile și șirurile sunt
imuabile (odată ce sunt create, sunt fixe). Părți ale unei secvențe pot fi accesate prin feliere,
furnizând doi indici care indică pozițiile de început și de sfârșit ale feliei. Pentru a modifica o
listă, atribuiți noi valori pozițiilor acesteia sau utilizați atribuirea pentru a suprascrie felii întregi.

Apartenența: dacă o valoare poate fi găsită într-o secvență (sau alt container) este verificat cu
operatorul in. Utilizarea în cu șiruri este un caz special - vă va permite să căutați subșiruri.

Metode: Unele dintre tipurile încorporate (cum ar fi liste și șiruri de caractere, dar nu tupluri)
au multe metode utile atașate. Acestea sunt puțin ca funcții, cu excepția faptului că sunt
strâns legate de o anumită valoare. Metodele sunt un aspect important al programării
orientate pe obiecte, pe care îl analizăm în Capitolul 7.

Funcții noi în acest capitol


Func ie Descriere
len(seq) Returnează lungimea unei secvențe

list(seq) Convertește o secvență într-o listă

max(args) Returnează maximul unei secvențe sau al unui set de argumente

min(args) Returnează minimul unei secvențe sau al unui set de argumente

inversat(seq) Vă permite să repetați o secvență în sens invers

sortat(seq) Returnează o listă sortată a elementelor secv

tuplu(seq) Convertește o secvență într-un tuplu

Ce acum?
Acum că sunteți familiarizat cu secvențele, să trecem la secvențele de caractere, cunoscute și sub numele de șiruri.

44
Machine Translated by Google

CAPITOLUL 3

Lucrul cu șiruri

Ați mai văzut șiruri și știți cum să le faceți. De asemenea, v-ați uitat la cum să accesați caracterele lor individuale prin
indexare și tăiere. În acest capitol, vedeți cum să le utilizați pentru a formata alte valori (pentru tipărire, de exemplu) și
aruncați o privire rapidă asupra lucrurilor utile pe care le puteți face cu metodele cu șir, cum ar fi împărțirea, unirea,
căutarea și multe altele.

Operații de bază cu șiruri


Toate operațiile standard de secvență (indexare, tăiere, înmulțire, apartenență, lungime, minim și maxim)
funcționează cu șiruri, așa cum ați văzut în capitolul anterior. Amintiți-vă, totuși, că șirurile de caractere sunt imuabile,
astfel încât toate tipurile de atribuiri de articole sau felii sunt ilegale.

>>> site = 'http://www.python.org' >>> site


web[-3:] = 'com'
Traceback (cel mai recent apel ultimul):
fișierul „<pyshell#19>”, linia 1, în ? site-ul
web[-3:] = „com”
TypeError: obiectul nu acceptă atribuirea secțiunilor

Formatarea șirurilor: versiunea scurtă


Dacă sunteți nou în programarea Python, sunt șanse să nu aveți nevoie de toate opțiunile disponibile în formatarea
șirurilor Python, așa că vă voi oferi versiunea scurtă aici. Dacă sunteți interesat de detalii, aruncați o privire la secțiunea
„Formatarea șirurilor: versiunea lungă”, care urmează. În caz contrar, citiți acest lucru și treceți la secțiunea „Metode șiruri”.

Formatarea valorilor ca șiruri de caractere este o operație atât de importantă și una care trebuie să răspundă unor astfel de diverse
set de cerințe, că mai multe abordări au fost adăugate limbajului de-a lungul anilor. Din punct de vedere istoric, soluția
principală a fost utilizarea operatorului de formatare a șirurilor (numit corect), semnul procentului. Comportamentul
acestui operator emulează funcția clasică printf din limbajul C. În stânga %, plasați un șir (șirul de format); în dreapta
acestuia, plasați valoarea pe care doriți să o formatați. Puteți folosi o singură valoare, cum ar fi un șir sau un număr, puteți
folosi un tuplu de valori (dacă doriți să formatați mai multe) sau, așa cum am discutat în capitolul următor, puteți folosi un
dicționar. Cel mai frecvent caz este tuplu.

>>> format = "Bună ziua, %s. %s suficient pentru tine?"


>>> valori = ('world', 'Hot') >>> format % values 'Bună,
lume. Destul de fierbinte pentru tine?

© Magnus Lie Hetland 2017 45


ML Hetland, Beginning Python, DOI 10.1007/978-1-4842-0028-5_3
Machine Translated by Google

Capitolul 3 Lucrul cu șiruri

Părțile %s ale șirului de format sunt numite specificatori de conversie. Acestea marchează locurile în care urmează să fie
introduse valorile. S înseamnă că valorile trebuie formatate ca și cum ar fi șiruri de caractere; daca nu sunt, se vor converti cu str.
Alți specificatori conduc la alte forme de conversie; de exemplu, %.3f va formata valoarea ca un număr în virgulă mobilă cu trei
zecimale.
Această metodă de formatare încă funcționează și este încă foarte activă într-o mulțime de coduri, așa că s-ar putea să o
întâlniți. O altă soluție pe care o puteți întâlni sunt așa-numitele șiruri de șablon, care au apărut cu ceva timp în urmă ca o
încercare de a simplifica mecanismul de formatare de bază, folosind o sintaxă similară cu shell-urile UNIX, de exemplu.

>>> din Șablon de import de șir >>> tmpl =


Template("Bună ziua, $cine! $ce suficient pentru tine?") >>>
tmpl.substitute(who="Marte", ce="Prăfuit")
„Bună, Marte! Suficient de praf pentru tine?

Argumentele cu semnele egale în ele sunt așa-numitele argumente de cuvinte cheie — veți auzi multe despre acestea în Capitolul
6. În contextul formatării șirurilor de caractere, vă puteți gândi la ele doar ca la o modalitate de a furniza valori câmpurilor de
înlocuire numite. .
Atunci când scrieți coduri noi, mecanismul de alegere este metoda șirurilor de format, care combină și extinde punctele
forte ale metodelor anterioare. Fiecare câmp de înlocuire este inclus între paranteze și poate include un nume, precum și
informații despre cum să convertiți și să formatați valoarea furnizată pentru acel câmp.
Cel mai simplu caz este în cazul în care câmpurile nu au nume sau în care fiecare nume este doar un index.

>>> „{}, {} și {}”.format(„primul”, „al doilea”, „al treilea”) „primul, al doilea și al


treilea” >>> „{0}, {1} și {2} „.format(„primul”, „al doilea”, „al treilea”) „primul,
al doilea și al treilea”

Indicii nu trebuie să fie astfel în ordine, totuși.

>>> "{3} {0} {2} {1} {3} {0}".format("fi", "nu", "sau", "la") 'a fi sau a nu fi'

Câmpurile denumite funcționează exact așa cum era de așteptat.

>>> din importul matematic pi


>>> „{nume} este aproximativ {valoare:.2f}.”.format(valoare=pi, nume="π") „π este aproximativ
3,14.'

Ordinea argumentelor cuvintelor cheie nu contează, desigur. În acest caz, am furnizat și un specificator de format de .2f, separat
de numele câmpului prin două puncte, ceea ce înseamnă că vrem formatare flotantă cu trei zecimale. Fără specificat, rezultatul ar
fi următorul:

>>> „{nume} este aproximativ {valoare}.”.format(value=pi, name="π") „π este aproximativ


3,141592653589793.'

În cele din urmă, în Python 3.6, există o comandă rapidă pe care o puteți folosi dacă aveți variabile numite identic
cu câmpurile de înlocuire corespunzătoare. În acest caz, puteți folosi așa-numitele șiruri f, scrise cu prefixul f.

>>> din import matematic e


>>> f"Constanta lui Euler este aproximativ {e}."
„Constanta lui Euler este de aproximativ 2,718281828459045.”

46
Machine Translated by Google

Capitolul 3 Lucrul cu șiruri

Aici, câmpul de înlocuire numit e pur și simplu extrage valoarea variabilei cu același nume, pe măsură ce șirul este construit.
Aceasta este echivalentă cu următoarea expresie, puțin mai explicită:

>>> „Constanta lui Euler este aproximativ {e}.”.format(e=e)


„Constanta lui Euler este de aproximativ 2,718281828459045.”

Formatarea șirurilor: versiunea lungă


Facilitățile de formatare a șirurilor de caractere sunt extinse, așa că chiar și această versiune lungă nu poate face o explorare
completă a tuturor detaliilor sale, dar să aruncăm o privire la componentele principale. Ideea este că numim metoda format pe
un șir, furnizându-i valori pe care dorim să le formatăm. Șirul conține informații despre cum se efectuează această formatare,
specificate într-un mini-limbaj șablon. Fiecare valoare este îmbinată în șir într-unul dintre mai multe câmpuri de înlocuire, fiecare
dintre acestea fiind cuprins între acolade. Dacă doriți să includeți acolade literale în rezultatul final, puteți să le specificați folosind
acolade duble în șirul de format, adică {{ sau }}.

>>> "{{ceci n'est pas une replacement field}}".format() "{ceci n'est pas une
replacement field}"

Cea mai interesantă parte a unui șir de format se găsește în interiorul câmpurilor de înlocuire, constând din următoarele
părți, toate fiind opționale:

• Un nume de câmp. Un index sau un identificator. Aceasta ne spune ce valoare va fi formatată și îmbinată
în acest câmp specific. Pe lângă denumirea obiectului în sine, putem numi și o anumită parte a
valorii, cum ar fi un element dintr-o listă, de exemplu.

• Un steag de conversie. Un semn de exclamare, urmat de un singur caracter. The


Cele acceptate în prezent sunt r (pentru repr), s (pentru str) sau a (pentru ascii). Dacă este furnizat,
acest flag suprascrie mecanismele de formatare proprii ale obiectului și folosește funcția specificată
pentru a-l transforma într-un șir înainte de orice formatare ulterioară.

• Un specificator de format. Două puncte, urmate de o expresie în mini-limba cu specificarea formatului.


Acest lucru ne permite să specificăm detaliile formatării finale, inclusiv tipul de formatare (de
exemplu, șir, virgulă mobilă sau număr hexazecimal), lățimea câmpului și precizia numerelor, modul
de afișare a semnelor și a separatorilor de mii și diverse forme de aliniere și umplutură.

Să ne uităm la unele dintre aceste elemente mai detaliat.

Nume de câmpuri de înlocuire În cel mai simplu

caz, doar furnizați argumente fără nume pentru a formata și utilizați câmpuri nenumite în șirul de format. Câmpurile și
argumentele sunt apoi împerecheate în ordinea în care sunt date. De asemenea, puteți furniza argumentelor nume, care sunt
apoi folosite în câmpurile de înlocuire pentru a solicita aceste valori specifice. Cele două strategii pot fi amestecate liber.

>>> "{foo} {} {bar} {}".format(1, 2, bar=4, foo=3) '3 1 4 2'

Indicii argumentelor nenumite pot fi, de asemenea, utilizați pentru a le solicita în afara ordinului.

>>> "{foo} {1} {bar} {0}".format(1, 2, bar=4, foo=3) '3 2 4 1'

47
Machine Translated by Google

Capitolul 3 Lucrul cu șiruri

Combinarea numerotării manuale și automate a câmpurilor nu este totuși permisă, deoarece aceasta ar putea deveni rapid
confuză.
Dar nu trebuie să utilizați valorile furnizate în sine - puteți accesa părți din ele, la fel ca în
cod Python obișnuit. Iată un exemplu:

>>> nume complet = ["Alfred", "Smoketoomuch"]


>>> „Domnul {nume[1]}”.format(nume=nume complet)
'Mr Smoketoomuch'
>>> import math >>>
tmpl = "Modulul {mod.__name__} definește valoarea {mod.pi} pentru π" >>> tmpl.format(mod=math)

„Modulul de matematică definește valoarea 3,141592653589793 pentru π”

După cum puteți vedea, putem folosi atât indexarea, cât și notația cu puncte pentru metode, atribute sau variabile și funcții
în modulele importate. (Variabila __name__ cu aspect ciudat conține numele unui anumit modul.)

Conversii de bază
După ce ați specificat ce trebuie să conțină un câmp, puteți adăuga instrucțiuni despre cum să-l formatați. În primul rând,
puteți furniza un semnal de conversie.

>>> print("{pi!s} {pi!r} {pi!a}".format(pi="π")) π 'π' '\u03c0'

Cele trei steaguri (s, r și a) duc la conversie folosind str, repr și, respectiv, ascii. Funcția str creează, în general, o versiune de șir
cu aspect natural a valorii (în acest caz, nu face nimic șirului de intrare); șirul repr încearcă să creeze o reprezentare Python a
valorii date (în acest caz, un șir literal), în timp ce funcția ascii insistă să creeze o reprezentare care să conțină doar caractere
permise în codificarea ASCII. Acest lucru este similar cu modul în care a funcționat repr în Python 2.

De asemenea, puteți specifica tipul de valoare pe care o convertiți sau, mai degrabă, ce fel de valoare doriți să fie tratată.
De exemplu, puteți furniza un număr întreg, dar doriți să îl tratați ca un număr zecimal. Faceți acest lucru folosind caracterul f
(pentru punct fix) în specificația formatului, adică după separatorul de două puncte.

>>> „Numărul este {num}”.format(num=42)


„Numărul este 42”
>>> „Numărul este {num:f}”.format(num=42)
„Numărul este 42.000000”

Sau poate ai prefera să-l formatezi ca un număr binar?

>>> „Numărul este {num:b}”.format(num=42)


„Numărul este 101010”

Există mai mulți astfel de specificatori de tip. Pentru o listă, consultați Tabelul 3-1.

48
Machine Translated by Google

Capitolul 3 Lucrul cu șiruri

Tabelul 3-1. Specificatorii tipului de formatare a șirurilor

Tip Sens
b Formatează un număr întreg ca un număr binar.

c Interpretează un număr întreg ca punct de cod Unicode.

d Formatează un număr întreg ca un număr zecimal. Implicit pentru numere întregi.

e Formatează un număr zecimal în notație științifică cu e pentru a indica exponentul.

E La fel ca e, dar folosește E pentru a indica exponentul.

f Formatează un număr zecimal cu un număr fix de zecimale.

F La fel ca f, dar formatează valori speciale (nan și inf) în majuscule.

g Alege automat între notație fixă și notație științifică. Implicit pentru numere zecimale, cu excepția faptului că
versiunea implicită are cel puțin o zecimală.

G La fel ca g, dar cu majuscule indicatorul exponent și valori speciale.


n La fel ca g, dar inserează caractere separatoare de numere dependente de localitate.

o Formatează un număr întreg ca un număr octal.

s Formatează un șir așa cum este. Implicit pentru șiruri.

X Formatează un număr întreg ca un număr hexazecimal, cu litere mici.

X La fel ca x, dar cu litere mari.

Formatează un număr ca procent (înmulțit cu 100, formatat cu f, urmat de %).


%

Separatoare de lățime, precizie și mii Când formatați numere în virgulă mobilă (sau alte

tipuri de numere zecimale mai specializate), implicit este afișarea a șase cifre după virgulă și, în toate cazurile, implicit este să lăsați
valoarea formatată să aibă exact lățimea necesară pentru a-l afișa, fără nici un fel de umplutură. Este posibil ca aceste valori implicite
să nu fie exact ceea ce doriți, desigur, și vă puteți mări specificația formatului cu detalii despre lățime și precizie pentru a se potrivi
preferințelor dvs.

Lățimea este indicată printr-un număr întreg, după cum urmează:

>>> „{num:10}”.format(num=3)
'
3'
>>> „{name:10}”.format(name="Bob")
'
— Bob

Numerele și șirurile sunt aliniate diferit, după cum puteți vedea. Vom reveni la aliniere în secțiunea următoare.
Precizia este, de asemenea, specificată de un număr întreg, dar este precedată de un punct, făcând aluzie la punctul zecimal.

>>> „Ziua Pi este {pi:.2f}”.format(pi=pi)


„Ziua Pi este 3.14”

Aici, am specificat în mod explicit tipul f, deoarece implicit tratează precizia puțin diferit. (Consultați Bibliotecii de referință Python
pentru regulile precise.) Puteți combina lățimea și precizia, desigur.

>>> "{pi:10.2f}".format(pi=pi) 3.14'


'

De fapt, puteți folosi precizia și pentru alte tipuri, deși probabil că nu veți avea nevoie de asta foarte des. 49
Machine Translated by Google

Capitolul 3 Lucrul cu șiruri

>>> "{:.5}".format("Guido van Rossum")


"Guido"

În cele din urmă, puteți indica că doriți separatori de mii, folosind o virgulă.

>>> „Un googol este {:,}”.format(10**100)


„Un gol este 10.000.000.000.000.000.000.000.000.000.000.000.000.000.000.000.000.000.000.00
0.000.000.000.000.000.000.000.000.000.000.000.000.000.000.00 0.000.000.000.000.000.000000000000000000000000000000000

Când este utilizată împreună cu celelalte elemente de formatare, această virgulă ar trebui să se afle între lățimea și punctul
care indică precizia.1

Semne, aliniere și zero-padding O parte din mașina de formatare

este destinată formatării numerelor, de exemplu, pentru tipărirea unui tabel cu valori frumos aliniate. Lățimea și precizia ne
duc cea mai mare parte a drumului până acolo, dar rezultatul nostru frumos poate fi în continuare aruncat dacă includem
numere negative. Și, după cum ați văzut, șirurile și numerele sunt aliniate diferit; poate vrem să schimbăm asta, de exemplu,
pentru a include o bucată de text în mijlocul unei coloane de numere? Înainte de numerele de lățime și precizie, puteți pune
un „steag”, care poate fi fie zero, plus, minus sau gol. Un zero înseamnă că numărul va fi completat cu zero.

>>> '{:010.2f}'.format(pi) '0000003.14'

Specificați alinierea la stânga, la dreapta și la centru cu <, > și, respectiv, ^.

>>> print('{0:<10.2f}\n{0:^10.2f}\n{0:>10.2f}'.format(pi)) 3.14

3.14
3.14

Puteți mări specificatorul de aliniere cu un caracter de umplere, care este utilizat în locul caracterului spațiu.

>>> "{:$^15}".format(" CÂȘTIGĂ MARE ")


„$$$ CÂȘTIGĂ MARE $$$”

Există, de asemenea, specificatorul mai specializat =, care plasează orice caracter de umplere între semn și cifre.

>>> print('{0:10.2f}\n{1:10.2f}'.format(pi, -pi))


3.14
-3.14
>>> print('{0:10.2f}\n{1:=10.2f}'.format(pi, -pi))
3.14
- 3.14

Dacă doriți să includeți și semne pentru numere pozitive, utilizați specificatorul + (după specificatorul de aliniere,
dacă există), în loc de -. Dacă utilizați caracterul spațiu, pozitiv va avea un spațiu inserat în loc
de +.

1 Și dacă doriți un separator de mii dependent de local, ar trebui să utilizați în schimb tipul n.

50
Machine Translated by Google

Capitolul 3 Lucrul cu șiruri

>>> print('{0:-.2}\n{1:-.2}'.format(pi, -pi)) # Implicit 3.1 -3.1 >>>


print('{0:+.2} \n{1:+.2}'.format(pi, -pi)) +3.1 -3.1 >>> print('{0: .2}\n{1:
.2}'.format(pi, - pi))

3.1
-3.1

O componentă finală este opțiunea hash (#), pe care o plasați între semn și lățime (dacă sunt prezente).
Acest lucru declanșează o formă alternativă de conversie, cu detalii diferite între tipuri. De exemplu, pentru
conversia binară, octală și hexazecimală, se adaugă un prefix.

>>> "{:b}".format(42)
'101010' >>>
"{:#b}".format(42) '0b101010'

Pentru diferite tipuri de numere zecimale, forțează includerea punctului zecimal (iar pentru g, păstrează zerouri
zecimale).

>>> "{:g}".format(42) '42'


>>> "{:#g}".format(42)
'42.0000'

În exemplul prezentat în Lista 3-1, am folosit formatarea șirurilor de două ori pe aceleași șiruri - prima dată pentru a
insera lățimile câmpurilor în ceea ce urmează să devină eventualii specificatori de format. Deoarece aceste informații
sunt furnizate de utilizator, nu pot codifica lățimile câmpurilor.

Lista 3-1. Exemplu de formatare a șirurilor

# Imprimați o listă de prețuri formatată cu o lățime dată

width = int(input('Vă rugăm să introduceți lățimea: '))

price_width = 10
item_width = lățime - price_width

header_fmt = '{{:{}}}{{:>{}}}'.format(item_width, price_width) fmt


= '{{:{}}}{{:>{}.2f}}'.format(item_width, price_width)

print('=' * latime)

print(header_fmt.format('Articol', 'Pre '))

print('-' * latime)

print(fmt.format('Mere', 0,4))
print(fmt.format('Pere', 0,5))
print(fmt.format('Cantalopi', 1,92))

51
Machine Translated by Google

Capitolul 3 Lucrul cu șiruri

print(fmt.format('Caise uscate (16 oz)', 8)) print(fmt.format('Prune


(4 lbs.)', 12))

print('=' * latime)

Următorul este un exemplu de rulare a programului:

Vă rugăm introduceți lățimea: 35


====================================
Articol Preț
------------------------------------

Merele 0,40
Pere 0,50
Cantalupi Caise 1.92
uscate (16 oz.) 8.00
Prune uscate (4 lbs.) 12.00
====================================

Metode cu șiruri
Ați întâlnit deja metode în liste. Șirurile de caractere au un set mult mai bogat de metode, în parte pentru că șirurile au
„moștenit” multe dintre metodele lor din modulul șiruri de caractere în care se aflau ca funcții în versiunile anterioare de
Python (și unde le puteți găsi în continuare, dacă simțiți nevoia) .
Deoarece există atât de multe metode de șir, doar câteva dintre cele mai utile sunt descrise aici. Pentru o referință
completă, consultați Anexa B. În descrierea metodelor de șir, veți găsi referințe la alte metode de șir înrudite în acest capitol
(marcat „Vezi și”) sau în Anexa B.

DAR STRING NU E MOART

Chiar dacă metodele cu șir au depășit complet modulul șir , modulul include în continuare câteva constante și funcții
care nu sunt disponibile ca metode cu șir. Următoarele sunt câteva constante utile disponibile din string2 :

• string.digits: Un șir care conține cifrele 0–9 • string.ascii_letters:

Un șir care conține toate literele ASCII (majuscule și minuscule) • string.ascii_lowercase: Un șir care conține

toate literele ASCII minuscule • string.printable: Un șir care conține toate caractere ASCII imprimabile •

string.punctuation: Un șir care conține toate caracterele de punctuație ASCII • string.ascii_uppercase: Un șir

care conține toate literele ASCII majuscule

În ciuda faptului că se tratează în mod explicit cu caracterele ASCII, valorile sunt de fapt șiruri Unicode (necodificate).

Pentru o descriere mai detaliată a modulului, consultați Secțiunea 6.1 a Bibliotecii de referință Python (https://
2

docs. python.org/3/library/string.html).

52
Machine Translated by Google

Capitolul 3 Lucrul cu șiruri

centru
Metoda centrală centrează șirul prin umplerea acestuia pe fiecare parte cu un anumit caracter de umplere - spații în
mod implicit.

>>> „The Middle by Jimmy Eat World”.center(39)


' '
The Middle de Jimmy Eat World >>>
„The Middle de Jimmy Eat World”.center(39, "*")
„*****The Middle de Jimmy Eat World*****”

În Anexa B: ljust, rjust, zfill.

găsi
Metoda find găsește un subșir într-un șir mai mare. Returnează indexul din stânga unde se găsește subșirul. Dacă nu
este găsit , este returnat –1.

>>> 'Cu un moo-moo aici, și un moo-moo acolo'.find('moo') 7 >>> title =


"Circul zburător al lui Monty Python" >>> title.find('Monty')

0
>>> title.find('Python') 6

>>> title.find('Zboară') 15

>>> title.find('Zirquss')
-1

În prima noastră întâlnire cu calitatea de membru în Capitolul 2, am creat o parte a unui filtru de spam utilizând
expresia „$$$” în subiect. Am fi putut folosi, de asemenea, find (care ar fi funcționat și înainte de Python 2.3, când în
ar putea fi folosit doar atunci când se verifică apartenența unui singur caracter în șiruri).

>>> subiect = '$$$ Imbogateste-te acum!!! $$$'


>>> subject.find('$$$')
0

Notă Metoda string find nu returnează o valoare booleană. Dacă find returnează 0, așa cum a făcut aici,
înseamnă că a găsit subșirul, la indicele zero.

De asemenea, puteți furniza un punct de plecare pentru căutarea dvs. și, opțional, un punct de final.

>>> subiect = '$$$ Imbogateste-te acum!!! $$$'


>>> subject.find('$$$')
0
>>> subject.find('$$$', 1) # Furnizează doar începutul
20
>>> subiect.găsește('!!!') 16

53
Machine Translated by Google

Capitolul 3 Lucrul cu șiruri

>>> subject.find('!!!', 0, 16) # Furnizarea începutului și sfârșitului -1

Rețineți că intervalul specificat de valorile de pornire și oprire (al doilea și al treilea parametri) include primul
indice, dar nu și al doilea. Aceasta este o practică comună în Python.

În Anexa B: rfind, index, rindex, count, startswith, endswith.

a te alatura

O metodă de șir foarte importantă, îmbinarea este inversul împărțirii. Este folosit pentru a uni elementele unei secvențe.

>>> seq = [1, 2, 3, 4, 5] >>> sep


= '+' >>> sep.join(seq) #
Încercarea de a se alătura unei liste de numere Traceback (cel mai
recent apel ultimul): Fișier „<stdin>”, linia 1, în ?

TypeError: elementul secvenței 0: șir așteptat, int găsit >>> seq = ['1',
'2', '3', '4', '5'] >>> sep.join(seq) # Alăturarea unei liste de șiruri de
caractere '1+2+3+4+5' >>> dirs = '', 'usr', 'bin', 'env' >>> '/'.join(dirs) '/
usr/bin/env ' >>> print('C:' + '\\'.join(dirs))

C:\usr\bin\env

După cum puteți vedea, elementele secvenței care urmează să fie unite trebuie să fie toate șiruri. Observați
cum în ultimele două exemple folosesc o listă de directoare și le formatez conform convențiilor UNIX și DOS/
Windows pur și simplu folosind un separator diferit (și adăugând un nume de unitate în versiunea DOS).

Vezi și: split.

inferior

Metoda mai mică returnează o versiune cu minuscule a șirului.

>>> 'Trondheim Hammer Dance'.lower()


'Trondheim Hammer Dance'

Acest lucru poate fi util dacă doriți să scrieți cod care nu ține seama de majuscule, adică cod care ignoră diferența
dintre literele mari și mici. De exemplu, să presupunem că doriți să verificați dacă un nume de utilizator este
găsit într-o listă. Dacă lista dvs. conține șirul „gumby” și utilizatorul își introduce numele ca „Gumby”, nu îl veți găsi.

>>> dacă 'Gumby' în ['gumby', 'smith', 'jones']: print('L-am găsit!')


...
>>>

Desigur, același lucru se va întâmpla dacă ați stocat „Gumby” și utilizatorul scrie „gumby”, sau chiar „GUMBY”.
O soluție la aceasta este să convertiți toate numele în litere mici atât la stocare, cât și la căutare. Codul ar arăta
cam așa:

54
Machine Translated by Google

Capitolul 3 Lucrul cu șiruri

>>> name = 'Gumby'


>>> names = ['gumby', 'smith', 'jones'] >>> if
name.lower() in names: print('Am gasit!')
...
L-am gasit!
>>>

Vezi și: islower, istitle, isupper, traduce.

În Anexa B: majuscule, casefold, swapcase, titlu, upper.

TITLUL CASĂ

O rudă cu mai mică este metoda titlului (vezi Anexa B), în care titlul majusculează un șir - adică toate
cuvintele încep cu caractere majuscule, iar toate celelalte caractere sunt cu litere mici. Cu toate acestea,
limitele cuvintelor sunt definite într-un mod care poate da unele rezultate nefirești.

>>> „asta-i tot oameni”.title()


„Asta e tot, oameni buni”

O alternativă este funcția capwords din modulul șir .

>>> import string >>>


string.capwords(„asta e tot, oameni buni”)
Asta e tot, oameni buni"

Desigur, dacă doriți un titlu cu majuscule cu adevărat corect (care depinde de stilul pe care îl utilizați -
eventual articole cu minuscule, conjuncții de coordonare, prepoziții cu mai puțin de cinci litere și așa mai
departe), practic sunteți pe cont propriu.

replace

Metoda înlocuire returnează un șir în care toate aparițiile unui șir au fost înlocuite cu altul.

>>> 'Acesta este un test'.replace('este', 'eez')


„Nu e un test”

Dacă ați folosit vreodată caracteristica „căutare și înlocuire” a unui program de procesare de text, veți vedea fără
îndoială utilitatea acestei metode.

Vezi și: traduce.

În Anexa B: expandtabs.

split O

metodă de șir foarte importantă, split este inversul joinului și este folosită pentru a împărți un șir într-o secvență.

55
Machine Translated by Google

Capitolul 3 Lucrul cu șiruri

>>> '1+2+3+4+5'.split('+') ['1', '2',


'3', '4', '5'] >>> '/usr/bin /
env'.split('/') ['', 'usr', 'bin', 'env']

>>> 'Utilizarea implicită'.split()


[„Utilizarea”, „la”, „implicit”]

Rețineți că, dacă nu este furnizat niciun separator, valoarea implicită este împărțirea pe toate rulările de caractere consecutive cu spații
albe (spații, tab-uri, linii noi și așa mai departe).

Vezi și: join.

În Anexa B: partiție, rpartiție, rsplit, splitlines.

strip

Metoda strip returnează un șir în care spațiul alb din stânga și dreapta (dar nu în interior) a fost eliminat (eliminat).

'
>>> spațiul alb intern este păstrat '.bandă()
„spațiul alb intern este păstrat”

Ca și în cazul inferior, banda poate fi utilă atunci când se compară intrarea cu valorile stocate. Să revenim la exemplul
numelui de utilizator din secțiunea de mai jos și să presupunem că utilizatorul introduce din greșeală un spațiu după numele său.

>>> names = ['gumby', 'smith', 'jones'] >>> name =


'gumby ' >>> dacă numele în nume: print('L-am
găsit!')
...
>>> dacă name.strip() în nume: print('Am găsit!')
...
L-am gasit!
>>>

De asemenea, puteți specifica ce caractere vor fi eliminate, listându-le pe toate într-un parametru șir.

>>> '*** SPAM * pentru * toată lumea!!! ***'.strip(' *!')


„SPAM* pentru * toată lumea”

Decaparea se efectuează numai la capete, astfel încât asteriscurile interne nu sunt îndepărtate.

În Anexa B: lstrip, rstrip.

Traduceți

Similar cu înlocuirea, translate înlocuiește părți ale unui șir, dar spre deosebire de înlocuire, translate funcționează
numai cu caractere individuale. Puterea sa constă în faptul că poate efectua mai multe înlocuiri simultan și poate face
acest lucru mai eficient decât înlocuirea.
Există destul de multe utilizări mai degrabă tehnice pentru această metodă (cum ar fi traducerea caracterelor newline sau
alte caractere speciale dependente de platformă), dar să luăm în considerare un exemplu mai simplu (deși puțin mai
prostesc). Să presupunem că doriți să traduceți un text simplu în limba engleză într-unul cu accent german. Pentru a face
acest lucru, trebuie să înlocuiți caracterul c cu k și s cu z.

56
Machine Translated by Google

Capitolul 3 Lucrul cu șiruri

Înainte de a putea folosi translate, totuși, trebuie să creați un tabel de traducere. Acest tabel de traducere
conține informații despre ce puncte de cod Unicode ar trebui traduse în care. Construiți un astfel de tabel folosind metoda
maketrans pe tipul de șir str însuși. Metoda ia două argumente: două șiruri de caractere de lungime egală, unde fiecare
caracter din primul șir ar trebui înlocuit cu caracterul din aceeași poziție în al doilea șir.3 În cazul exemplului nostru simplu,
codul ar arăta ca următorul :

>>> tabel = str.maketrans('cs', 'kz')

Putem arunca o privire în tabel dacă dorim, deși tot ce vom vedea este o mapare între punctele de cod Unicode.

>>> tabel
{115: 122, 99: 107}

Odată ce aveți un tabel de traducere, îl puteți folosi ca argument pentru metoda translate.

>>> 'acesta este un test incredibil'.translate(table) 'thiz iz an


inkredible tezt'

Un al treilea argument opțional poate fi furnizat pentru maketrans, specificând literele care ar trebui șterse. Dacă doriți
să emulați un german care vorbește rapid, de exemplu, puteți șterge toate spațiile.

>>> table = str.maketrans('cs', 'kz', ' ') >>> 'acesta este un


test incredibil'.translate(table) 'thizizaninkredibletezt'

Vezi și: înlocui, coborâ.

Este șirul meu...


Există o mulțime de metode de șir care încep cu is, cum ar fi isspace, isdigit sau isupper, care determină dacă șirul dvs. are
anumite proprietăți (cum ar fi toate spațiile albe, cifrele sau majuscule), caz în care metodele returnează True. În caz contrar,
desigur, revin False.

În Anexa B: isalnum, isalpha, isdecimal, isdigit, isidentifier, islower, isnumeric, isprintable, isspace, istitle, isupper.

Un rezumat rapid
În acest capitol, ați văzut două moduri importante de a lucra cu șiruri.

Formatarea șirurilor: operatorul modulo (%) poate fi utilizat pentru a îmbina valorile într-un șir
care conține steaguri de conversie, cum ar fi %s. Puteți utiliza acest lucru pentru a formata valori în
mai multe moduri, inclusiv justificarea la dreapta sau la stânga, setarea unei anumite lățimi și precizie
a câmpului, adăugarea unui semn (plus sau minus) sau completarea la stânga cu zerouri.

Metode cu șiruri: șirurile au o multitudine de metode. Unele dintre ele sunt extrem de utile
(cum ar fi split and join), în timp ce altele sunt folosite mai rar (cum ar fi istitle sau capitalize).

3
De asemenea, puteți furniza un dicționar, despre care veți afla în capitolul următor, maparea caracterelor la alte caractere sau
la Niciunul, dacă urmează să fie șterse.

57
Machine Translated by Google

Capitolul 3 Lucrul cu șiruri

Funcții noi în acest capitol


Func ie Descriere
string.capwords(s[, sep]) Împarte s cu split (folosind sep), scrie cu majuscule elementele și unește cu
un singur spațiu

ascii(obj) Construiește o reprezentare ASCII a obiectului dat

Ce acum?
Listele, șirurile și dicționarele sunt trei dintre cele mai importante tipuri de date din Python. Ați văzut liste și șiruri,
așa că ghiciți ce urmează? În următorul capitol, ne uităm la modul în care dicționarele acceptă nu numai indici
întregi, ci și alte tipuri de chei (cum ar fi șiruri sau tupluri). Au și câteva metode, deși nu atât de multe ca șirurile.

58
Machine Translated by Google

CAPITOLUL 4

Dicționare: Când Indicii


Nu va face

Ați văzut că listele sunt utile atunci când doriți să grupați valori într-o structură și să faceți referire la fiecare valoare după
număr. În acest capitol, veți afla despre o structură de date în care vă puteți referi la fiecare valoare după nume. Acest tip de
structură se numește mapare. Singurul tip de mapare încorporat în Python este dicționarul. Valorile dintr-un dicționar nu au
nicio ordine anume, dar sunt stocate sub o cheie, care poate fi un număr, un șir sau chiar un tuplu.

Dic ionar Uses


Dicționarul de nume ar trebui să vă ofere un indiciu despre scopul acestei structuri. O carte obișnuită este făcută pentru a
fi citită de la început până la sfârșit. Dacă doriți, îl puteți deschide rapid pe orice pagină dată. Aceasta este un pic ca o listă
Python. Pe de altă parte, dicționarele - atât cele reale, cât și echivalentul lor Python - sunt construite astfel încât să puteți
căuta cu ușurință un anumit cuvânt (cheie) pentru a-i găsi definiția (valoarea).
Un dicționar este mai potrivit decât o listă în multe situații. Iată câteva exemple de utilizări ale dicționarelor Python:

• Reprezentând starea unei table de joc, fiecare tastă fiind un tuplu de coordonate

• Stocarea timpilor de modificare a fișierelor, cu numele fișierelor ca chei

•Un telefon digital/agendă de adrese

Să presupunem că ai o listă de oameni.

>>> nume = ['Alice', 'Beth', 'Cecil', 'Dee-Dee', 'Earl']

Ce se întâmplă dacă ai vrea să creezi o mică bază de date în care să poți stoca numerele de telefon ale acestor
oameni - cum ai face asta? O modalitate ar fi să faci o altă listă. Să presupunem că stocați doar extensiile lor din patru cifre.
Atunci ai obține ceva de genul acesta:

>>> numere = ['2341', '9102', '3158', '0142', '5551']

După ce ați creat aceste liste, puteți căuta numărul de telefon al lui Cecil după cum urmează:

>>> numere[names.index('Cecil')] '3158'

© Magnus Lie Hetland 2017 59


ML Hetland, Beginning Python, DOI 10.1007/978-1-4842-0028-5_4
Machine Translated by Google

Capitolul 4 Dicționare: când indicii nu vor funcționa

Funcționează, dar este puțin practic. Ceea ce ai vrea cu adevărat să faci este ceva de genul următor:

>>> agenda telefonică['Cecil']


'3158'

Ghici ce? Dacă agenda telefonică este un dicționar, puteți face exact asta.

Crearea și utilizarea dicționarelor


Dicționarele sunt scrise astfel:

agenda telefonică = {'Alice': '2341', 'Beth': '9102', 'Cecil': '3258'}

Dicționarele constau din perechi (numite articole) de chei și valorile lor corespunzătoare . În acest exemplu, numele sunt cheile, iar
numerele de telefon sunt valorile. Fiecare cheie este separată de valoarea sa prin două puncte (:), elementele sunt separate prin virgule,
iar totul este închis între acolade. Un dicționar gol (fără elemente) este scris doar cu două acolade, astfel: {}.

Notă Cheile sunt unice într-un dicționar (și orice alt tip de mapare). Valorile nu trebuie să fie unice
într-un dicționar.

Funcția dict
Puteți utiliza funcția dict1 pentru a construi dicționare din alte mapări (de exemplu, alte dicționare) sau din secvențe de
perechi (cheie, valoare).

>>> articole = [('nume', 'Gumby'), ('varsta', 42)] >>> d =


dict(articole) >>> d {'varsta': 42, 'nume': 'Gumby '} >>> d['nume']

„Gumby”

Poate fi folosit și cu argumente de cuvinte cheie, după cum urmează:

>>> d = dict(nume='Gumby', varsta=42) >>> d


{'varsta': 42, 'nume': 'Gumby'}

Deși aceasta este probabil cea mai utilă aplicație a dict, o puteți folosi și cu un argument de mapare pentru a crea un dicționar cu
aceleași elemente ca și maparea. (Dacă este folosit fără argumente, returnează un nou dicționar gol, la fel ca și alte funcții similare,
cum ar fi list, tuple și str.) Dacă cealaltă mapare este un dicționar (care este, până la urmă, singurul tip de mapare încorporat ), puteți
utiliza în schimb metoda de copiere a dicționarului, așa cum este descris mai târziu în acest capitol.

1 Funcția dict nu este deloc o funcție. Este o clasă, la fel ca list, tuple și str.

60
Machine Translated by Google

Capitolul 4 Dicționare: când indicii nu vor funcționa

Dic ionar de bază Operations


Comportamentul de bază al unui dicționar îl oglindește în multe feluri pe cel al unei secvențe.

•len(d) returnează numărul de elemente (perechi cheie-valoare) în d.

•d[k] returnează valoarea asociată cheii k.

•d[k] = v asociază valoarea v cu cheia k.

•del d[k] șterge articolul cu tasta k.

•k în d verifică dacă există un element în d care are cheia k.

Deși dicționarele și listele au câteva caracteristici comune, există câteva distincții importante:

Tipuri de chei: cheile de dicționar nu trebuie să fie neapărat numere întregi (deși ar putea fi).
Ele pot fi orice tip imuabil, cum ar fi numere în virgulă mobilă (reale), șiruri de caractere sau
tupluri.

Adăugarea automată: Puteți aloca o valoare unei chei, chiar dacă acea cheie nu este în
dicționar pentru început; în acest caz, va fi creat un nou articol. Nu puteți atribui o valoare
unui index în afara intervalului listei (fără a utiliza append sau ceva de genul acesta).

Apartenența: expresia k în d (unde d este un dicționar) caută o cheie, nu o valoare. Expresia


v în l, pe de altă parte (unde l este o listă) caută o valoare, nu un index. Acest lucru poate
părea puțin inconsecvent, dar este de fapt destul de natural când te obișnuiești. La urma
urmei, dacă dicționarul are cheia dată, verificarea valorii corespunzătoare este ușoară.

Sfat Verificarea apartenenței cheie într-un dicționar este mai eficientă decât verificarea apartenenței la o listă.
Diferența este mai mare cu cât structurile de date sunt mai mari.

Primul punct – că cheile pot fi de orice tip imuabil – este principalul punct forte al dicționarelor. Al doilea punct este, de
asemenea, important. Uită-te la diferența aici:

>>> x = [] >>>
x[42] = 'Foobar'
Traceback (cel mai recent apel ultimul): fișierul
„<stdin>”, linia 1, în ?
IndexError: indexul de atribuire a listei în afara intervalului >>>
x = {} >>> x[42] = „Foobar”

>>> x
{42: „Foobar”}

În primul rând, încerc să atribui șirul „Foobar” la poziția 42 într-o listă goală - în mod clar imposibil, deoarece acea poziție
nu există. Pentru a face acest lucru posibil, ar trebui să inițializez x cu [Niciun] * 43 sau ceva, mai degrabă decât[].
simplu pur și
Următoarea încercare, însă, funcționează perfect. Aici atribui „Foobar” tastei 42 a unui dicționar gol. Puteți vedea că aici nu
este nicio problemă. Un articol nou este pur și simplu adăugat în dicționar și sunt în afaceri.

61
Machine Translated by Google

Capitolul 4 Dicționare: când indicii nu vor funcționa

Lista 4-1 arată codul pentru exemplul de agenda telefonică.

Lista 4-1. Dic ionar Exemplu

# O bază de date simplă

# Un dicționar cu nume de persoane ca chei. Fiecare persoană este reprezentată ca # un


alt dicționar cu tastele „telefon” și „adresă” care se referă la numărul de telefon și, respectiv, adresa.
oameni = {

„Alice”:
{ „phone”: „2341”,
„addr”: „Foo drive 23”
},

„Beth”:
{ „telefon”: „9102”,
„adresă”: „Strada barului 42”
},

„Cecil”:
{ „phone”: „3158”,
„adr”: „Baz Avenue 90”
}

# Etichete descriptive pentru numărul de telefon și adresa. Acestea vor fi folosite # la


tipărirea rezultatului. labels = { 'telefon': 'număr de telefon', 'adresă': 'adresă'

nume = input('Nume: ')

# Căutăm un număr de telefon sau o adresă? cerere =


input('Număr de telefon (p) sau adresa (a)? ')

# Folosiți cheia corectă: if


request == 'p': key = 'phone' if request
== 'a': key = 'adr'

# Încercați să imprimați informații numai dacă numele este o cheie validă în


# dicționarul nostru: dacă numele în persoane: print(„{}’s {} este
{}.”.format(nume, etichete[cheie], persoane[nume) ][cheie]))

62
Machine Translated by Google

Capitolul 4 Dicționare: când indicii nu vor funcționa

Iată un exemplu de rulare a programului:

Nume: Beth
Număr de telefon (p) sau adresa (a)? p Numărul
de telefon al lui Beth este 9102.

Formatarea șirurilor de caractere cu dicționare În capitolul 3, ați

văzut cum puteți utiliza formatarea șirurilor de caractere pentru a formata valorile furnizate ca argumente individuale
(denumite sau nenumite) pentru metoda de formatare. Uneori, colectarea unui set de valori numite sub forma unui dicționar
poate ușura lucrurile. De exemplu, dicționarul poate conține tot felul de informații, iar șirul dvs. de format va alege doar
ceea ce are nevoie. Va trebui să specificați că furnizați o mapare, utilizând format_map.

>>> agenda
telefonică {'Beth': '9102', 'Alice': '2341', 'Cecil': '3258'}
>>> „Numărul de telefon al lui Cecil este {Cecil}.”.format_map(phonebook)
„Numărul de telefon al lui Cecil este 3258”.

Când utilizați astfel de dicționare, puteți avea orice număr de specificatori de conversie, atâta timp cât toate cheile date sunt
găsite în dicționar. Acest tip de formatare a șirurilor poate fi foarte utilă în sistemele de șabloane (în acest caz folosind HTML).

>>> template = '''<html> ...


<head><title>{title}</title></head> ... <body> ...
<h1>{title}</h1> ... <p>{text}</p> ... </body>''' >>>
data = {'title': 'Pagina mea de pornire', 'text': 'Bine ați
venit pe pagina mea de pornire! '} >>>
print(template.format_map(data)) <html>
<head><title>Pagina mea de pornire</title></head> <body> <h1>Pagina mea de pornire</h1> <p>
Bun venit pe pagina mea de pornire!</p> </body>

Dic ionar Methods


La fel ca și celelalte tipuri încorporate, dicționarele au metode. Deși aceste metode pot fi foarte utile, probabil că nu veți
avea nevoie de ele la fel de des ca metodele listă și șir. Poate doriți să parcurgeți mai întâi această secțiune pentru a vă face o
idee despre metodele disponibile și apoi să reveniți mai târziu dacă trebuie să aflați exact cum funcționează o anumită metodă.

63
Machine Translated by Google

Capitolul 4 Dicționare: când indicii nu vor funcționa

clar
Metoda clear elimină toate elementele din dicționar. Aceasta este o operație pe loc (cum ar fi list.sort), deci nu returnează nimic
(sau, mai degrabă, Niciunul).

>>> d = {} >>>
d['nume'] = 'Gumby' >>>
d['varsta'] = 42 >>> d {'varsta':
42, 'nume': 'Gumby'} >>>
valoare_returnată = d.clear() >>> d {}
>>> print(valoare_returnată)

Nici unul

De ce este util acest lucru? Să luăm în considerare două scenarii. Iată primul:

>>> x = {} >>>
y = x >>>
x['cheie'] = 'valoare' >>> y
{'cheie': 'valoare'} >>> x = {}
>>> x = {} {'cheie': 'valoare'}

Și iată al doilea scenariu:

>>> x = {} >>>
y = x >>>
x['key'] = 'value' >>> y {'key':
'value'} >>> x.clear() >>> y {}

În ambele scenarii, x și y se referă inițial la același dicționar. În primul scenariu, „golesc” x, atribuindu-i un dicționar nou,
gol. Asta nu afectează deloc y, care se referă în continuare la dicționarul original.
Acesta poate fi comportamentul pe care îl doriți, dar dacă doriți cu adevărat să eliminați toate elementele
dicționarului original , trebuie să utilizați clear. După cum puteți vedea în al doilea scenariu, y este, de asemenea, gol după aceea.

copy

Metoda copy returnează un nou dicționar cu aceleași perechi cheie-valoare (o copie superficială, deoarece valorile în sine
sunt aceleași, nu copii).

>>> x = {'nume utilizator': 'admin', 'ma ini': ['foo', 'bar', 'baz']} >>> y = x.copy() >>>
y['nume utilizator' ] = 'mlh'

64
Machine Translated by Google

Capitolul 4 Dicționare: când indicii nu vor funcționa

>>> y['machines'].remove('bar') >>> y


{'username': 'mlh', 'machines': ['foo', 'baz']}

>>> x
{'nume utilizator': 'admin', 'ma ini': ['foo', 'baz']}

După cum puteți vedea, atunci când înlocuiți o valoare în copie, originalul nu este afectat. Cu toate acestea, dacă
modificați o valoare (în loc, fără a o înlocui), originalul este schimbat și pentru că aceeași valoare este stocată acolo (cum ar
fi lista de „mașini” din acest exemplu).
O modalitate de a evita această problemă este să faceți o copie profundă, să copiați valorile, orice valori pe care le conțin și
asa mai departe, de asemenea. Reușiți acest lucru folosind funcția deepcopy din modulul de copiere.

>>> din copie import deepcopy >>> d =


{} >>> d['nume'] = ['Alfred', 'Bertrand']
>>> c = d.copy() >>> dc = deepcopy( d) >>>
d['nume'].append('Clive')

>>> c
{'nume': ['Alfred', 'Bertrand', 'Clive']} >>> dc {'nume':
['Alfred', 'Bertrand']}

de la chei

Metoda fromkeys creează un nou dicționar cu cheile date, fiecare cu o valoare implicită corespunzătoare, None.

>>> {}.fromkeys(['nume', 'vârsta']) {'varsta':


Niciuna, 'nume': Niciuna}

Acest exemplu construiește mai întâi un dicționar gol și apoi apelează metoda fromkeys pentru a crea un alt dicționar
- o strategie oarecum redundantă. În schimb, puteți apela metoda direct pe dict, care (așa cum am menționat anterior)
este tipul tuturor dicționarelor. (Conceptul de tipuri și clase este discutat mai amănunțit în capitolul 7.)

>>> dict.fromkeys(['nume', 'vârsta']) {'varsta':


Niciuna, 'nume': Niciuna}

Dacă nu doriți să utilizați None ca valoare implicită, puteți furniza propria dvs. implicită.

>>> dict.fromkeys(['nume', 'vârsta'], '(necunoscut)') {'varsta':


'(necunoscut)', 'nume': '(necunoscut)'}

obține

Metoda get este o modalitate iertatoare de a accesa articolele din dicționar. De obicei, atunci când încercați să accesați un
articol care nu este prezent în dicționar, lucrurile merg foarte prost.

>>> d = {}
>>> print(d['nume'])

65
Machine Translated by Google

Capitolul 4 Dicționare: când indicii nu vor funcționa

Traceback (cel mai recent apel ultimul):


fișierul „<stdin>”, linia 1, în ?
KeyError: „nume”

Nu este cazul cu get.

>>> print(d.get('nume'))
Nici unul

După cum puteți vedea, atunci când utilizați get pentru a accesa o cheie inexistentă, nu există nicio excepție. În schimb,
obțineți valoarea None. Puteți furniza propria dvs. valoare „implicit”, care este apoi utilizată în loc de Niciunul.

>>> d.get('nume', 'N/A')


'N / A'

Dacă cheia este acolo, obțineți funcționează ca o căutare obișnuită în dicționar.

>>> d['nume'] = 'Eric' >>>


d.get('nume')
„Eric”

Lista 4-2 arată o versiune modificată a programului din Lista 4-1, care folosește metoda get pentru a accesa intrările
„bază de date”.

Lista 4-2. Dic ionar Metodă Exemplu

# O bază de date simplă folosind get()

# Inserați aici baza de date (persoane) din Lista 4-1 .

labels =
{ 'telefon': 'număr de telefon',
'adresă': 'adresă'
}

nume = input('Nume: ')

# Căutăm un număr de telefon sau o adresă? cerere = input('Număr


de telefon (p) sau adresa (a)? ')

# Folosiți cheia corectă: cheie


= cerere # În cazul în care cererea nu este nici „p”, nici „a” dacă cerere ==
„p”: cheie = „telefon” dacă cerere == „a”: cheie = „adresă”

# Utilizați get pentru a furniza valori implicite:


person = people.get(name, {}) label =
labels.get(key, key) result = person.get(key,
'not available')

print(„{} lui {} este {}.”.format(nume, etichetă, rezultat))

66
Machine Translated by Google

Capitolul 4 Dicționare: când indicii nu vor funcționa

Urmează un exemplu de rulare a acestui program. Observați cum flexibilitatea adăugată a get permite programului să ofere
un răspuns util, chiar dacă utilizatorul introduce valori pentru care nu eram pregătiți.

Nume: Gumby
Număr de telefon (p) sau adresa (a)? medie de bataie Media de
bataie a lui Gumby nu este disponibilă.

articole

Metoda itemilor returnează toate articolele din dicționar ca o listă de articole în care fiecare articol are forma (cheie,
valoare). Articolele nu sunt returnate într-o anumită ordine.

>>> d = {'title': 'Site Web Python', 'url': 'http://www.python.org', 'spam': 0} >>> d.items() dict_items([( 'url',
'http://www.python.org'), ('spam', 0), ('titlu', 'Site Web Python')])

Valoarea returnată este de un tip special numit vizualizare dicționar. Vizualizările dicționarului pot fi folosite pentru iterare
(vezi Capitolul 5 pentru mai multe despre asta). În plus, puteți determina durata lor și puteți verifica calitatea de membru.

>>> it = d.items() >>>


len(it) 3

>>> ('spam', 0) în el
Adevărat

Un lucru util despre vizualizări este că nu copiază nimic; ele reflectă întotdeauna dicționarul de bază, chiar dacă îl modificați.

>>> d['spam'] = 1 >>>


('spam', 0) în el
Fals
>>> d['spam'] = 0 >>>
('spam', 0) în el
Adevărat

Dacă, totuși, preferați să copiați elementele într-o listă (ceea ce s-a întâmplat când ați folosit elemente în versiuni mai vechi
de Python), puteți oricând să faceți asta singur.

>>> list(d.items()) [('spam',


0), ('titlu', 'Site Web Python'), ('url', 'http://www.python.org')]

chei
Metoda cheilor returnează o vizualizare a cheilor din dicționar.

pop
Metoda pop poate fi folosită pentru a obține valoarea corespunzătoare unei chei date și apoi pentru a elimina perechea cheie-
valoare din dicționar.

67
Machine Translated by Google

Capitolul 4 Dicționare: când indicii nu vor funcționa

>>> d = {'x': 1, 'y': 2} >>>


d.pop('x') 1 >>> d {'y': 2}

popitem
Metoda popitem este similară cu list.pop, care apare de pe ultimul element al unei liste. Spre deosebire de
list.pop, totuși, popitem apare un element arbitrar, deoarece dicționarele nu au un „ultimul element” sau orice
ordine. Acest lucru poate fi foarte util dacă doriți să eliminați și să procesați elementele unul câte unul într-un mod
eficient (fără a prelua mai întâi o listă a cheilor).

>>> d = {'url': 'http://www.python.org', 'spam': 0, 'title': 'Site Web Python'} >>> d.popitem() ('url' , 'http://
www.python.org') >>> d {'spam': 0, 'title': 'Site Web Python'}

Deși popitem este similar cu metoda list pop, nu există un echivalent în dicționar pentru append (care adaugă un
element la sfârșitul unei liste). Pentru că dicționarele nu au ordine, o astfel de metodă nu ar avea niciun sens.

Sfat Dacă doriți ca metoda popitem să urmeze o ordonare previzibilă, aruncați o privire la clasa
OrderedDict din modulul de colecții .

setdefault
Metoda setdefault este oarecum similară cu get, prin faptul că preia o valoare asociată cu o anumită cheie. În plus față
de funcționalitatea get, setdefault setează valoarea corespunzătoare cheii date dacă nu este deja în dicționar.

>>> d = {}
>>> d.setdefault('nume', 'N/A')
'N/A'
>>> d
{'nume': 'N/A'} >>>
d['nume'] = 'Gumby' >>>
d.setdefault('nume', 'N/A')
„Gumby”
>>> d
{„nume”: „Gumby”}

După cum puteți vedea, atunci când cheia lipsește, setdefault returnează valoarea implicită și actualizează dicționarul în
consecință. Dacă cheia este prezentă, valoarea acesteia este returnată, iar dicționarul rămâne neschimbat. Valoarea implicită este
opțională, ca și în cazul get; dacă este omis, nu este folosit niciunul.

68
Machine Translated by Google

Capitolul 4 Dicționare: când indicii nu vor funcționa

>>> d = {}
>>> print(d.setdefault('nume'))

Niciuna
>>> d {'nume': Niciuna}

Sfat Dacă doriți o implicită globală pentru întregul dicționar, verificați clasa defaultdict din
modulul de colecții .

Actualizați

Metoda de actualizare actualizează un dicționar cu elementele altuia.

>>> d =
... { 'title': 'Site Web Python', 'url':
... 'http://www.python.org', 'changed':
... 'Mar 14 22:09:15 MET 2016'
... }
>>> x = {'title': 'Python Language Website'} >>>
d.update(x) >>> d {'url': 'http://www.python.org',
„schimbat”: „14 martie 22:09:15 MET 2016”, „titlu”:
„Site web în limbajul Python”}

Elementele din dicționarul furnizat sunt adăugate celui vechi, înlocuind orice articole de acolo cu
aceleași chei.
Metoda de actualizare poate fi apelată în același mod ca și funcția dict (sau constructorul de tip), așa cum sa
discutat mai devreme în acest capitol. Aceasta înseamnă că actualizarea poate fi apelată cu o mapare, o secvență (sau alt
obiect iterabil) de perechi (cheie, valoare) sau argumente de cuvinte cheie.

valorile

Metoda values returnează o vizualizare a dicționarului a valorilor din dicționar. Spre deosebire de chei, vizualizarea
returnată de valori poate conține duplicate.

>>> d = {}
>>> d[1] = 1
>>> d[2] = 2
>>> d[3] = 3
>>> d[4] = 1
>>> d.valori( )
dict_values([1, 2, 3, 1])

69
Machine Translated by Google

Capitolul 4 Dicționare: când indicii nu vor funcționa

Un rezumat rapid
În acest capitol, ați aflat despre următoarele:

Mapări: o mapare vă permite să etichetați elementele sale cu orice obiect imuabil, cele
mai obișnuite tipuri fiind șiruri și tupluri. Singurul tip de mapare încorporat în Python este
dicționarul.

Formatarea șirurilor cu dicționare: puteți aplica operația de formatare a șirurilor la


dicționare folosind format_map, în loc să utilizați argumente numite cu format.

Metode de dicționar: dicționarele au destul de multe metode, care sunt numite în același mod
ca și metodele listă și șir.

Funcții noi în acest capitol


Func ie Descriere
dict(seq) Creează dicționar din perechi (cheie, valoare) (sau o mapare sau argumente ale cuvintelor cheie)

Ce acum?
Acum știți multe despre tipurile de date de bază ale Python și despre cum să le utilizați pentru a forma expresii. După cum
vă amintiți din Capitolul 1, programele de calculator au un alt ingredient important – afirmațiile. Sunt tratate în detaliu în
capitolul următor.

70
Machine Translated by Google

CAPITOLUL 5

Condiționale, bucle și unele


Alte declarații

Până acum, sunt sigur că devii puțin nerăbdător. În regulă, toate aceste tipuri de date sunt simple, dar nu poți face mare lucru
cu ele, nu-i așa?
Să creștem puțin ritmul. Am întâlnit deja câteva tipuri de declarații (instrucțiuni tipărite,
instrucțiuni de import și atribuiri). Să aruncăm mai întâi o privire la câteva modalități de a le folosi înainte de a ne
scufunda în lumea condiționalelor și a buclelor. Apoi vom vedea cum funcționează listele de înțelegere aproape ca și condițiile
și buclele, chiar dacă sunt expresii și, în final, vom arunca o privire la pass, del și exec.

Mai multe despre imprimare și import


Pe măsură ce aflați mai multe despre Python, este posibil să observați că unele aspecte ale Python pe care credeați că le
cunoașteți au caracteristici ascunse care așteaptă să vă surprindă plăcut. Să aruncăm o privire la câteva astfel de caracteristici
frumoase în imprimare și import. Deși tipărirea este într-adevăr o funcție, obișnuia să fie un tip de declarație propriu, motiv pentru
care o discut aici.

Sfat Pentru multe aplica ii, înregistrarea în jurnal (folosind modulul de înregistrare) va fi mai potrivită decât prin imprimare .

Vezi capitolul 19 pentru mai multe detalii.

Imprimarea mai multor argumente


Ați văzut cum imprimarea poate fi folosită pentru a imprima o expresie, care este fie un șir, fie convertită automat într-
una. Dar, de fapt, puteți imprima mai mult de o expresie, atâta timp cât le separați
virgule:

>>> print('Vârsta:', 42)


Vârsta: 42

© Magnus Lie Hetland 2017 71


ML Hetland, Beginning Python, DOI 10.1007/978-1-4842-0028-5_5
Machine Translated by Google

Capitolul 5 Condiționale, bucle și alte instrucțiuni

După cum puteți vedea, un caracter spațiu este inserat între fiecare argument. Acest comportament poate fi foarte util dacă
doriți să combinați text și valori variabile fără a utiliza întreaga putere a formatării șirurilor.

>>> name = 'Gumby'


>>> salutation = 'Mr.' >>>
salut = „Bună ziua,” >>>
print(felicitare, salutare, nume)
Bună, domnule Gumby

Dacă șirul de salut nu ar avea virgulă, cum ați obține virgula în rezultat? Nu ai putea folosi pur și simplu

tipăriți (felicitare, ',', salutare, nume)

pentru că asta ar introduce un spațiu înainte de virgulă. O soluție ar fi următoarea:

print(felicitare + ',', salut, nume)

care adaugă pur și simplu virgula la salut. Puteți specifica un separator personalizat, dacă doriți:

>>> print("eu", "doresc", "să", "înregistrați", "a", "reclamație", sep="_")


Doresc_să_înregistrez_o_plângere

De asemenea, puteți specifica un șir de sfârșit personalizat, pentru a înlocui linia nouă implicită. De exemplu, dacă furnizați
un șir gol, mai târziu puteți continua imprimarea pe aceeași linie.

print('Bună,', sfâr it='')


print('lume!')

Acest program imprimă Bună, lume!. 1

Importarea a ceva ca Altceva


De obicei, când importați ceva dintr-un modul, fie utilizați

importa un modul

sau utilizați

din somemodule import somefunction

sau

dintr-un modul, importați o funcție, o altă funcție, o altă funcție

sau

*
de la importul unui modul

1 Aceasta va funcționa numai într-un script, și nu într-o sesiune interactivă Python. În sesiunea interactivă, fiecare instrucțiune va fi
executată (și va tipări conținutul) separat.

72
Machine Translated by Google

Capitolul 5 Condiționale, bucle și alte instrucțiuni

A patra versiune ar trebui folosită numai atunci când sunteți sigur că doriți să importați totul din modulul dat. Dar
dacă aveți două module, fiecare conținând o funcție numită deschis, de exemplu, ce faceți atunci? Puteți importa pur
și simplu modulele folosind primul formular și apoi utilizați funcțiile după cum urmează:

modul1.deschis(...)
modul2.deschis(...)

Dar există o altă opțiune: puteți adăuga o clauză as la sfârșit și puteți furniza numele pe care doriți să îl utilizați, fie
pentru întregul modul:

>>> import matematică ca


foobar >>> foobar.sqrt(4)
2.0

sau pentru funcția dată:

>>> din matematică import sqrt ca foobar


>>> foobar(4)
2.0

Pentru funcțiile deschise, puteți utiliza următoarele:

din module1 import deschis ca open1 din


module2 import deschis ca open2

Notă Unele module, cum ar fi os.path, sunt aranjate ierarhic (unul în interiorul celuilalt). Pentru mai multe despre
structura modulului, consultați secțiunea despre pachete din Capitolul 10.

Magia sarcinii
Declarația umilă a misiunii are și câteva trucuri în mânecă.

Dezambalarea secvenței Ați văzut

destul de multe exemple de atribuiri, atât pentru variabile, cât și pentru părți ale structurilor de date (cum ar fi
pozițiile și secțiunile dintr-o listă sau sloturile dintr-un dicționar), dar există mai multe. Puteți efectua mai multe
sarcini diferite simultan.

>>> x, y, z = 1, 2, 3 >>>
print(x, y, z) 1 2 3

Nu sună util? Ei bine, îl puteți folosi pentru a comuta conținutul a două (sau mai multe) variabile.

>>> x, y = y, x >>>
print(x, y, z) 2 1 3

73
Machine Translated by Google

Capitolul 5 Condiționale, bucle și alte instrucțiuni

De fapt, ceea ce fac aici se numește despachetarea secvenței (sau despachetarea iterabilă). Am o secvență (sau un obiect
iterabil arbitrar) de valori și o despachetez într-o secvență de variabile. Lasă-mă să fiu mai explicit.

>>> valori = 1, 2, 3 >>>


valori (1, 2, 3) >>> x, y, z =
valori

>>> x
1

Acest lucru este util în special atunci când o funcție sau o metodă returnează un tuplu (sau altă secvență sau obiect iterabil).
Să presupunem că doriți să preluați (și să eliminați) o pereche cheie-valoare arbitrară dintr-un dicționar. Puteți utiliza
apoi metoda popitem, care face exact asta, returnând perechea ca tuplu. Apoi puteți despacheta tuplul returnat direct în
două variabile.

>>> scoundrel = {'nume': 'Robin', 'prietena': 'Marion'} >>> cheie, valoare =


scoundrel.popitem() >>> cheie 'prietena' >>> valoare 'Marion'

Acest lucru permite funcțiilor să returneze mai mult de o valoare, ambalate ca un tuplu, ușor accesibile printr-o singură
atribuire. Secvența pe care o despachetați trebuie să aibă exact atâtea articole câte ținte enumerate în stânga semnului =;
în caz contrar, Python ridică o excepție atunci când atribuirea este efectuată.

>>> x, y, z = 1, 2
Traceback (cel mai recent apel ultimul):
Fișierul „<stdin>”, linia 1, în <modul>
ValueError: aveți nevoie de mai mult de 2 valori pentru a
despacheta >>> x, y, z = 1, 2, 3, 4
Traceback (cel mai recent apel ultimul):
Fișierul „<stdin>”, linia 1, în <modul>
ValueError: prea multe valori pentru a despacheta

În loc să vă asigurați că numărul de valori se potrivește exact, puteți aduna toate valorile superflue folosind operatorul
stea (*). De exemplu:

>>> a, b, * rest = [1, 2, 3, 4] >>> rest [3,


4]

Puteți plasa această variabilă marcată cu stea și în alte poziții.

>>> nume = "Albus Percival Wulfric Brian Dumbledore" >>> primul,


*middle, last = name.split() >>> mijloc ['Percival', 'Wulfric', 'Brian']

74
Machine Translated by Google

Capitolul 5 Condiționale, bucle și alte instrucțiuni

Partea dreaptă a sarcinii poate fi orice fel de secvență, dar variabila marcată cu stea va ajunge întotdeauna să
conțină o listă. Acest lucru este adevărat chiar dacă numărul de valori se potrivește exact.

>>> a, *b, c = "abc" >>>


a, b, c ('a', ['b'], 'c')

Același tip de adunare poate fi folosit și în listele de argumente ale funcției (vezi capitolul 6).

Atribuții înlănțuite Atribuțiile

înlănțuite sunt folosite ca o comandă rapidă atunci când doriți să legați mai multe variabile la aceeași valoare. Acest
lucru poate părea un pic ca atribuțiile simultane din secțiunea anterioară, cu excepția faptului că aici aveți de-a face
cu o singură valoare:

x = y = o anumită funcție()

care este la fel ca acesta:

y = o anumită funcție()
x=y

Rețineți că afirmațiile precedente pot să nu fie aceleași cu

x = somefunction() y =
somefunction()

Pentru mai multe despre acest lucru, consultați secțiunea despre operatorul de identitate (este) mai târziu în acest capitol.

Atribuții mărite În loc să scrieți x = x + 1,

puteți pune operatorul expresiei (în acest caz +) înaintea operatorului de atribuire (=) și scrieți x += 1. Aceasta se
numește o atribuire augmentată și funcționează cu toate operatorii standard, cum ar fi *, /, % și așa mai departe.

>>> x = 2
>>> x += 1
>>> x *= 2
>>> x
6

Funcționează și cu alte tipuri de date (atâta timp cât operatorul binar însuși lucrează cu acele tipuri de date).

>>> fnord = 'foo' >>>


fnord += 'bar' >>>
fnord *= 2 >>> fnord
'foobarfoobar'

Atribuțiile mărite vă pot face codul mai compact și concis și, în multe cazuri, mai ușor de citit.

75
Machine Translated by Google

Capitolul 5 Condiționale, bucle și alte instrucțiuni

Blocuri: Bucuria indentării


Un bloc nu este cu adevărat un tip de declarație, ci ceva de care veți avea nevoie atunci când abordați următoarele două
secțiuni.
Un bloc este un grup de instrucțiuni care pot fi executate dacă o condiție este adevărată (instrucțiuni condiționate),
executate de mai multe ori (bucle) și așa mai departe. Un bloc este creat prin indentarea unei părți a codului dvs., adică plasând
spații în fața acestuia.

Notă Puteți utiliza și caracterele tabulatoare pentru a indenta blocurile. Python interpretează o filă ca deplasare la

următoarea oprire de tabulație, cu o oprire de tabulație la fiecare opt spații, dar stilul standard și preferabil este să folosească
numai spații, nu file și, în special, patru spații pentru fiecare nivel de indentare.

Fiecare linie dintr-un bloc trebuie să fie indentată cu aceeași sumă. Următorul este pseudocod (nu real
Cod Python) care arată cum funcționează indentarea:

aceasta este o linie


aceasta este o alta linie:
acesta este un alt bloc
continuand acelasi bloc ultima
linie a acestui bloc
Uf, acolo am scăpat din blocul interior

În multe limbi, un cuvânt sau caracter special (de exemplu, început sau {) este folosit pentru a începe un bloc, iar altul (cum ar
fi sfâr itul sau }) este folosit pentru a-l termina. În Python, se utilizează două puncte (:) pentru a indica faptul că un bloc este
pe cale să înceapă, iar apoi fiecare linie din acel bloc este indentată (cu aceeași cantitate). Când te întorci la aceeași cantitate de
indentare ca și un bloc care înconjoară, știi că blocul curent s-a încheiat. (Mulți editori de programare și IDE-uri sunt conștienți
de modul în care funcționează această indentare a blocurilor și vă pot ajuta să o faceți corect fără prea mult efort.)

Acum, să aruncăm o privire la ce pot fi folosite aceste blocuri.

Condiții și declarații condiționale


Până acum ați scris programe în care fiecare instrucțiune este executată, una după alta. Este timpul să treceți dincolo de
asta și să lăsați programul să aleagă dacă să execute sau nu un bloc de instrucțiuni.

Deci , pentru asta sunt acele valori booleene

Acum, în sfârșit, veți avea nevoie de acele valori de adevăr (numite și valori booleene , după George Boole, care a făcut o
mulțime de lucruri inteligente despre valorile de adevăr) de care v-ați întâlnit în mod repetat.

Notă Dacă ați fost foarte atent, ați observat bara laterală din Capitolul 1, „Sneak Peek: The if statement”, care descrie
declarația if . Nu l-am prezentat oficial până acum și, după cum veți vedea, este ceva mai mult decât ceea ce v-am spus

până acum.

76
Machine Translated by Google

Capitolul 5 Condiționale, bucle și alte instrucțiuni

Următoarele valori sunt considerate de către interpret ca fiind false atunci când sunt evaluate ca expresie booleană (de
exemplu, ca condiție a unei instrucțiuni if):

""
Fals Nici unul 0 () [] {}

Cu alte cuvinte, valorile standard False și None, zero numeric de toate tipurile (inclusiv float, complex și așa mai
departe), secvențe goale (cum ar fi șiruri goale, tupluri și liste) și mapări goale (cum ar fi dicționare) sunt toate
considerate a fi false. Orice altceva2 este interpretat ca adevărat, inclusiv valoarea specială True. 3 Ați înțeles? Aceasta
înseamnă că fiecare valoare din Python poate fi interpretată ca o valoare de adevăr, ceea ce poate fi puțin confuz la
început, dar poate fi și extrem de util. Și chiar dacă aveți toate aceste valori de adevăr din care să alegeți, valorile
de adevăr „standard” sunt Adevărat și Fals. În unele limbi (cum ar fi C și Python înainte de versiunea 2.3), valorile
standard de adevăr sunt 0 (pentru fals) și 1 (pentru adevărat). De fapt, Adevărat și Fals nu sunt atât de diferite - sunt doar
versiuni glorificate ale lui 0 și 1 care arată diferit, dar acționează la fel.

>>> Adevărat
Adevărat

>>> Fals
Fals
>>> Adevarat == 1
Adevărat

>>> Fals == 0
Adevărat

>>> Adevărat + Fals + 42


43

Deci, acum, dacă vedeți o expresie logică care returnează 1 sau 0 (probabil într-o versiune mai veche de Python), veți ști
că ceea ce înseamnă cu adevărat este adevărat sau fals.
Valorile booleene True și False aparțin tipului bool, care poate fi folosit (la fel ca, de exemplu,
list, str și tuple) pentru a converti alte valori.

>>> bool('cred, deci sunt')

Adevărat >>> bool(42)

Adevărat >>> bool('')


Fals
>>> bool(0)
Fals

Deoarece orice valoare poate fi folosită ca valoare booleană, cel mai probabil vei avea nevoie de o astfel de conversie
explicită (adică Python va converti automat valorile pentru tine).

2 Cel puțin atunci când vorbim despre tipurile încorporate — după cum vedeți în Capitolul 9, puteți influența dacă obiectele pe care le
construiți sunt interpretate ca adevărate sau false.
3 După cum spune veteranul Python Laura Creighton, distincția este într-adevăr mai aproape de ceva versus nimic, mai degrabă decât adevărat
vs fals.

77
Machine Translated by Google

Capitolul 5 Condiționale, bucle și alte instrucțiuni

Notă De i [] i "" sunt ambele false (adică bool([]) == bool("") == False), ele nu sunt egale (adică [] != ""). Același
lucru este valabil și pentru alte obiecte false de diferite tipuri (de exemplu, exemplul posibil mai evident () != Fals).

Execuție condiționată și declarația if


Valorile de adevăr pot fi combinate și vom reveni la cum să facem asta, dar mai întâi să vedem pentru ce le puteți folosi. Încercați
să rulați următorul script:

name = input('Care este numele tău? ') if


name.endswith('Gumby'):
print('Bună ziua, domnule Gumby')

Aceasta este instrucțiunea if, care vă permite să faceți execuție condiționată. Aceasta înseamnă că dacă condiția (expresia
după if, dar înainte de două puncte) este evaluată la adevărat (așa cum a fost definit anterior), următorul bloc (în acest caz, o
singură instrucțiune de tipărire) este executat. Dacă condiția este falsă, atunci blocul nu este executat (dar ai ghicit asta, nu-i
așa?).

Notă În bara laterală „Sneak Peek: The if Statement” din Capitolul 1, declarația a fost scrisă pe o singură linie. Acest
lucru este echivalent cu utilizarea unui bloc cu o singură linie, ca în exemplul precedent.

clauze else
În exemplul din secțiunea anterioară, dacă introduceți un nume care se termină cu „Gumby”, numele metodei. endswith
returnează True, făcând declarația if să intre în bloc și salutul este tipărit. Dacă doriți, puteți adăuga o alternativă, cu clauza else
(numită clauză pentru că nu este cu adevărat o declarație separată, ci doar o parte din instrucțiunea if).

name = input('Care este numele tău?') if


name.endswith('Gumby'):
print('Salut, domnule Gumby') else:
print('Bună, străin')

Aici, dacă primul bloc nu este executat (deoarece condiția este evaluată ca fals), introduceți în schimb al doilea bloc. Acest lucru
arată cu adevărat cât de ușor este să citești codul Python, nu-i așa? Citiți codul cu voce tare (din if) și sună exact ca o propoziție
normală (sau poate nu chiar normală).
Există, de asemenea, o rudă apropiată a instrucțiunii if, numită expresie condiționată. Aceasta este versiunea Python
a operatorului ternar din C. Aceasta este o expresie care folosește if și else pentru a-și determina valoarea:

status = "prieten" dacă name.endswith ("Gumby") else "străin"

Valoarea expresiei este prima valoare furnizată (în acest caz, „prieten”) ori de câte ori condiția (orice vine imediat după
dacă) este adevărată, iar ultima valoare (în acest caz, „străin”) în caz contrar.

78
Machine Translated by Google

Capitolul 5 Condiționale, bucle și alte instrucțiuni

elif clauze
Dacă doriți să verificați mai multe condiții, puteți utiliza elif, care este prescurtarea pentru „altfel dacă”. Este o
combinație între o clauză if și o clauză else — o clauză else cu o condiție.

num = int(input('Introduceți un număr: '))


dacă num > 0:
print('Numarul este pozitiv')
elif num < 0:
print('Numărul este negativ') altfel:

print('Numarul este zero')

Blocuri de cuibărit
Să aruncăm câteva clopote și fluiere. Puteți avea instrucțiuni if în alte blocuri de instrucțiuni if, după cum
urmează:

name = input('Care este numele tău? ') if


name.endswith('Gumby'):
if name.startswith('Mr.'):
print('Bună ziua, domnule
Gumby') elif name.startswith('Doamna'):
print('Bună ziua, doamna
Gumby') else:
print('Bună, Gumby')
altceva:

print('Bună, străin')

Aici, dacă numele se termină cu „Gumby”, verificați și începutul numelui - într-o declarație separată if în interiorul
primului bloc. Observați folosirea lui elif aici. Ultima alternativă (clauza else) nu are nicio condiție - dacă nu este
aleasă nicio altă alternativă, o utilizați pe ultima. Dacă doriți, puteți omite oricare dintre celelalte clauze. Dacă omiteți
clauza interioară else, nume care nu încep nici cu „Mr”. sau „Doamna”. sunt ignorate (presupunând că numele era
„Gumby”). Dacă renunțați la clauza else exterioară, străinii sunt ignorați.

Condiții mai complexe


Asta e tot ce trebuie să știi despre declarațiile if. Acum să revenim la condițiile în sine, pentru că ele sunt partea cu
adevărat interesantă a execuției condiționate.

Operatori de comparație Poate

că cei mai de bază operatori utilizați în condiții sunt operatorii de comparație. Sunt folosite (surpriză, surpriză)
pentru a compara lucrurile. Tabelul 5-1 rezumă operatorii de comparație.

79
Machine Translated by Google

Capitolul 5 Condiționale, bucle și alte instrucțiuni

Tabelul 5-1. Operatorii de comparație Python

Expresie Descriere

x == y x este egal cu y.

x<y x este mai mic decât y.

x>y x este mai mare decât y.

x >= y x este mai mare sau egal cu y.

x <= y x este mai mic sau egal cu y.

x != y x nu este egal cu y.

x este y x și y sunt același obiect.

x nu este y x și y sunt obiecte diferite. x este

x în y un membru al containerului (de exemplu, secvența) y. x

x nu în y nu este membru al containerului (de exemplu, secvența) y.

COMPARAREA TIPURILOR INCOMPATIBILE

În teorie, puteți compara oricare două obiecte x și y pentru dimensiunea relativă (folosind operatori precum < și <=) și
puteți obține o valoare de adevăr. Cu toate acestea, o astfel de comparație are sens numai dacă x și y sunt de aceleași
tipuri sau strâns înrudite (cum ar fi două numere întregi sau un număr întreg și un număr în virgulă mobilă).

La fel cum nu are prea mult sens să adăugați un număr întreg la un șir, a verifica dacă un număr întreg este mai
mic decât un șir pare destul de inutil. În mod ciudat, în versiunile Python anterioare 3, ți se permitea să faci asta.
Chiar dacă utilizați un Python mai vechi, ar trebui să stați departe de astfel de comparații, deoarece rezultatul este
total arbitrar și se poate schimba între fiecare execuție a programului dumneavoastră. În Python 3, compararea
tipurilor incompatibile în acest fel nu mai este permisă.

Comparațiile pot fi înlănțuite în Python, la fel ca și atribuțiile — puteți pune mai mulți operatori de comparație într-un lanț,
cum ar fi: 0 < vârsta < 100.
Unii dintre acești operatori merită o atenție specială și vor fi descriși în secțiunile următoare.

Operatorul de egalitate
Dacă vrei să știi dacă două lucruri sunt egale, folosește operatorul de egalitate, scris ca semn de egalitate dublu, ==.

>>> "foo" == "foo"

Adevărat >>> „foo” == „bar”


Fals

Dubla? De ce nu poți folosi un singur semn de egalitate, așa cum se întâmplă în matematică? Sunt sigur că ești suficient
de deștept ca să-ți dai seama singur, dar hai să încercăm.

>>> "foo" = "foo"


SyntaxError: nu se poate atribui literalului

80
Machine Translated by Google

Capitolul 5 Condiționale, bucle și alte instrucțiuni

Singurul semn de egalitate este operatorul de atribuire, care este folosit pentru a schimba lucrurile, ceea ce nu este ceea ce
vrei să faci când compari lucrurile.

este: Operatorul de identitate


Operatorul este interesant. Se pare că funcționează la fel ca ==, dar nu funcționează.

>>> x = y = [1, 2, 3] >>> z = [1,


2, 3] >>> x == y

Adevărat

>>> x == z

Adevarat >>> x este y

Adevarat >>> x este z


Fals

Până la ultimul exemplu, acest lucru arată bine, dar apoi obțineți acel rezultat ciudat: x nu este z, deși sunt egali. De ce? Pentru
că sunt teste pentru identitate, mai degrabă decât pentru egalitate. Variabilele x și y au fost legate la aceeași listă, în timp ce z este
pur și simplu legat de o altă listă care se întâmplă să conțină aceleași valori în aceeași ordine.
Ele pot fi egale, dar nu sunt același obiect.
Ti se pare nerezonabil? Luați în considerare acest exemplu:

>>> x = [1, 2, 3] >>> y =


[2, 4] >>> x nu este y
Adevărat >>> del x[2]
>>> y[1] = 1 >>>
y.reverse()

În acest exemplu, încep cu două liste diferite, x și y. După cum puteți vedea, x nu este y (doar inversul lui x este y), ceea ce știți
deja. Schimb un pic listele și, deși acum sunt egale, sunt încă două liste separate.

>>> x == y

Adevarat >>> x este y


Fals

Aici, este evident că cele două liste sunt egale, dar nu identice.
Pentru a rezuma, utilizați == pentru a vedea dacă două obiecte sunt egale și pentru a vedea dacă sunt identice (același obiect).

Atenție Evitați utilizarea is cu valori de bază, imuabile, cum ar fi numere și șiruri. Rezultatul este
imprevizibil din cauza modului în care Python gestionează aceste obiecte intern.

81
Machine Translated by Google

Capitolul 5 Condiționale, bucle și alte instrucțiuni

în: Operatorul de membru

Am introdus deja operatorul in (în capitolul 2, în secțiunea „Abonament”). Poate fi folosit în condiții, la fel ca toți ceilalți
operatori de comparație.

name = input('Care este numele tău?') if 's' în


nume: print('Numele tău conține litera "s".')
else: print('Numele tău nu conține litera "s".' )

Comparații de șiruri și secvențe Șirurile sunt


comparate în funcție de ordinea lor atunci când sunt sortate alfabetic.

>>> „alfa” < „beta”


Adevărat

Ordonarea este alfabetică, dar alfabetul este tot Unicode, ordonat după punctele lor de cod.

" " " "


>>> <
Adevărat

De fapt, caracterele sunt sortate după valorile lor ordinale. Valoarea ordinală a unei litere poate fi găsită cu funcția ord, a cărei
inversă este chr:

>>> ord(" ")


128585 >>> ord("
") 128586 >>>
chr(128584)

' '

Această abordare este destul de rezonabilă și consecventă, dar s-ar putea să contravină modului în care ați rezolva singur
lucrurile uneori. De exemplu, literele majuscule pot să nu funcționeze așa cum doriți.

>>> "a" < "B"


Fals

Un truc este să ignori diferența dintre literele mari și mici și să folosești metoda șirurilor mai mici. Iată un exemplu (vezi
capitolul 3):

>>> "a".lower() < "B".lower()


Adevărat

>>> 'FnOrD'.lower() == 'Fnord'.lower()


Adevărat

82
Machine Translated by Google

Capitolul 5 Condiționale, bucle și alte instrucțiuni

Alte secvențe sunt comparate în același mod, cu excepția faptului că în loc de caractere, este posibil să aveți alte
tipuri de elemente.

>>> [1, 2] < [2, 1]


Adevărat

Dacă secvențele conțin alte secvențe ca elemente, aceeași regulă se aplică acestor elemente de secvență.

>>> [2, [1, 4]] < [2, [1, 5]]


Adevărat

Operatori booleeni
Acum, aveți o mulțime de lucruri care returnează valorile adevărului. (De fapt, dat fiind faptul că toate
valorile pot fi interpretate ca valori de adevăr, toate expresiile le returnează.) Dar poate doriți să verificați mai
multe condiții. De exemplu, să presupunem că doriți să scrieți un program care citește un număr și verifică dacă
este între 1 și 10 (inclusiv). Ai putea sa faci asa:

number = int(input('Introduceți un număr între 1 și 10: ')) if number <=


10: if number >= 1: print('Great!') else: print('Wrong!')

else:
print('Greșit!')

Ar funcționa, dar este neîndemânatic. Faptul că trebuie să scrieți print „Greșit!” în două locuri ar trebui să te avertizeze
despre această stângăcie. Dublarea eforturilor nu este un lucru bun. Deci ce faci? Este atât de simplu.

număr = int(input('Introduceți un număr între 1 și 10: ')) dacă numărul


<= 10 și numărul >= 1: print('Genial!') else: print('Greșit!')

Rețineți că aș fi putut (și probabil ar fi trebuit) să simplific acest exemplu folosind următoarea
comparație înlănțuită: 1 <= număr <= 10.

Operatorul și este așa-numitul operator boolean. Este nevoie de două valori de adevăr și returnează adevărat
dacă ambele sunt adevărate și false în caz contrar. Mai ai doi din acești operatori, sau și nu. Doar cu aceste trei,
puteți combina valorile de adevăr în orice mod doriți.

if ((cash > price) sau customer_has_good_credit) și nu out_of_stock: give_goods()

83
Machine Translated by Google

Capitolul 5 Condiționale, bucle și alte instrucțiuni

LOGICA DE SCURT CIRCUIT ȘI EXPRESII CONDIȚIONALE

Operatorii booleeni au o proprietate interesantă: ei evaluează doar ceea ce trebuie să evalueze. De exemplu, expresia x
și y necesită ca atât x , cât și y să fie adevărate; deci, dacă x este fals, expresia returnează fals imediat, fără să vă faceți
griji pentru y. De fapt, dacă x este fals, returnează x; în caz contrar, returnează y.
(Puteți vedea cum dă acest sens sensul așteptat?) Acest comportament se numește logica de scurtcircuit (sau evaluare
leneșă): operatorii booleeni sunt adesea numiți operatori logici și, după cum puteți vedea, a doua valoare este uneori
„scurtcircuitată”. .” Acest lucru funcționează cu sau, de asemenea. În expresia x sau y, dacă x este adevărat, este
returnat; în caz contrar, y este returnat. (Puteți vedea cum are sens acest lucru?) Rețineți că acest lucru înseamnă că
orice cod pe care îl aveți (cum ar fi un apel de funcție) după un operator boolean poate să nu fie executat deloc. Este
posibil să vedeți acest comportament exploatat în cod ca următorul:

name = input('Vă rugăm să introduceți numele dvs.: ') sau '<necunoscut>'

Dacă nu este introdus niciun nume, expresia sau are valoarea „<necunoscut>”. În multe cazuri, s-ar putea să doriți
să utilizați o expresie condiționată mai degrabă decât astfel de trucuri de scurtcircuit, deși afirmații precum cele
anterioare își au utilizările lor.

Aserțiuni
Există o rudă utilă a instrucțiunii if, care funcționează mai mult sau mai puțin așa (pseudocod):

daca nu conditie:
program de blocare

Acum, de ce naiba ai vrea așa ceva? Pur și simplu pentru că este mai bine ca programul tău să se blocheze atunci
când apare o condiție de eroare decât într-un moment mult mai târziu. Practic, puteți cere ca anumite lucruri să fie
adevărate (de exemplu, când verificați proprietățile necesare ale parametrilor funcțiilor dvs. sau ca ajutor în timpul testării
și depanării inițiale). Cuvântul cheie folosit în declarație este assert.

>>> age = 10
>>> assert 0 < age < 100 >>>
age = -1 >>> assert 0 < age <
100 Traceback (cel mai recent
apel ultimul): Fișier „<stdin>”, rândul 1, în ?

AssertionError

Poate fi util să puneți declarația assert în programul dumneavoastră ca punct de control, dacă știți că ceva trebuie să fie adevărat pentru
ca programul dumneavoastră să funcționeze corect.
Un șir poate fi adăugat după condiție, pentru a explica afirmația.

>>> vârsta = -1
>>> afirmă 0 < vârsta < 100, „Vârsta trebuie să fie realistă”
Traceback (cel mai recent apel ultimul):
fișierul „<stdin>”, linia 1, în ?
AssertionError: Vârsta trebuie să fie realistă

84
Machine Translated by Google

Capitolul 5 Condiționale, bucle și alte instrucțiuni

Bucle
Acum știi cum să faci ceva dacă o condiție este adevărată (sau falsă), dar cum faci ceva de mai multe ori? De exemplu, este posibil
să doriți să creați un program care să vă amintească să plătiți chiria în fiecare lună, dar cu instrumentele pe care le-am analizat
până acum, ar trebui să scrieți programul astfel (pseudocod):

trimite mail
asteapta o luna trimite mail
asteapta o luna trimite mail
asteapta o luna
(... si asa mai departe)

Dar dacă ai vrea să continue să facă asta până când l-ai oprit? Practic, vrei ceva de genul acesta (din nou, pseudocod):

cât timp nu suntem opriți: trimiteți


mail
asteapta o luna

Sau, să luăm un exemplu mai simplu. Să presupunem că doriți să tipăriți toate numerele de la 1 la 100. Din nou, ați putea face asta
într-un mod stupid.

print(1)
print(2)
print(3)
...
imprimare(99)
imprimare(100)

Dar nu ai început să folosești Python pentru că ai vrut să faci prostii, nu?

în timp ce Loops
Pentru a evita codul greoi din exemplul precedent, ar fi util să poți face ceva de genul acesta:

x=1
în timp ce x <= 100:
print(x)
x += 1

Acum, cum faci asta în Python? Ai ghicit-o faci chiar așa. Nu atât de complicat, nu-i așa? De asemenea, puteți utiliza o buclă pentru a
vă asigura că utilizatorul introduce un nume, după cum urmează:

''
nume =
în timp ce nu nume:

name = input('Vă rugăm să introduceți numele dvs.: ')


print('Bună ziua, {}!'.format(nume))

85
Machine Translated by Google

Capitolul 5 Condiționale, bucle și alte instrucțiuni

Încercați să rulați acest lucru și apoi apăsați tasta Enter când vi se cere să vă introduceți numele. Veți vedea că întrebarea
apare din nou, deoarece numele este încă un șir gol, care se evaluează ca fals.

Sfat Ce s-ar întâmpla dacă ați introduce doar un spațiu ca nume? Incearca-l. Este acceptat deoarece un șir cu un
caracter spațiu nu este gol și, prin urmare, nu este fals. Acesta este cu siguranță un defect în micul nostru program,
dar este ușor de corectat: doar schimbați fără nume în timp ce nu nume sau nume.isspace() sau, poate, dacă nu
nume.strip().

for Loops

Instrucțiunea while este foarte flexibilă. Poate fi folosit pentru a repeta un bloc de cod în timp ce orice condiție este adevărată.
Deși acest lucru poate fi foarte frumos în general, uneori este posibil să doriți ceva adaptat nevoilor dvs. specifice.
O astfel de nevoie este de a efectua un bloc de cod pentru fiecare element al unui set (sau, de fapt, secvență sau alt obiect
iterabil) de valori.

Notă În principiu, un obiect iterabil este orice obiect pe care îl puteți itera (adică, utilizați într-o buclă for ). Aflați
mai multe despre iterabili și iteratoare în Capitolul 9, dar deocamdată, vă puteți gândi pur și simplu la ele ca secvențe.

Puteți face acest lucru cu declarația for:

cuvinte = [„acest”, „este”, „un”, „ex”, „papagal”] pentru cuvânt


în cuvinte: print(cuvânt)

sau

numere = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9] pentru număr în


numere: print(number)

Deoarece iterarea (un alt cuvânt pentru buclă) pe o serie de numere este un lucru obișnuit de făcut, Python are o funcție
încorporată pentru a crea intervale pentru tine.

>>> interval (0, 10)


interval (0, 10) >>>
listă (interval (0, 10)) [0, 1, 2,
3, 4, 5, 6, 7, 8, 9]

Gamele funcționează ca felii. Acestea includ prima limită (în acest caz 0), dar nu și ultima (în acest caz 10). Destul de des,
doriți ca intervalele să înceapă de la 0, iar acest lucru este de fapt presupus dacă furnizați o singură limită (care va fi apoi
ultima).

>>> interval (10)


interval (0, 10)

86
Machine Translated by Google

Capitolul 5 Condiționale, bucle și alte instrucțiuni

Următorul program scrie numerele de la 1 la 100:

pentru numărul din interval


(1.101): print(număr)

Observați că aceasta este mult mai compactă decât bucla while pe care am folosit-o mai devreme.

Sfat Dacă puteți utiliza o buclă for mai degrabă decât o buclă while , probabil că ar trebui să faceți acest lucru.

Iterarea peste dicționare Pentru a parcurge

cheile unui dicționar, puteți utiliza o declarație simplă pentru, la fel ca și cu secvențele.

d = {'x': 1, 'y': 2, 'z': 3} pentru cheie


în d: print(cheie, 'corespunde cu',
d[cheie])

Ați fi putut folosi o metodă de dicționar, cum ar fi cheile, pentru a prelua cheile. Dacă doar valorile ar fi de interes,
ai fi putut folosi d.values. Vă amintiți că d.items returnează perechi cheie-valoare ca tupluri. Un lucru grozav despre
buclele for este că puteți utiliza despachetarea secvenței în ele.

pentru cheie, valoare în d.items():


print(cheie, „corespunde cu”, valoare)

Notă Ca întotdeauna, ordinea elementelor de dicționar este nedefinită. Cu alte cuvinte, atunci când iterați fie
cheile, fie valorile unui dicționar, puteți fi sigur că le veți procesa pe toate, dar nu puteți ști în ce ordine. Dacă
ordinea este importantă, puteți stoca cheile sau valorile într-o listă separată și, de exemplu, o puteți sorta înainte
de a o itera. Dacă doriți ca maparea dvs. să-și amintească ordinea de inserare a articolelor sale, puteți utiliza clasa
OrderedDict din modulul de colecții .

Unele utilitare de iterație


Python are mai multe funcții care pot fi utile atunci când se repetă peste o secvență (sau alt obiect iterabil).
Unele dintre acestea sunt disponibile în modulul itertools (menționat în capitolul 10), dar există câteva funcții
încorporate care sunt și ele destul de utile.

Iterație paralelă
Uneori doriți să repetați peste două secvențe în același timp. Să presupunem că aveți următoarele două liste:

nume = ['anne', 'beth', 'george', 'damon'] varste =


[12, 45, 32, 102]

87
Machine Translated by Google

Capitolul 5 Condiționale, bucle și alte instrucțiuni

Dacă doriți să imprimați nume cu vârstele corespunzătoare, puteți face următoarele:

pentru i în interval(len(nume)):
print(nume[i], „este”, vârstele [i], „ani vechi”)

Aici, i servește ca nume de variabilă standard pentru indici de buclă (cum sunt numite aceste lucruri). Un instrument
util pentru iterația paralelă este funcția zip încorporată, care „zipsește” secvențele, returnând o secvență de tupluri.
Valoarea returnată este un obiect zip special, destinat iterației, dar poate fi convertită folosind listă, pentru a arunca o
privire asupra conținutului său.

>>> listă(zip(nume, vârste)) [('anne',


12), ('beth', 45), ('george', 32), ('damon', 102)]

Acum putem despacheta tuplurile din bucla noastră.

pentru nume, vârstă în zip(nume, vârste):


print(nume, „este”, vârsta, „ani”)

Funcția zip funcționează cu câte secvențe doriți. Este important să rețineți ce face zip atunci când secvențele sunt de
lungimi diferite: se oprește atunci când se epuizează cea mai scurtă secvență.

>>> listă(zip(interval(5), interval(100000000))) [(0, 0),


(1, 1), (2, 2), (3, 3), (4, 4)]

Iterație numerotată
În unele cazuri, doriți să iterați peste o secvență de obiecte și, în același timp, să aveți acces la indexul obiectului curent.
De exemplu, este posibil să doriți să înlocuiți fiecare șir care conține subșirul „xxx” într-o listă de șiruri. Cu siguranță ar
exista multe moduri de a face acest lucru, dar să presupunem că doriți să faceți ceva în următoarele direcții:

pentru șir în șiruri: dacă 'xxx'


în șir: index =
strings.index(string) # Căutați șirul în lista de șiruri strings[index] = '[cenzurat]'

Acest lucru ar funcționa, dar pare inutil să căutați șirul dat înainte de a-l înlocui. De asemenea, dacă nu l-ați înlocuit,
căutarea s-ar putea să vă dea indexul greșit (adică indexul unei apariții anterioare a aceluiași cuvânt). O versiune mai
bună ar fi următoarea:

index = 0
pentru șir în șiruri de caractere:
dacă „xxx” în șir de
caractere: șiruri de caractere[index] =
index „[cenzurat]” += 1

88
Machine Translated by Google

Capitolul 5 Condiționale, bucle și alte instrucțiuni

Acest lucru pare, de asemenea, puțin ciudat, deși acceptabil. O altă soluție este utilizarea funcției încorporate
enumerate.

pentru index, șir în enumerate(șiruri de caractere):


dacă „xxx” în șir de caractere: șiruri de
caractere[index] = „[cenzurat]”

Această funcție vă permite să iterați peste perechi index-valoare, unde indicii sunt furnizați automat.

Iterație inversată și sortată


Să ne uităm la alte două funcții utile: inversat și sortat. Sunt similare cu metodele de listă inversă și sortare (cu argumente
de preluare sortate similare cu cele luate prin sortare), dar funcționează pe orice secvență sau obiect iterabil și, în loc să
modifice obiectul în loc, returnează versiuni inversate și sortate.

>>> sortat([4, 3, 6, 8, 3]) [3, 3, 4, 6, 8]


>>> sortat('Bună, lume!')

[' ', '!', ',', 'H', 'd', 'e', 'l', 'l', 'l', 'o', 'o', 'r', 'w '] >>> listă(inversată('Bună, lume!')) ['!', 'd', 'l', 'r',
'o', 'w', '', ',', ' o', 'l', 'l', 'e', 'H'] >>> ''.join(reversed('Bună, lume!')) '!dlrow ,olleH'

Rețineți că, deși sortat returnează o listă, inversat returnează un obiect iterabil mai misterios, la fel ca zip.
Nu trebuie să vă faceți griji cu privire la ce înseamnă acest lucru cu adevărat; îl puteți folosi în bucle sau metode precum join
fără probleme. Pur și simplu nu îl puteți indexa sau tăia, sau apelați metode de listă direct pe el. Pentru a îndeplini aceste
sarcini, trebuie doar să convertiți obiectul returnat cu listă.

Sfat Putem folosi trucul cu minuscule pentru a obține o sortare alfabetică corectă. De exemplu, puteți
folosi str.lower ca argument cheie pentru sortare sau sortare. De exemplu, sortat("aBc", cheie=str.lower)
returnează ['a', 'B', 'c'].

Ieșirea din bucle


De obicei, o buclă pur și simplu execută un bloc până când condiția sa devine falsă sau până când a consumat toate elementele
secvenței. Dar uneori poate doriți să întrerupeți bucla, să începeți o nouă iterație (o „rundă” de execuție a blocului) sau pur și
simplu să încheiați bucla.

pauză
Pentru a încheia (a ieși dintr-o buclă), folosiți break. Să presupunem că doriți să găsiți cel mai mare pătrat (rezultatul unui
număr întreg înmulțit cu el însuși) sub 100. Apoi începeți de la 100 și repeți în jos până la 0. Când ați găsit un pătrat, nu este
nevoie să continuați, așa că pur și simplu ieși din buclă.

89
Machine Translated by Google

Capitolul 5 Condiționale, bucle și alte instrucțiuni

din matematică import sqrt


pentru n în interval (99, 0, -1):
rădăcină = sqrt(n) dacă
rădăcină == int(rădăcină):
print(n) break

Dacă rulați acest program, acesta va tipări 81 și se va opri. Observați că am adăugat un al treilea argument la interval -
acesta este pasul, diferența dintre fiecare pereche de numere adiacente din succesiune. Poate fi folosit pentru a itera în jos
așa cum am făcut aici, cu o valoare de pas negativă și poate fi folosit pentru a sări peste numere.

>>> interval (0, 10, 2) [0,


2, 4, 6, 8]

continua
Declarația continue este folosită mai rar decât break. Face ca iterația curentă să se încheie și să „sare” la începutul următoarei.
În principiu, înseamnă „săriți restul corpului buclei, dar nu terminați bucla”. Acest lucru poate fi util dacă aveți un corp de buclă
mare și complicat și mai multe motive posibile pentru a o sări peste el. În acest caz, puteți utiliza continuare, după cum urmează:

pentru x în
continuare: dacă condiția1:
continuă dacă condiția2:
continuă dacă condiția3: continuă

face_ceva()
face_ceva_altceva()
face_alt_ceva() etc()

În multe cazuri, totuși, pur și simplu folosirea unei declarații if este la fel de bună.

pentru x în
următoarea: dacă nu (condiția1 sau condiția2 sau condiția3):
do_something() do_something_else() do_another_thing() etc()

Chiar dacă continuare poate fi un instrument util, nu este esențial. Declarația break, totuși, este ceva cu care ar trebui să te
obișnuiești, pentru că este folosit destul de des împreună cu while True, așa cum este explicat în secțiunea următoare.

90
Machine Translated by Google

Capitolul 5 Condiționale, bucle și alte instrucțiuni

În timp ce True/break Idiom

Buclele for și while din Python sunt destul de flexibile, dar din când în când, este posibil să întâmpinați o problemă care vă face să doriți să
aveți mai multe funcționalități. De exemplu, să presupunem că doriți să faceți ceva atunci când un utilizator introduce cuvinte la un prompt și doriți
să încheiați bucla atunci când nu este furnizat niciun cuvânt. O modalitate de a face asta ar fi astfel:

cuvânt = „fachin” în
timp ce cuvânt:

word = input('Vă rugăm să introduceți un cuvânt: ') # faceți


ceva cu cuvântul: print('Cuvântul a fost', cuvânt)

Iată un exemplu de sesiune:

Vă rugăm să introduceți un cuvânt: primul


Cuvântul a fost primul
Vă rugăm să introduceți un cuvânt: secundă
Cuvântul a fost al doilea
Vă rugăm să introduceți un cuvânt:

Aceasta funcționează exact așa cum se dorește. (Se presupune că ați face ceva mai util cu cuvântul decât să-l tipăriți.) Totuși, după cum puteți
vedea, acest cod este puțin urât. Pentru a intra în buclă în primul rând, trebuie să atribuiți cuvântului o valoare inactivă (neutilizată). Valorile false
de genul acesta sunt de obicei un semn că nu faci lucrurile corect. Să încercăm să scăpăm de el.

word = input('Vă rugăm să introduceți un cuvânt: ') în timp


ce cuvânt: # faceți ceva cu cuvântul: print('Cuvântul a fost ',

cuvânt) word = input('Vă rugăm să introduceți un


cuvânt: ')

Aici manechinul a dispărut, dar am un cod repetat (ceea ce este și un lucru rău): trebuie să folosesc aceeași atribuire și să apelez la intrare
în două locuri. Cum pot evita asta? Pot folosi idiomul while True/break.

în timp ce este adevărat:

word = input('Vă rugăm să introduceți un cuvânt: ') dacă nu


cuvânt: break # faceți ceva cu cuvântul: print('Cuvântul a

fost ', cuvânt)

Partea while True vă oferă o buclă care nu se va termina niciodată de la sine. În schimb, puneți condiția într-o instrucțiune if în interiorul buclei, care
apelează break atunci când condiția este îndeplinită. Astfel, puteți încheia bucla oriunde în interiorul buclei, nu numai la început (ca și în cazul unei
bucle normale while). Linia if/break împarte bucla în mod natural în două părți: prima se ocupă de configurarea lucrurilor (partea care ar fi duplicată
cu o buclă normală while), iar cealaltă parte folosește inițializarea din prima parte, cu condiția să că condiția buclei este adevărată.

Deși ar trebui să fiți atenți să folosiți break prea des în codul dvs. (pentru că vă poate face bucle
mai greu de citit, mai ales dacă puneți mai mult de o pauză într-o singură buclă), această tehnică specifică este atât de comună încât
majoritatea programatorilor Python (inclusiv dvs.) probabil vă vor putea urma intențiile.

91
Machine Translated by Google

Capitolul 5 Condiționale, bucle și alte instrucțiuni

else clauze în bucle


Când utilizați instrucțiuni break în bucle, este adesea pentru că ați „găsit” ceva sau pentru că ceva s-a „întîmplat”. Este
ușor să faci ceva când erupți (cum ar fi print(n)), dar uneori s-ar putea să vrei să faci ceva dacă nu ai izbucnit. Dar cum afli?
Puteți folosi o variabilă booleană, să o setați la False înainte de buclă și să o setați la True atunci când izbucniți. Apoi puteți
folosi o declarație if pentru a verifica dacă ați izbucnit.

broke_out = Fals pentru


x în seq: do_something(x)
if condition(x):
broke_out = True
break

do_something_else(x) if not
broke_out: print("Nu am izbucnit!")

O modalitate mai simplă este să adăugați o clauză else în bucla dvs. - este executată numai dacă nu ați apelat break. Să
reutilizam exemplul din secțiunea anterioară despre pauză.

din matematică import sqrt


pentru n în interval (99, 81, -1):
rădăcină = sqrt (n) dacă rădăcină
== int (rădăcină): print(n) break

altceva:

print("Nu l-am gasit!")

Observați că am schimbat limita inferioară (exclusivă) la 81 pentru a testa clauza else. Dacă rulați programul, se afișează „Nu l-
am găsit!” deoarece (după cum ați văzut în secțiunea despre break) cel mai mare pătrat sub 100 este 81. Puteți folosi clauze
continue, break și else cu atât buclele for, cât și buclele while.

Înțelegeri—Ușor Loopy
Înțelegerea listelor este o modalitate de a face liste din alte liste (similar cu înțelegerea seturilor, dacă cunoașteți termenul
respectiv de la matematică). Funcționează într-un mod similar cu buclele for și este de fapt destul de simplu.

>>> [x * x pentru x în intervalul (10)] [0,


1, 4, 9, 16, 25, 36, 49, 64, 81]

Lista este compusă din x*x pentru fiecare x din intervalul (10). Destul de direct? Ce se întâmplă dacă doriți să imprimați
numai acele pătrate care sunt divizibile cu 3? Apoi puteți utiliza operatorul modulo—y % 3 returnează zero când y este
divizibil cu 3. (Rețineți că x*x este divizibil cu 3 numai dacă x este divizibil cu 3.) Introduceți acest lucru în înțelegerea listei
adăugând un dacă parte din ea.

>>> [x*x pentru x în intervalul (10) dacă x % 3 == 0] [0, 9,


36, 81]

92
Machine Translated by Google

Capitolul 5 Condiționale, bucle și alte instrucțiuni

De asemenea, puteți adăuga mai multe pentru piese.

>>> [(x, y) pentru x în interval (3) pentru y în interval (3)] [(0, 0),
(0, 1), (0, 2), (1, 0), ( 1, 1), (1, 2), (2, 0), (2, 1), (2, 2)]

Ca o comparație, următoarele două bucle for construiesc aceeași listă:

rezultat = []
for x in range(3): for y
in range(3)
result.append((x, y))

Aceasta poate fi combinată cu o clauză if, la fel ca înainte.

>>> fete = ['alice', 'bernice', 'clarice'] >>> băieți = ['chris',


'arnold', 'bob'] >>> [b+'+'+g pentru b la băieți pentru g la
fete dacă b[0] == g[0]] ['chris+clarice', 'arnold+alice', 'bob+bernice']

Acest lucru dă perechile de băieți și fete care au aceeași literă inițială în prenume.

O SOLUȚIE MAI BUNĂ

Exemplul de împerechere băiat/fată nu este deosebit de eficient, deoarece verifică fiecare împerechere posibilă.
Există multe modalități de a rezolva această problemă în Python. Următoarele au fost sugerate de Alex Martelli:

fete = ['alice', 'bernice', 'clarice'] băieți = ['chris',


'arnold', 'bob'] letterGirls = {} pentru fată la fete:
letterGirls.setdefault(girl[0], [] ).adăugați(fată)
print([b+'+'+g pentru b la băieți pentru g în
literăFetele[b[0]]])

Acest program construiește un dicționar, numit letterGirls, în care fiecare intrare are o singură literă ca cheie și
o listă de nume de fete ca valoare. ( Metoda dicționarului setdefault este descrisă în capitolul anterior.) După ce
acest dicționar a fost construit, lista de înțelegere trece peste toți băieții și caută toate fetele al căror nume începe
cu aceeași literă ca și băiatul actual. În acest fel, înțelegerea listei nu trebuie să încerce orice combinație posibilă de
băiat și fată și să verifice dacă primele litere se potrivesc.

Folosirea parantezelor normale în loc de paranteze nu vă va oferi o „înțelegere tuple” - veți ajunge
cu un generator. Vedeți bara laterală „Generatoare Loopy” din Capitolul 9 pentru mai multe informatii. Cu toate acestea,
puteți folosi acolade pentru a realiza înțelegerea dicționarului.

>>> pătrate = {i:"{} pătrat este {}".format(i, i**2) pentru i în intervalul (10)} >>> pătrat[8] '8 pătrat
este 64'

În loc de o singură expresie în fața pentru, așa cum ați avea cu o listă de înțelegere, aveți două expresii separate prin două
puncte. Acestea vor deveni cheile și valorile lor corespunzătoare.

93
Machine Translated by Google

Capitolul 5 Condiționale, bucle și alte instrucțiuni

Și Trei pentru Drum


Pentru a încheia capitolul, să aruncăm o privire rapidă la alte trei declarații: pass, del și exec.

Nu s-a intamplat nimic!


Uneori nu trebuie să faci nimic. Acest lucru poate nu este foarte des, dar atunci când se întâmplă, este bine să știți
că aveți declarația de trecere.

>>> trece
>>>

Nu se întâmplă mare lucru aici.

Acum, de ce naiba ai vrea o afirmație care nu face nimic? Poate fi util ca substituent în timp ce scrieți cod. De
exemplu, este posibil să fi scris o declarație if și doriți să o încercați, dar vă lipsește codul pentru unul dintre blocurile
dvs. Luați în considerare următoarele:

if name == 'Ralph Auldus Melish':


print('Bun venit!') elif name == 'Enid':
# Neterminat încă ... elif name == 'Bill
Gates': print('Acces refuzat')

Acest cod nu va rula deoarece un bloc gol este ilegal în Python. Pentru a remedia acest lucru, adăugați pur și simplu o declarație de
trecere în blocul din mijloc.

if name == 'Ralph Auldus Melish':


print('Bun venit!') elif name == 'Enid':
# Neterminat încă ... pass elif name ==
'Bill Gates': print('Acces refuzat')

Notă O alternativă la combinația dintre un comentariu și o instrucțiune de trecere este să inserați pur și simplu un șir.

Acest lucru este util în special pentru funcțiile neterminate (vezi Capitolul 6) și clasele (vezi Capitolul 7), deoarece acestea vor

acționa apoi ca șiruri de documente (explicat în Capitolul 6).

Ștergerea cu del În general,

Python șterge obiectele pe care nu le mai folosiți (pentru că nu vă mai referiți la ele prin intermediul unor variabile
sau părți ale structurilor dvs. de date).

>>> ticălos = {'vârsta': 42, 'prenume': 'Robin', 'numele': 'de Locksley'} >>> robin = ticălos

94
Machine Translated by Google

Capitolul 5 Condiționale, bucle și alte instrucțiuni

>>> ticălos
{'vârsta': 42, 'prenume': 'Robin', 'numele': 'de Locksley'} >>> robin {'vârsta': 42,
'prenume': 'Robin', „nume”: „al lui Locksley”} >>> ticălos = Niciunul >>> robin
{„vârsta”: 42, „nume”: „Robin”, „nume”: „al lui Locksley”} >>> robin = Nici unul

La început, robin și ticălos sunt amândoi legați de același dicționar. Așa că, când îi desemnez pe Nimic pe ticălos,
dicționarul este încă disponibil prin Robin. Dar când îi atribui Nimic și lui Robin, dicționarul plutește brusc în
memoria computerului fără nici un nume atașat. Nu am cum să-l recuperez sau să-l folosesc, așa că interpretul
Python (în înțelepciunea sa infinită) îl șterge pur și simplu. (Aceasta se numește colectare de gunoi.) Rețineți că aș fi
putut folosi orice valoare, în afară de None, de asemenea. Dicționarul ar fi la fel de dispărut.

O altă modalitate de a face acest lucru este să folosim instrucțiunea del (pe care am folosit-o pentru a șterge secvența și dicționarul
elementele din capitolele 2 și 4, vă amintiți?). Acest lucru nu numai că elimină o referință la un obiect, dar elimină
și numele în sine.

>>> x = 1
>>> del x
>>> x
Traceback (cel mai recent apel ultimul):
Fișierul „<pyshell#255>”, linia 1, în ?
X
NameError: numele „x” nu este definit

Acest lucru poate părea ușor, dar poate fi de fapt puțin dificil de înțeles uneori. De exemplu, în exemplul următor, x
și y se referă la aceeași listă:

>>> x = ["Bună ziua", "lume"] >>>


y = x >>> y[1] = "Python"

>>> x
[„Bună ziua”, „Python”]

Ați putea presupune că ștergând x, ați șterge și y, dar nu este cazul.

>>> del x
>>> y
[„Bună ziua”, „Python”]

De ce asta? x și y s-au referit la aceeași listă, dar ștergerea x nu l-a afectat deloc pe y. Motivul pentru aceasta este că
ștergeți doar numele, nu lista în sine (valoarea). De fapt, nu există nicio modalitate de a șterge valori în Python - și nu
este nevoie, deoarece interpretul Python o face singur ori de câte ori nu mai folosiți valoarea.

95
Machine Translated by Google

Capitolul 5 Condiționale, bucle și alte instrucțiuni

Executarea și evaluarea șirurilor cu exec și eval


Uneori poate doriți să creați cod Python „din zbor” și să-l executați ca instrucțiune sau să-l evaluați ca expresie. Acest
lucru se poate limita uneori cu magia neagră – considerați-vă avertizat. Atât exec, cât și eval sunt funcții, dar exec era
un tip de declarație propriu, iar eval este strâns legat de acesta, așa că de aceea le discut aici.

Atenție În această secțiune, învățați să executați cod Python stocat într-un șir. Aceasta este o posibilă gaură de
securitate de mari dimensiuni. Dacă executați un șir în care părți din conținut au fost furnizate de un utilizator, aveți
puțin sau deloc control asupra codului pe care îl executați. Acest lucru este deosebit de periculos în aplicațiile de
rețea, cum ar fi scripturile CGI (Common Gateway Interface), despre care veți afla în Capitolul 15.

exec
Funcția exec este folosită pentru a executa un șir.

>>> exec("print('Bună, lume!')")


Salut Lume!

Cu toate acestea, utilizarea instrucțiunii exec cu un singur argument este rareori un lucru bun. În cele mai multe
cazuri, doriți să îi furnizați un spațiu de nume - un loc în care își poate pune variabilele. În caz contrar, codul vă va
deteriora spațiul de nume (adică vă va schimba variabilele). De exemplu, să presupunem că codul folosește numele sqrt.

>>> din matematică import sqrt


>>> exec("sqrt = 1") >>> sqrt(4)

Traceback (cel mai recent apel ultimul):


fișierul „<pyshell#18>”, linia 1, în ? sqrt(4)

TypeError: obiectul nu poate fi apelat: 1

Ei bine, de ce ai face așa ceva în primul rând? Funcția exec este utilă în principal atunci când construiți șirul de cod
din mers. Și dacă șirul este construit din părți pe care le obțineți din alte locuri și, eventual, de la utilizator, rareori
puteți fi sigur de exact ce va conține. Deci, pentru a fi în siguranță, îi oferiți un dicționar, care va funcționa ca spațiu
de nume pentru el.

Notă Conceptul de spații de nume, sau domenii, este unul important. O veți analiza în profunzime în capitolul
următor, dar pentru moment, vă puteți gândi la un spațiu de nume ca la un loc în care vă păstrați variabilele, la
fel ca un dicționar invizibil. Deci, atunci când executați o atribuire de genul x = 1, stocați cheia x cu valoarea 1 în
spațiul de nume curent, care va fi adesea spațiul de nume global (pe care l-am folosit, în cea mai mare parte, până
acum), dar nu nu trebuie să fie.

96
Machine Translated by Google

Capitolul 5 Condiționale, bucle și alte instrucțiuni

Faceți acest lucru adăugând un al doilea argument - un dicționar care va funcționa ca spațiu de nume pentru șirul dvs. de
cod.4

>>> din matematică import sqrt


>>> scope = {} >>> exec('sqrt = 1',
scope) >>> sqrt(4)

2.0
>>> domeniu ['sqrt'] 1

După cum puteți vedea, codul potențial distructiv nu suprascrie funcția sqrt. Funcția funcționează exact așa cum ar trebui,
iar variabila sqrt rezultată din atribuirea executată este disponibilă din domeniu.

Rețineți că, dacă încercați să imprimați domeniul de aplicare, vedeți că conține o mulțime de lucruri, deoarece dicționarul
numit __builtins__ este adăugat automat și conține toate funcțiile și valorile încorporate.

>>> len(sfera)
2
>>> scope.keys()
['sqrt', '__builtins__']

eval
O funcție încorporată care este similară cu exec este eval (pentru „evaluare”). La fel cum exec execută o serie de
instrucțiuni Python, eval evaluează o expresie Python (scrisă într-un șir) și returnează valoarea rezultată. (exec nu
returnează nimic, deoarece este o declarație în sine.) De exemplu, puteți utiliza următoarele pentru a face un calculator
Python:

>>> eval(input("Introduceți o expresie aritmetică: "))


Introduceți o expresie aritmetică: 6 + 18 * 2 42

Puteți furniza un spațiu de nume cu eval, la fel ca și cu exec, deși expresiile rareori refac variabilele așa cum o fac de obicei
instrucțiunile.

Aten ie Chiar dacă expresiile nu relega variabilele de regulă, cu siguran ă că pot (de exemplu, apelând
func ii care relega variabilele globale). Prin urmare, utilizarea eval cu o bucată de cod neîncrezătoare nu este mai
sigură decât utilizarea exec. În prezent, nu există o modalitate sigură de a executa cod neîncrezat în Python. O
alternativă este utilizarea unei implementări a Python, cum ar fi Jython (vezi capitolul 17) și utilizați un mecanism nativ,
cum ar fi sandbox-ul Java.

4
De fapt, puteți furniza exec două spații de nume, unul global și unul local. Cel global trebuie să fie un dicționar, dar cel local
poate fi orice mapare. Același lucru este valabil și pentru eval.

97
Machine Translated by Google

Capitolul 5 Condiționale, bucle și alte instrucțiuni

AMORZAREA LOMEI

Când furnizați un spațiu de nume pentru exec sau eval, puteți, de asemenea, să introduceți câteva valori înainte de a utiliza
efectiv spațiul de nume.

>>> scope = {} >>>


scope['x'] = 2 >>>
scope['y'] = 3 >>> eval('x
* y', scope)
6

În același mod, un domeniu dintr-un apel exec sau eval poate fi folosit din nou în altul.

>>> scope = {} >>>


exec('x = 2', scope) >>> eval('x *
x', scope)
4

Ai putea construi programe destul de complicate în acest fel, dar... probabil că nu ar trebui.

Un rezumat rapid
În acest capitol, ați văzut mai multe tipuri de afirmații.

Imprimare: Puteți utiliza instrucțiunea print pentru a imprima mai multe valori, separându-
le cu virgule. Dacă încheiați declarația cu o virgulă, instrucțiunile print ulterior vor continua
să se imprime pe aceeași linie.

Import: Uneori nu vă place numele unei funcții pe care doriți să o importați - poate
ați folosit deja numele pentru altceva. Puteți utiliza instrucțiunea import … as … pentru a
redenumi local o funcție.

Atribuții: Ați văzut că, prin minunea despachetării secvenței și a atribuirilor înlănțuite,
puteți atribui valori mai multor variabile simultan și că, cu atribuiri augmentate, puteți
modifica o variabilă în loc.

Blocuri: blocurile sunt folosite ca mijloc de grupare a declarațiilor prin indentare.


Ele sunt folosite în condiționale și bucle și, după cum vedeți mai târziu în carte, în
definițiile de funcții și clase, printre altele.

Condiționale: O instrucțiune condiționată fie execută un bloc, fie nu, în funcție de o condiție
(expresie booleană). Mai multe condiționale pot fi legate împreună cu if/elif/ else. O variație
pe această temă este expresia condiționată, a if b else c.

Aserțiuni: o aserțiune pur și simplu afirmă că ceva (o expresie booleană) este adevărată,
opțional cu un șir care explică de ce trebuie să fie așa. Dacă se întâmplă ca expresia să fie
falsă, aserția vă va opri programul (sau de fapt ridică o excepție - mai multe despre asta în
Capitolul 8). Este mai bine să găsiți o eroare mai devreme decât să o lăsați să se strecoare în
programul dvs. până nu știți de unde provine.

98
Machine Translated by Google

Capitolul 5 Condiționale, bucle și alte instrucțiuni

Bucle: fie puteți executa un bloc pentru fiecare element dintr-o secvență (cum ar fi un interval
de numere), fie puteți continua să îl executați cât timp o condiție este adevărată. Pentru a
sări peste restul blocului și a continua cu următoarea iterație, utilizați instrucțiunea continue;
pentru a ieși din buclă, utilizați instrucțiunea break. Opțional, puteți adăuga o clauză else la
sfârșitul buclei, care va fi executată dacă nu ați executat nicio instrucțiune break în interiorul
buclei.

Înțelegerea: Acestea nu sunt cu adevărat declarații – sunt expresii care seamănă mult cu
bucle, motiv pentru care le-am grupat cu instrucțiunile în buclă.
Prin înțelegerea listelor, puteți construi liste noi din cele vechi, aplicând funcții elementelor,
filtrăndu-le pe cele pe care nu le doriți și așa mai departe.
Tehnica este destul de puternică, dar în multe cazuri, utilizarea buclelor simple și a
condiționalelor (care vor duce întotdeauna la bun sfârșit) poate fi mai ușor de citit. Expresii
similare pot fi folosite pentru a construi dicționare.

pass, del, exec și eval: instrucțiunea pass nu face nimic, ceea ce poate fi util ca substituent, de
exemplu. Instrucțiunea del este folosită pentru a șterge variabile sau părți ale unei structuri de
date, dar nu poate fi folosită pentru a șterge valori. Funcția exec este folosită pentru a executa
un șir ca și cum ar fi un program Python. Funcția eval evaluează o expresie scrisă într-un șir și
returnează rezultatul.

Funcții noi în acest capitol


Func ie Descriere
chr(n) Returnează un șir de un caracter atunci când trece ordinalul n (0 n < 256)

eval(sursă[, globals[, locals]]) exec(source[, Evaluează un șir ca expresie și returnează valoarea

globals[, locals]]) enumerate(seq) ord(c) Evaluează și execută un șir ca instrucțiune

range([start,] stop[, step]) inversat( seq) Produce perechi (indice, valoare) potrivite pentru iterare

sorted(seq[, cmp][, key][, reverse]) Returnează Returnează valoarea ordinală întreagă a unui șir de un caracter

o listă cu valorile seq în ordine sortată Creează o listă de numere întregi

xrange([start,] stop[, step]) zip(seq1, seq2,...) Oferă valorile seq în ordine inversă, potrivite pentru iterație

Creează un obiect xrange, folosit pentru iterare

Creează o nouă secvență potrivită pentru iterație paralelă

Ce acum?
Acum ai clarificat elementele de bază. Puteți implementa orice algoritm pe care îl puteți visa; puteți citi parametrii și
puteți imprima rezultatele. În următoarele două capitole, veți afla despre ceva care vă va ajuta să scrieți programe mai mari fără
a pierde imaginea de ansamblu. Acel ceva se numește abstracție.

99
Machine Translated by Google

CAPITOLUL 6

Abstracția

În acest capitol, veți învăța cum să grupați instrucțiunile în funcții, ceea ce vă permite să spuneți computerului cum să
facă ceva și să-i spuneți o singură dată. Nu va trebui să-i dai aceleași instrucțiuni detaliate din nou și din nou. Capitolul
oferă o introducere detaliată a parametrilor și a domeniului și veți afla ce este recursiunea și ce poate face aceasta
pentru programele dvs.

Lenea este o virtute


Programele pe care le-am scris până acum au fost destul de mici, dar dacă vrei să faci ceva mai mare, în curând vei avea
probleme. Luați în considerare ce se întâmplă dacă ați scris un cod într-un loc și trebuie să îl utilizați și în alt loc. De
exemplu, să presupunem că ați scris un fragment de cod care a calculat unele numere Fibonacci (o serie de numere în
care fiecare număr este suma celor două anterioare).

fibs = [0, 1]
pentru i în intervalul
(8): fibs.append(fibs[-2] + fibs[-1])

După ce rulează acest lucru, fibs conține primele zece numere Fibonacci.

>>> fibs
[0, 1, 1, 2, 3, 5, 8, 13, 21, 34]

Este în regulă dacă ceea ce doriți este să calculați primele zece numere Fibonacci o dată. Puteți chiar să modificați bucla
for pentru a funcționa cu un interval dinamic, lungimea secvenței rezultate fiind furnizată de utilizator.

fibs = [0, 1] num


= int(input('Câte numere Fibonacci vrei? ')) pentru i în interval (num-2):
fibs.append(fibs[-2] + fibs[-1] ) imprimare (fibs)

Dar dacă vrei să folosești numerele și pentru altceva? Cu siguranță ai putea scrie din nou aceeași buclă atunci când ai
nevoie, dar dacă ai fi scris o bucată de cod mai complicată, cum ar fi una care a descărcat un set de pagini web și a
calculat frecvențele tuturor cuvintelor folosite? Mai vrei să scrii tot codul de mai multe ori, o dată de fiecare dată când ai
nevoie de el? Nu, programatorii adevărați nu fac asta. Programatorii adevărați sunt leneși – nu leneși într-un mod rău, ci
în sensul că nu fac muncă inutilă.

© Magnus Lie Hetland 2017 101


ML Hetland, Beginning Python, DOI 10.1007/978-1-4842-0028-5_6
Machine Translated by Google

Capitolul 6 Abstrac ia

Deci, ce fac programatorii adevărați? Își fac programele mai abstracte. Ai putea face
programul anterior mai abstract după cum urmează:

num = input('Câte numere vrei? ') print(fibs(num))

Aici se scrie în mod concret doar ceea ce este specific acestui program (citirea numărului și tipărirea rezultatului). De fapt,
calcularea numerelor Fibonacci se face într-o manieră abstractă: pur și simplu îi spui computerului să o facă. Nu spui în mod specific
cum ar trebui făcut. Creezi o funcție numită fibs și o folosești atunci când ai nevoie de funcționalitatea micului program Fibonacci. Vă
scutește de mult efort dacă aveți nevoie de el în mai multe locuri.

Abstracție și structură
Abstracția poate fi utilă ca economisitor de forță de muncă, dar este de fapt mai importantă decât atât. Este cheia pentru ca
programele de calculator să fie înțelese de oameni (ceea ce este esențial, indiferent dacă le scrii sau le citești). Calculatoarele în
sine sunt perfect mulțumite cu instrucțiuni foarte concrete și specifice, dar oamenii în general nu sunt. Dacă îmi ceri indicații către
cinema, de exemplu, nu ai vrea să-ți răspund: „Mergeți 10 pași înainte, întoarceți 90 de grade la stânga, mai mergeți 5 pași,
întoarceți 45 de grade la dreapta, mergeți 123 de pași. .” În curând ți-ai pierde urma, nu-i așa?

Acum, dacă ți-aș spune în schimb „Mergi pe strada asta până ajungi la un pod, treci podul și cinematograful este în stânga
ta”, cu siguranță m-ai înțelege. Ideea este că știi deja să mergi pe stradă și să treci un pod. Nici nu aveți nevoie de instrucțiuni
explicite despre cum să faceți.
Structurați programele de calculator într-un mod similar. Programele dvs. ar trebui să fie destul de abstracte, ca în
„Descărcați pagina, calculați frecvențele și imprimați frecvența fiecărui cuvânt”. Acest lucru este ușor de înțeles.
De fapt, să traducem această descriere de nivel înalt într-un program Python chiar acum.

pagina = download_page()
frecvențe = compute_frequencies(pagina)
pentru cuvânt, frecvență în frecvențe:
print(cuvânt, frecvență)

Citind acest lucru, oricine ar putea înțelege ce face programul. Cu toate acestea, nu ați spus în mod explicit nimic despre cum
ar trebui să o facă. Spune-i computerului să descarce pagina și să calculeze frecvențele.
Specificul acestor operații va trebui scris în altă parte — în definiții separate ale funcției.

Crearea propriilor funcții


O funcție este ceva pe care îl puteți apela (eventual cu niște parametri - lucrurile pe care le puneți în paranteze), care
efectuează o acțiune și returnează o valoare.1 În general, puteți spune dacă ceva este apelabil sau nu cu funcția încorporată
apelabil.

>>> import
matematică >>> x = 1
>>> y = math.sqrt >>>
apelabil(x)
Fals
>>> apelabil(y)
Adevărat

1
De fapt, funcțiile din Python nu returnează întotdeauna valori. Veți afla mai multe despre asta mai târziu în capitol.

102
Machine Translated by Google

Capitolul 6 Abstrac ia

După cum știți din secțiunea anterioară, crearea de funcții este esențială pentru programarea structurată. Deci,
cum definiți o funcție? Faceți acest lucru cu declarația def (sau „definiția funcției”).

def hello(nume):
return „Bună ziua,” + nume + '!'

După ce rulați aceasta, aveți o nouă funcție disponibilă, numită hello, care returnează un șir cu un salut pentru
numele dat ca singur parametru. Puteți utiliza această funcție la fel cum le folosiți pe cele încorporate.

>>> print(bună ziua('lume'))


Salut Lume! >>>
print(bună ziua('Gumby'))
Bună, Gumby!

Destul de îngrijit, nu? Luați în considerare cum ați scrie o funcție care a returnat o listă de numere Fibonacci. U or!
Folosiți doar codul de mai înainte, dar în loc să citiți un număr de la utilizator, îl primiți ca parametru.

def fibs(num):
rezultat = [0, 1]
pentru i în interval (num-2):
rezultat.append(rezultat[-2] + rezultat[-1])
returnează rezultat

După rularea acestei declarații, practic i-ați spus interpretului cum să calculeze numerele Fibonacci. Acum nu mai
trebuie să vă faceți griji pentru detalii. Pur și simplu utilizați funcția fibs.

>>> fibs(10) [0,


1, 1, 2, 3, 5, 8, 13, 21, 34] >>> fibs(15) [0,
1, 1, 2, 3, 5, 8, 13, 21, 34, 55, 89, 144, 233,
377]

Numele num și rezultat sunt destul de arbitrare în acest exemplu, dar returnarea este importantă.
Instrucțiunea return este folosită pentru a returna ceva din funcție (așa cum am folosit-o și în funcția hello
anterioară).

Documentarea funcțiilor Dacă doriți să

vă documentați funcțiile, astfel încât să fiți sigur că alții le vor înțelege mai târziu, puteți adăuga comentarii
(începând cu semnul hash, #). Un alt mod de a scrie comentarii este pur și simplu să scrieți șiruri de caractere
singure. Astfel de șiruri pot fi deosebit de utile în unele locuri, cum ar fi imediat după o instrucțiune def (și la
începutul unui modul sau al unei clase - aflați mai multe despre clase în Capitolul 7 și module din capitolul 10). Dacă
puneți un șir la începutul unei funcții, acesta este stocat ca parte a funcției și se numește docstring. Următorul cod
demonstrează cum să adăugați un docstring la o funcție:

def square(x):
„Calculează pătratul numărului x”. întoarce x
* X

103
Machine Translated by Google

Capitolul 6 Abstrac ia

Docstring poate fi accesat astfel:

>>> square.__doc__
'Calculează pătratul numărului x.'

Notă _ _doc_ _ este un atribut al funcției. Veți afla multe mai multe despre atribute în Capitolul 7. Sublinierea
dublă din numele atributului înseamnă că acesta este un atribut special. Atributele speciale sau „magice” ca acesta
sunt discutate în Capitolul 9.

O funcție specială încorporată numită ajutor poate fi destul de utilă. Dacă îl utilizați în interpretul interactiv, puteți obține
informații despre o funcție, inclusiv șirul său documentar.

>>> ajutor(pătrat)
Ajutor pe pătratul funcției din modulul __main__:

pătrat(x)
Calculează pătratul numărului x.

Întâlnești din nou funcția de ajutor în capitolul 10.

Funcții care nu sunt cu adevărat funcții


Funcțiile, în sens matematic, returnează întotdeauna ceva care este calculat din parametrii lor.
În Python, unele funcții nu returnează nimic. În alte limbaje (cum ar fi Pascal), astfel de funcții pot fi numite alte lucruri
(cum ar fi proceduri), dar în Python, o funcție este o funcție, chiar dacă din punct de vedere tehnic nu este. Funcțiile care
nu returnează nimic pur și simplu nu au o declarație return. Sau, dacă au instrucțiuni return, nu există nicio valoare după
cuvântul return.

def test():
print('Acesta este tipărit') return
print('Acesta nu este')

Aici, instrucțiunea return este folosită pur și simplu pentru a încheia funcția.

>>> x = test()
Aceasta este tipărită

După cum puteți vedea, a doua declarație de tipărire este omisă. (Acesta este un pic ca folosirea break in bucle, cu excepția
faptului că ieși din funcție.) Dar dacă testul nu returnează nimic, la ce se referă x? Sa vedem:

>>> x
>>>

Nimic acolo. Să ne uităm puțin mai de aproape.

>>> print(x)
Nici unul

104
Machine Translated by Google

Capitolul 6 Abstrac ia

Aceasta este o valoare familiară: Niciuna. Deci toate funcțiile returnează ceva ; doar că se întorc Nici unul când
nu le spui ce să se întoarcă.

Atenție Nu lăsați acest comportament implicit să vă declanșeze. Dacă returnați valori din interiorul declarațiilor if și altele

asemenea, asigurați-vă că ați acoperit fiecare caz, astfel încât să nu returnați accidental None atunci când apelantul așteaptă

o secvență, de exemplu.

Magia parametrilor
Utilizarea funcțiilor este destul de simplă și nici crearea lor nu este chiar atât de complicată. Modul în care funcționează
parametrii poate, totuși, să dureze ceva pentru a vă obișnui. Mai întâi, să facem elementele de bază.

De unde vin valorile?


Uneori, atunci când definiți o funcție, vă puteți întreba de unde își iau parametrii valorile.
În general, nu ar trebui să vă faceți griji pentru asta. Scrierea unei funcții este o chestiune de a oferi un
serviciu oricărei părți a programului dvs. (și posibil chiar și altor programe) ar putea avea nevoie de el. Sarcina dvs.
este să vă asigurați că funcția își face treaba dacă este furnizată cu parametri acceptabili și, de preferință, eșuează
într-un mod evident dacă parametrii sunt greșiți. (Faceți acest lucru cu assert sau cu excepții în general. Veți afla mai
multe despre excepții în Capitolul 8.)

Notă Variabilele pe care le scrieți după numele funcției în instrucțiunile def sunt adesea numite parametri formali ai

funcției. Valorile pe care le furnizați atunci când apelați funcția se numesc parametrii actuali sau argumente. În general, nu

voi fi prea pretențios în privința distincției. Dacă este important, voi apela valorile parametrilor actuali pentru a le distinge de

parametrii formali, care sunt mai mult ca variabile.

Pot schimba un parametru?


Deci, funcția dvs. primește un set de valori prin parametrii săi. Le poți schimba? Și ce se întâmplă dacă o faci? Ei bine,
parametrii sunt doar variabile ca toate celelalte, așa că funcționează așa cum v-ați aștepta. Atribuirea unei noi valori
unui parametru în interiorul unei funcții nu va schimba deloc lumea exterioară.

>>> def try_to_change(n): n =


... 'Dl. Gumby
...
>>> nume = 'Dna. Entitate'
>>> try_to_change(nume)
>>> nume
'D-na. Entitate'

105
Machine Translated by Google

Capitolul 6 Abstrac ia

În interiorul try_to_change, parametrul n primește o nouă valoare, dar după cum puteți vedea, asta nu afectează
numele variabilei. La urma urmei, este o cu totul altă variabilă. E ca și cum ai face așa ceva:

>>> nume = 'Dna. Entitate'


>>> n = nume # Acesta este aproape ceea ce se întâmplă la trecerea unui parametru
>>> n = 'Dl. Gumby # Acest lucru se face în interiorul funcției
>>> nume
'D-na. Entitate'

Aici, rezultatul este evident. În timp ce variabila n este schimbată, numele variabilei nu. În mod similar, când
relegați (alocați la) un parametru din interiorul unei funcții, variabilele din afara funcției nu vor fi afectate.

Notă Parametrii sunt păstrați în ceea ce se numește un domeniu local. Scoping este discutat mai târziu în acest capitol.

Șirurile (și numerele și tuplurile) sunt imuabile, ceea ce înseamnă că nu le puteți modifica (adică le puteți înlocui
doar cu valori noi). Prin urmare, nu sunt multe de spus despre ei ca parametri. Dar luați în considerare ce se întâmplă
dacă utilizați o structură de date mutabilă, cum ar fi o listă.

>>> def change(n): n[0]


... = 'Dl. Gumby
...
>>> nume = ['Mrs. Entitate”, „Dna. Lucru'] >>>
schimbare (nume)
>>> nume
['Domnul. Gumby', 'Dna. Lucru']

În acest exemplu, parametrul este modificat. Există o diferență crucială între acest exemplu și cel precedent. În cea
precedentă, pur și simplu am dat variabilei locale o nouă valoare, dar în aceasta, de fapt, modificăm lista la care este
legată numele variabilelor. Sună ciudat? Nu este chiar atât de ciudat.
Să o facem din nou fără apelul de funcție.

>>> nume = ['Mrs. Entitate”, „Dna. Lucru']


>>> n = nume # Din nou prefăcându-vă că trece nume ca parametru
>>> n[0] = „Dl. Gumby' # Schimba lista
>>> nume
['Domnul. Gumby', 'Dna. Lucru']

Ați mai văzut așa ceva. Când două variabile se referă la aceeași listă, acestea . . . consultați aceeași listă.
Este într-adevăr la fel de simplu. Dacă doriți să evitați acest lucru, trebuie să faceți o copie a listei. Când faceți feliere pe
o secvență, felia returnată este întotdeauna o copie. Astfel, dacă faci o felie din întreaga listă, primești o copie.

>>> nume = ['Mrs. Entitate”, „Dna. Lucru'] >>> n =


nume[:]

106
Machine Translated by Google

Capitolul 6 Abstrac ia

Acum n și numele conțin două liste separate (neidentice) care sunt egale.

>>> n este nume


Fals
>>> n == nume
Adevărat

Dacă schimbați n acum (cum ați făcut în cadrul modificării funcției), nu va afecta numele.

>>> n[0] = „Dl. Gumby


>>> n
['Domnul. Gumby', 'Dna. Lucru']
>>> nume
['D-na. Entitate”, „Dna. Lucru']

Să încercăm acest truc cu schimbare.

>>> modificare(nume[:])
>>> nume
['D-na. Entitate”, „Dna. Lucru']

Acum parametrul n conține o copie, iar lista originală este în siguranță.

Notă În cazul în care vă întrebați, numele care sunt locale unei funcții, inclusiv parametrii, nu se confruntă cu numele din
afara funcției (adică cele globale). Pentru mai multe informații despre acest lucru, consultați discuția despre acoperire mai
târziu în acest capitol.

De ce aș dori să-mi modific parametrii?


Utilizarea unei funcții pentru a schimba o structură de date (cum ar fi o listă sau un dicționar) poate fi o modalitate bună de a
introduce abstractizarea în programul dumneavoastră. Să presupunem că doriți să scrieți un program care stochează nume
și care vă permite să căutați oameni după nume, nume sau nume. Puteți utiliza o structură de date ca aceasta:

stocare = {}
stocare['first'] = {}
stocare['middle'] = {}
stocare['last'] = {}

Stocarea structurii de date este un dicționar cu trei chei: „primul”, „de mijloc” și „ultimul”. Sub fiecare dintre aceste taste,
stocați un alt dicționar. În aceste subdicționare, veți folosi nume (primul, mijlocul sau ultimul) ca chei și veți insera liste de
persoane ca valori. De exemplu, pentru a mă adăuga la această structură, puteți face următoarele:

>>> eu = 'Magnus Lie Hetland' >>>


stocare['primul']['Magnus'] = [eu] >>> stocare['middle']
['Lie'] = [eu] >>> stocare ['ultimul']['Hetland'] = [eu]

107
Machine Translated by Google

Capitolul 6 Abstrac ia

Sub fiecare cheie, stocați o listă de persoane. În acest caz, listele mă conțin doar pe mine.
Acum, dacă doriți o listă cu toate persoanele înregistrate care au al doilea nume Lie, puteți face următoarele:

>>> stocare['middle']['Lie']
[„Magnus Lie Hetland”]

După cum puteți vedea, adăugarea de persoane la această structură este puțin plictisitoare, mai ales când obțineți mai multe
persoane cu același nume, nume sau nume de familie, pentru că atunci trebuie să extindeți lista care este deja stocată sub
acel nume. Să adăugăm sora mea și să presupunem că nu știi ce este deja stocat în baza de date.

>>> my_sister = 'Anne Lie Hetland' >>>


storage['first'].setdefault('Anne', []).append(my_sister) >>>
storage['middle'].setdefault('Lie', []).append(sora_mea) >>>
stocare['ultimul'].setdefault('Hetland', []).append(sora_mea) >>> stocare['primul']
['Anne']
['Anne Lie Hetland'] >>>
stocare['middle']['Lie']
[„Magnus Lie Hetland”, „Anne Lie Hetland”]

Imaginați-vă că scrieți un program mare, plin cu actualizări ca acesta. Ar deveni rapid destul de greu de manevrat.
Scopul abstracției este să ascundeți toate detaliile sângeroase ale actualizărilor și puteți face asta cu funcții.
Să facem mai întâi o funcție pentru a inițializa o structură de date.

def init(date):
data['first'] = {}
data['middle'] = {}
data['last'] = {}

În codul precedent, pur și simplu am mutat instrucțiunile de inițializare în interiorul unei funcții. Îl poți folosi astfel:

>>> stocare = {} >>>


init(storage) >>>
stocare {'middle': {},
'last': {}, 'first': {}}

După cum puteți vedea, funcția s-a ocupat de inițializare, făcând codul mult mai lizibil.

Notă Cheile unui dicționar nu au o ordine specifică, așa că atunci când un dicționar este tipărit, ordinea
poate varia. Dacă ordinea este diferită în interpretul dvs., nu vă faceți griji.

Înainte de a scrie o funcție pentru stocarea numelor, să scriem una pentru a le obține.

căutare def (date, etichetă, nume):


date returnate[label].get(nume)

108
Machine Translated by Google

Capitolul 6 Abstrac ia

Cu căutarea, puteți lua o etichetă (cum ar fi „mijloc”) și un nume (cum ar fi „Minciuna”) și puteți obține o listă cu nume complete
returnate. Cu alte cuvinte, presupunând că numele meu a fost stocat, puteți face acest lucru:

>>> căutare (stocare, „mijloc”, „Minciună”)


[„Magnus Lie Hetland”]

Este important de observat că lista care este returnată este aceeași listă care este stocată în structura de date. Deci, dacă
modificați lista, modificarea afectează și structura datelor. (Acesta nu este cazul dacă nu sunt găsite persoane; atunci pur și
simplu returnați Nimic.)
Acum este timpul să scrieți funcția care stochează un nume în structura dvs. (nu vă faceți griji dacă nu face
simt imediat pentru tine).

def store(date, full_name):


names = full_name.split() if
len(names) == 2: names.insert(1, '') labels = 'primul',
'mijlocul', 'ultimul'

pentru etichetă, nume în zip(etichete, nume): persoane


= căutare (date, etichetă, nume) dacă persoane:
persoane.append(nume_complet) else:
date[etichetă][nume] = [nume_complet]

Funcția de stocare efectuează următorii pași:

1. Introduceți funcția cu parametrii date și full_name setate la niște valori pe care le primiți din
lumea exterioară.

2. Îți faci o listă numită nume împărțind full_name.

3. Dacă lungimea numelor este 2 (aveți doar un prenume și un nume de familie), introduceți un șir
gol ca mijloc de nume.

4. Stocați șirurile „primul”, „mijlocul” și „ultimul” ca un tuplu în etichete. (Ați putea folosi cu siguranță
o listă aici; este convenabil să lăsați parantezele.)

5. Utilizați funcția zip pentru a combina etichetele și numele, astfel încât acestea să se alinieze
corect, iar pentru fiecare pereche (etichetă, nume), faceți următoarele:

•Preluați lista care aparține etichetei și numelui date.

•Adăugați full_name la acea listă sau inserați o nouă listă dacă este necesar.

Hai să-l încercăm:

>>> MyNames = {} >>>


init(MyNames) >>>
store(MyNames, 'Magnus Lie Hetland') >>>
lookup(MyNames, 'middle', 'Lie')
[„Magnus Lie Hetland”]

109
Machine Translated by Google

Capitolul 6 Abstrac ia

Se pare că funcționează. Să mai încercăm câteva.

>>> magazin(Numele mele, „Robin Hood”)


>>> magazin(Numele mele, „Robin Locksley”)
>>> căutare(Numele mele, „primul”, „Robin”)
['Robin Hood', 'Robin Locksley'] >>>
magazin(MyNames, 'Mr. Gumby') >>>
Lookup(MyNumes, 'middle', '')
['Robin Hood', 'Robin Locksley', 'Mr. Gumby']

După cum puteți vedea, dacă mai multe persoane au același prenume, al doilea nume sau de familie, le puteți recupera pe toate împreună.

Notă Acest tip de aplicație este potrivită pentru programarea orientată pe obiecte, care este explicată în capitolul
următor.

Ce se întâmplă dacă parametrul meu este imuabil?


În unele limbi (cum ar fi C++, Pascal și Ada), relegarea parametrilor și efectuarea acestor modificări afectează
variabilele din afara funcției este un lucru de zi cu zi. În Python, nu este direct posibil; puteți modifica doar obiectele
parametri în sine. Dar dacă aveți un parametru imuabil, cum ar fi un număr?
Îmi pare rău, dar nu se poate. Ceea ce ar trebui să faceți este să returnați toate valorile de care aveți nevoie de la
funcția dvs. (ca un tuplu, dacă există mai multe). De exemplu, o funcție care crește valoarea numerică a unei variabile
cu una ar putea fi scrisă astfel:

>>> def inc(x): returnează x + 1


...
>>> foo = 10
>>> foo = inc(foo) >>>
foo
11

Dacă doriți cu adevărat să vă modificați parametrul, puteți folosi un truc, cum ar fi includerea valorii într-o listă, ca
acesta:

>>> def inc(x): x[0] = x[0] + 1


...
>>> foo = [10] >>>
inc(foo) >>> foo
[11]

Pur și simplu returnarea noii valori ar fi o soluție mai curată, totuși.

110
Machine Translated by Google

Capitolul 6 Abstrac ia

Parametrii și valorile implicite ale cuvintelor cheie

Parametrii pe care i-am folosit până acum se numesc parametri de poziție deoarece pozițiile lor sunt importante
– mai importante decât numele lor, de fapt. Tehnicile introduse în această secțiune vă permit să ocoliți cu totul
pozițiile și, deși poate dura ceva timp pentru a vă obișnui, veți vedea rapid cât de utile sunt pe măsură ce
programele dvs. cresc în dimensiune.
Luați în considerare următoarele două funcții:

def hello_1(felicitare, nume): print('{},


{}!'.format(salut, nume))

def hello_2(nume, salut): print('{},


{}!'.format(nume, salut))

Amândoi fac exact același lucru, doar cu numele parametrilor inversate.

>>> hello_1(„Bună ziua”, „lume”)


Salut Lume! >>>
hello_2('Bună ziua', 'lume')
Salut Lume!

Uneori (mai ales dacă aveți mulți parametri) ordinea poate fi greu de reținut. Pentru a ușura lucrurile, puteți
furniza numele parametrului dvs.

>>> hello_1(salut='Salut', nume='lume')


Salut Lume!

Ordinea de aici nu contează deloc.

>>> hello_1(nume='lume', salut='Bună ziua')


Salut Lume!

Cu toate acestea, numele o fac (după cum probabil ați adunat).

>>> hello_2(greeting='Hello', name='world') world,


Hello!

Parametrii care sunt furnizați cu un nume ca acesta se numesc parametri de cuvinte cheie. Pe cont propriu, punctul forte al
parametrilor cheie este că pot ajuta la clarificarea rolului fiecărui parametru. În loc să fie nevoie să folosești un apel ciudat și
misterios ca acesta:

>>> magazin(„Mr. Brainsample”, 10, 20, 13, 5)

ai putea folosi asta:

>>> magazin(pacient='Mr. Brainsample', ora=10, minut=20, ziua=13, luna=5)

Chiar dacă este nevoie de puțin mai multă tastare, este absolut clar ce face fiecare parametru. De asemenea,
dacă comandă amestecă, nu contează.

111
Machine Translated by Google

Capitolul 6 Abstrac ia

Cu toate acestea, ceea ce face ca argumentele cuvintelor cheie să fie stânjenitoare este că puteți da parametrilor
din funcție valori implicite.

def hello_3(salut='Salut', nume='lumea'): print('{},


{}!'.format(felicitare, nume))

Când un parametru are o valoare implicită ca aceasta, nu trebuie să-l furnizați atunci când apelați funcția! Nu puteți
furniza niciuna, unele sau toate, după cum vă dictează situația.

>>> salut_3()
Salut Lume! >>>
hello_3(„Salutări”)
Salutări, lume! >>>
hello_3('Salut', 'univers')
Salutări, univers!

După cum puteți vedea, acest lucru funcționează bine cu parametrii de poziție, cu excepția faptului că trebuie să
furnizați salutul dacă doriți să furnizați numele. Ce se întâmplă dacă doriți să furnizați doar numele, lăsând valoarea
implicită pentru salut? Sunt sigur că ai ghicit până acum.

>>> salut_3(nume='Gumby')
Bună, Gumby!

Destul de ingenios, nu? Și asta nu este tot. Puteți combina parametrii de poziție și de cuvinte cheie. Singura
cerință este ca toți parametrii de poziție să fie pe primul loc. Dacă nu, interpretul nu va ști care sunt (adică ce poziție
ar trebui să aibă).

Notă Dacă nu știți ce faceți, ați putea dori să evitați amestecarea parametrilor poziționali și ai cuvintelor
cheie. Această abordare este utilizată în general atunci când aveți un număr mic de parametri obligatorii și
mulți parametri de modificare cu valori implicite.

De exemplu, funcția noastră salut ar putea necesita un nume, dar ne permite (opțional) să specificăm salutul și
semnele de punctuație.

def hello_4(nume, salut='Salut', punctuație='!'):


print('{}, {}{}'.format(felicitare, nume, punctuație))

Această funcție poate fi apelată în mai multe moduri. Aici sunt câțiva dintre ei:

>>> hello_4('Marte')
Bună, Marte!
>>> hello_4('Marte', 'Bună')
Bună, Marte!
>>> hello_4('Marte', 'Bună', '...')
Bună, Marte...
>>> hello_4('Marte', punctuație='.')
Bună, Marte.
>>> hello_4('Marte', salut='Ceful dimineții pentru tine')
Primul dimineață pentru tine, Marte!
>>> salut_4()
112
Machine Translated by Google

Capitolul 6 Abstrac ia

Traceback (cel mai recent apel ultimul):


Fișierul „<stdin>”, linia 1, în <modul>
TypeError: hello_4() lipsește 1 argument de poziție necesar: „nume”

Notă Dacă aș fi dat nume și o valoare implicită, ultimul exemplu nu ar fi ridicat o excepție.

Este destul de flexibil, nu-i așa? Și nici nu a fost nevoie să facem mare lucru pentru a o atinge. În secțiunea următoare
devenim și mai flexibili.

Colectarea parametrilor Uneori poate

fi util să se permită utilizatorului să furnizeze orice număr de parametri. De exemplu, în programul de stocare a
numelor (descris în secțiunea „De ce aș dori să-mi modific parametrii?” mai devreme în acest capitol), puteți stoca doar
un nume odată. Ar fi bine să poți stoca mai multe nume, astfel:

>>> magazin (date, nume1, nume2, nume3)

Pentru ca acest lucru să fie util, ar trebui să aveți voie să furnizați câte nume doriți. De fapt, este foarte posibil.

Încercați următoarea definiție a funcției:

def print_params(*params):
print(params)

Aici, se pare că specific un singur parametru, dar are o stea mică ciudată (sau asterisc) în fața lui. Ce inseamna asta? Să
apelăm funcția cu un singur parametru și să vedem ce se întâmplă.

>>> print_params('Testare')
(„Testare”,)

Puteți vedea că ceea ce este imprimat este un tuplu pentru că are o virgulă în el. Deci, folosirea unei stea în fața
unui parametru îl pune într-un tuplu? Pluralul din params ar trebui să dea un indiciu despre ce se întâmplă.

>>> print_params(1, 2, 3) (1, 2, 3)

Steaua din fața parametrului pune toate valorile în același tuplu. Îi adună, ca să spunem așa. Și bineînțeles că am văzut
exact acest comportament în capitolul anterior, în discuția despre despachetarea secvenței. În atribuiri, variabila cu stea
colectează valori de prisos într-o listă, mai degrabă decât într-un tuplu, dar în afară de asta, cele două utilizări sunt destul
de asemănătoare. Să scriem o altă funcție:

def print_params_2(titlu, *params): print(titlu)


print(params)

si incearca:

>>> print_params_2('Params:', 1, 2, 3)
Parametrii:

(1, 2, 3)

113
Machine Translated by Google

Capitolul 6 Abstrac ia

Deci steaua înseamnă „Adună restul parametrilor de poziție”. Dacă nu dați niciun parametru de
adunat, parametrii vor fi un tuplu gol.

>>> print_params_2('Nimic:')
Nimic: ()

La fel ca în cazul atribuirilor, parametrul marcat cu stea poate apărea în alte poziții decât ultima. Spre deosebire
de sarcini, totuși, trebuie să faceți ceva muncă suplimentară și să specificați parametrii finali după nume.

>>> def în_mijloc(x, *y, z): print(x, y,


... z)
...
>>> în_mijloc(1, 2, 3, 4, 5, z=7) 1 (2, 3, 4, 5)
7 >>> în_mijloc(1, 2, 3, 4, 5, 7)

Traceback (cel mai recent apel ultimul):


Fișierul „<stdin>”, linia 1, în <modul>
TypeError: in_the_middle() lipsește 1 argument obligatoriu numai pentru cuvinte cheie: „z”

Steaua nu colectează argumente ale cuvintelor cheie.

>>> print_params_2('Hmm...', ceva=42)


Traceback (cel mai recent apel ultimul):
Fișierul „<stdin>”, linia 1, în <modul>
TypeError: print_params_2() a primit un argument cheie neașteptat „ceva”

Pe cei cu steaua dublă îi putem aduna .

>>> def print_params_3(**params):


... print(params)
...
>>> print_params_3(x=1, y=2, z=3) {'z':
3, 'x': 1, 'y': 2}

După cum puteți vedea, obținem un dicționar mai degrabă decât un tuplu. Aceste tehnici diferite funcționează bine împreună.

def print_params_4(x, y, z=3, *pospar, **keypar): print(x,


y, z) print(pospar) print(keypar)

Acest lucru funcționează exact așa cum era de așteptat.

>>> print_params_4(1, 2, 3, 5, 6, 7, foo=1, bar=2)


123
(5, 6, 7)
{'foo': 1, 'bar': 2} >>>
print_params_4(1, 2)
123
()
{}

114
Machine Translated by Google

Capitolul 6 Abstrac ia

Combinând toate aceste tehnici, poți face destul de multe. Dacă vă întrebați cum ar putea funcționa o combinație (sau
dacă este permisă), încercați-o! (În secțiunea următoare, veți vedea cum pot fi utilizate * și
o funcție,
** atunci
indiferent
când estedacă
apelată
au și
fost utilizate în definiția funcției.)
Acum, reveniți la problema inițială: cum puteți utiliza acest lucru în exemplul de stocare a numelui. Soluția este
prezentată aici:

def store(date, *nume_complete):


pentru nume_complet în
nume_complete: names =
full_name.split() if len(names) == 2: names.insert(1,
'') labels = 'primul', 'middle', 'last' pentru etichetă,
nume în zip( etichete, nume): persoane =
căutare(date, etichetă, nume) dacă persoane:
persoane.append(nume_complet) else:
date[etichetă][nume] = [nume_complet]

Utilizarea acestei funcții este la fel de ușoară ca și versiunea anterioară, care accepta un singur nume.

>>> d = {}
>>> init(d) >>>
store(d, „Han Solo”)

Dar acum poți face și asta:

>>> magazin(d, „Luke Skywalker”, „Anakin Skywalker”) >>>


căutare(d, „ultimul”, „Skywalker”)
[„Luke Skywalker”, „Anakin Skywalker”]

Inversarea procesului Acum ați

învățat despre adunarea parametrilor în tupluri și dicționare, dar de fapt este posibil să faceți și „opusul”, cu aceiași
doi operatori, * și **. Care ar putea fi opusul culegerii parametrilor? Să presupunem că avem următoarea funcție
disponibilă:

def add(x, y):


returnează x + y

Notă Pute i găsi o versiune mai eficientă a acestei func ii în modulul operator .

De asemenea, să presupunem că aveți un tuplu cu două numere pe care doriți să le adăugați.

parametri = (1, 2)

115
Machine Translated by Google

Capitolul 6 Abstrac ia

Acesta este mai mult sau mai puțin opusul a ceea ce am făcut anterior. În loc să adunăm parametrii, vrem să -i
distribuim . Acest lucru se face pur și simplu prin utilizarea operatorului
funcția, mai *
degrabă
la „celălalt
decâtcapăt”
atunci
- adică
cândatunci
o definiți.
când apelați

>>> add(*params)
3

Acest lucru funcționează și cu părți dintr-o listă de parametri, atâta timp cât partea extinsă este ultima.
Puteți folosi aceeași tehnică cu dicționarele,înainte,
folosindputeți
operatorul **. Presupunând că ați definit hello_3 ca
face următoarele:

>>> params = {'nume': 'Domnule Robin', 'felicitare': 'Bine întâlnit'}


>>> hello_3(**params)
Bine cunoscut, Sir Robin!

Folosind
* (sau
direct,
**)așa
atâtcă
când
ați putea
definiți
la cât
fel de
și când
bine apelați
să nu văfuncția
fi deranjat.
va trece pur și simplu tuplul sau dicționarul

>>> def with_stars(**kwds):


... print(kwds['nume'], 'este', kwds['varsta'], 'ani')
...
>>> def without_stars(kwds):
... print(kwds['nume'], 'este', kwds['varsta'], 'ani')
...
>>> args = {'nume': 'Dl. Gumby', 'varsta': 42} >>>
cu_stele(**args)
Domnul Gumby are 42 de
ani >>> without_stars(args)
Domnul Gumby are 42 de ani

După cum puteți vedea, în with_stars, folosesc stele atât la definirea, cât și la apelarea funcției. În fără_ stele, nu
folosesc stelele în niciunul dintre locuri, dar obțin exact același efect. Așadar, stelele sunt cu adevărat utile doar dacă
le folosiți fie atunci când definiți o funcție (pentru a permite un număr variabil de argumente), fie când apelați o
funcție (pentru a „încărca” un dicționar sau o secvență).

Sfat Poate fi util să folosiți acești operatori de îmbinare pentru a „trece” parametrii, fără să vă faceți prea multe griji cu privire la câți

sunt și așa mai departe. Iată un exemplu:

def foo(x, y, z, m=0, n=0):


print(x, y, z, m, n) def
call_foo(*args, **kwds):
print("Apelam foo!") foo
(*args, **kwds)

Acest lucru poate fi deosebit de util atunci când se apelează constructorul unei superclase (vezi Capitolul 9 pentru mai multe despre asta).

116
Machine Translated by Google

Capitolul 6 Abstrac ia

Practica parametrilor
Cu atât de multe moduri de a furniza și de a primi parametrii, este ușor să fii confuz. Așa că permiteți-mi să leg
totul împreună cu un exemplu. Mai întâi, să definim câteva funcții.

def story(**kwds):
'
return „A fost odată ca niciodată un „{job} numit \
{name}.’.format_map(kwds)

def power(x, y, *others): if


others: print('Received
redundant parameters:', others) return pow(x, y)

def interval(start, stop=None, step=1):


„Imită range() pentru pasul > 0” dacă
stop este Niciunul: start, stop = 0, start # Dacă opritorul nu este furnizat... #
result = [] amestecați parametrii

i = începe # Începem să numărăm la indicele de start # Până


în timp ce i < stop: când indicele ajunge la indicele de oprire ... # ... adăugați
rezultat.append(i) i indicele
0) la rezultat ... ... incrementați indicele cu pasul (>
+= pas returnează #
rezultat

Acum hai să le încercăm.

>>> print(povestea(job='rege', nume='Gumby'))


A fost odată ca niciodată un rege numit Gumby. >>>
print(povestea(nume='Sir Robin', job='cavaler curajos')))
A fost odată ca niciodată un cavaler curajos numit Sir Robin. >>> params =
{'job': 'language', 'name': 'Python'} >>> print(story(**params))

A fost odată un limbaj numit Python. >>> del params['job'] >>>


print(story(job='stroke of genius', **params))

A fost odată ca niciodată o lovitură de geniu numită Python. >>> putere(2, 3) 8

>>> putere(3, 2) 9

>>> putere(y=3, x=2) 8

>>> parametri = (5,) * 2 >>>


putere(*params) 3125

>>> putere (3, 3, „Bună, lume”)


Parametri redundanți primiți: („Bună ziua, lume”,) 27

117
Machine Translated by Google

Capitolul 6 Abstrac ia

>>> interval(10) [0,


1, 2, 3, 4, 5, 6, 7, 8, 9] >>> interval(1, 5)
[1, 2, 3, 4] >>> interval (3, 12, 4) [3, 7,
11] >>> putere(*interval(3, 7))

Parametri redundanți primiți: (5, 6) 81

Simțiți-vă liber să experimentați cu aceste funcții și funcții proprii până când sunteți sigur că înțelegeți cum
funcționează aceste lucruri.

Scoping
Ce sunt variabilele, de fapt? Vă puteți gândi la ele ca la nume care se referă la valori. Deci, după atribuirea x = 1,
numele x se referă la valoarea 1. Este aproape ca și cum ați folosi dicționare, în care cheile se referă la valori, cu
excepția faptului că utilizați un dicționar „invizibil”. De fapt, acest lucru nu este departe de adevăr. Există o funcție
încorporată numită vars, care returnează acest dicționar:

>>> x = 1
>>> scope = vars() >>>
scope['x'] 1

>>> domeniu ['x'] += 1


>>> x
2

Atenție În general, nu trebuie să modificați dicționarul returnat de vars deoarece, conform documentației
oficiale Python, rezultatul este nedefinit. Cu alte cuvinte, s-ar putea să nu obții rezultatul pe care îl urmărești.

Acest tip de „dicționar invizibil” se numește spațiu de nume sau domeniu. Deci, câte spații de nume există? Pe
lângă domeniul global, fiecare apel de funcție creează unul nou.

>>> def foo(): x = 42


...
>>> x = 1
>>> foo()
>>> x
1

Aici foo schimbă (relegă) variabila x, dar când te uiți la ea până la urmă, nu s-a schimbat până la urmă.
Asta pentru că atunci când apelați foo, este creat un nou spațiu de nume, care este folosit pentru blocul din interiorul
foo. Atribuirea x = 42 este efectuată în acest domeniu interior ( spațiul de nume local ) și, prin urmare, nu afectează x în
domeniul exterior (global) . Variabilele care sunt utilizate în cadrul unor funcții ca aceasta se numesc variabile locale (spre
deosebire de variabilele globale). Parametrii funcționează la fel ca variabilele locale, deci nu există nicio problemă în a
avea un parametru cu același nume ca o variabilă globală.

118
Machine Translated by Google

Capitolul 6 Abstrac ia

>>> def output(x): print(x)


...
>>> x = 1
>>> y = 2
>>> output(y)
2

Până acum, bine. Dar dacă doriți să accesați variabilele globale din interiorul unei funcții? Atâta timp
cât vrei să citești doar valoarea variabilei (adică nu vrei să o relegați), în general nu există nicio problemă.

>>> def combine(parametru): print(parametru + extern)


...
>>> extern = 'berry'
>>> combine('Arbust')
Arbust

Atenție Referirea la variabile globale ca aceasta este o sursă a multor erori. Utilizați variabilele globale cu grijă.

PROBLEMA Umbririi

Citirea valorii variabilelor globale nu este o problemă în general, dar un lucru o poate face
problematică. Dacă există o variabilă sau un parametru local cu același nume ca și variabila globală pe care
doriți să o accesați, nu o puteți face direct. Variabila globală este umbrită de cea locală.

Dacă este necesar, puteți obține în continuare acces la variabila globală utilizând funcția globals, o rudă
apropiată a vars, care returnează un dicționar cu variabilele globale. (locals returnează un dicționar cu
variabilele locale.)

De exemplu, dacă ați avut o variabilă globală numită parametru în exemplul anterior, nu ați putea să
o accesați din combinație deoarece aveți un parametru cu același nume. Într-un pic, totuși, ați fi putut
să vă referiți la el ca globals()['parametru'].

>>> def combine(parametru):


... print(parametru + globals()['parametru'])
...
>>> parametru = 'berry'
>>> combine('Arbust')
Arbust

119
Machine Translated by Google

Capitolul 6 Abstrac ia

Relegarea variabilelor globale (făcându-le să se refere la o valoare nouă) este o altă chestiune. Dacă atribuiți o
valoare unei variabile în interiorul unei funcții, aceasta devine automat locală, dacă nu îi spuneți lui Python altfel.
Și cum crezi că îi poți spune să facă o variabilă globală?

>>> x = 1
>>> def change_global():
... global xx = x + 1
...
...
>>> change_global()
>>> x
2

Bucată de tort!

SCOPE IMPRIMATE

Funcțiile Python pot fi imbricate - puteți pune una în alta. Iată un exemplu:

def foo():
def bar():
print("Bună ziua, lume!")
bar()

Imbricarea nu este în mod normal atât de utilă, dar există o aplicație specială care iese în evidență: utilizarea unei funcții
pentru a „crea” alta. Aceasta înseamnă că puteți (printre altele) să scrieți funcții precum următoarele:

def multiplicator(factor):
def multiplyByFactor(number):
returneaza numarul * factor
returnează multiplicareByFactor

O funcție este în interiorul alteia, iar funcția exterioară returnează cea interioară; adică funcția în sine este returnată —
nu este apelată. Ceea ce este important este că funcția returnată are încă acces la domeniul în care a fost definită; cu
alte cuvinte, își poartă mediul (și variabilele locale asociate) cu el!

De fiecare dată când funcția exterioară este apelată, cea interioară este redefinită și de fiecare dată, factorul variabil poate
avea o nouă valoare. Din cauza domeniilor imbricate ale lui Python, această variabilă din domeniul local exterior (a
multiplicatorului) este accesibilă mai târziu în funcția interioară, după cum urmează:

>>> dublu = multiplicator(2) >>>


dublu(5) 10

>>> triplu = multiplicator(3) >>>


triplu(3) 9 >>> multiplicator(5)
(4) 20

120
Machine Translated by Google

Capitolul 6 Abstrac ia

O funcție, cum ar fi multiplyByFactor , care stochează domeniul de aplicare, se numește închidere.

În mod normal, nu puteți relega variabilele din domeniile exterioare. Dacă doriți, totuși, puteți utiliza cuvântul
cheie nonlocal . Este folosit în același mod ca global și vă permite să atribuiți variabilelor în domenii exterioare
(dar neglobale).

Recursiune
Ați învățat multe despre crearea de funcții și apelarea lor. De asemenea, știți că funcțiile pot apela alte funcții. Ceea ce ar
putea fi o surpriză este că funcțiile se pot numi singure.
Dacă nu ați mai întâlnit așa ceva înainte, vă puteți întreba ce este acest cuvânt recursivitate .
Înseamnă pur și simplu să te referi la (sau, în cazul nostru, „a te suni”) la tine însuți. O definiție obișnuită (deși prostească)
este așa:

recursivitate \ri-'k&r-zh&n\ n: vezi recursivitate.

Dacă căutați „recursiune” în Google, veți vedea ceva similar.


Definițiile recursive (inclusiv definițiile de funcții recursive) includ referințe la termenul pe care îl definesc. În funcție de
cantitatea de experiență pe care o aveți cu el, recursiunea poate fi fie uluitoare, fie destul de simplă. Pentru o înțelegere mai
profundă a acesteia, probabil că ar trebui să vă cumpărați un manual bun de informatică, dar jocul cu interpretul Python vă
poate ajuta cu siguranță.
În general, nu vrei definiții recursive precum cea pe care am dat-o pentru cuvântul recursivitate, pentru că nu vei ajunge
nicăieri. Căutați recursiunea, care vă spune din nou să căutați recursiunea și așa mai departe. O definiție similară a funcției
ar fi

def recursion():
returnează recursiunea()

Este evident că acest lucru nu face nimic - este la fel de prostesc ca și definiția simulată a dicționarului. Dar ce se
întâmplă dacă îl rulezi? Ești binevenit să încerci. Veți descoperi că programul pur și simplu se blochează (creează o
excepție) după un timp. Teoretic, ar trebui pur și simplu să funcționeze pentru totdeauna. Cu toate acestea, de fiecare dată
când o funcție este apelată, aceasta consumă puțină memorie și, după ce au fost efectuate suficiente apeluri de funcție
(înainte ca apelurile anterioare să se întoarcă), nu mai este loc, iar programul se termină cu mesajul de eroare adâncimea
maximă a recursiunii depășit.
Tipul de recursivitate pe care îl aveți în această funcție se numește recursivitate infinită (la fel ca o buclă care
începe cu while True și care nu conține instrucțiuni de pauză sau returnare este o buclă infinită) , deoarece nu se termină
niciodată (în teorie). Ceea ce vrei este o funcție recursivă care face ceva util. O funcție recursivă utilă constă de obicei din
următoarele părți:

• Un caz de bază (pentru cea mai mică problemă posibilă) când funcția returnează o valoare
direct

•Un caz recursiv, care conține unul sau mai multe apeluri recursive pe părți mai mici ale
problemă

Ideea aici este că, prin împărțirea problemei în bucăți mai mici, recursiunea nu poate continua pentru totdeauna,
deoarece ajungeți întotdeauna cu cea mai mică problemă posibilă, care este acoperită de cazul de bază.
Deci aveți o funcție care se autoapelează. Dar cum este posibil asta? Nu este chiar atât de ciudat pe cât ar părea. După
cum am spus mai devreme, de fiecare dată când este apelată o funcție, este creat un nou spațiu de nume pentru acel apel
specific. Asta înseamnă că atunci când o funcție se numește „în sine”, de fapt vorbiți despre două funcții diferite (sau, mai
degrabă, aceeași funcție cu două spații de nume diferite). S-ar putea să vă gândiți la ea ca la o creatură dintr-o anumită specie
care vorbește cu o alta din aceeași specie.

121
Machine Translated by Google

Capitolul 6 Abstrac ia

Două clasice: factorial și putere

În această secțiune, examinăm două funcții recursive clasice. În primul rând, să presupunem că doriți să calculați factorialul
unui număr n. Factorialul lui n este definit ca n × (n–1) × (n–2) × aplicații (de. exemplu,
. . × 1. Este
înfolosit
calcularea câte matematică
în multe moduri diferite
există de a pune n oameni într-o linie).
Cum o calculezi? Ai putea întotdeauna să folosești o buclă.

def factorial(n): rezultat


=n
pentru i în intervalul (1, n):
rezultat *= i
returnează rezultatul

Aceasta funcționează și este o implementare simplă. Practic, ceea ce face este: mai întâi, setează rezultatul la n; apoi,
rezultatul este înmulțit cu fiecare număr de la 1 la n–1 pe rând; în cele din urmă, returnează rezultatul. Dar poți face asta
altfel, dacă vrei. Cheia este definiția matematică a factorialului, care poate fi enunțată după cum urmează:

• Factorialul lui 1 este 1.

•Factorialul unui număr n mai mare decât 1 este produsul lui n și factorialul lui n–1.

După cum puteți vedea, această definiție este exact echivalentă cu cea dată la începutul acestei secțiuni.
Acum luați în considerare modul în care implementați această definiție ca funcție. De fapt, este destul de simplu, o dată
înțelegi definiția în sine.

def factorial(n): dacă n


== 1:
întoarce 1
altceva:
întoarcere n * factorial (n - 1)

Aceasta este o implementare directă a definiției. Nu uitați că funcția de apel factorial(n) este o entitate diferită de apelul
de factorial(n - 1).
Să luăm în considerare un alt exemplu. Să presupunem că doriți să calculați puteri, la fel ca funcția încorporată pow
sau operatorul **. Puteți defini puterea (întreg) a unui număr în mai multe moduri diferite, dar să începem cu unul simplu:
puterea (x, n) (x la puterea lui n) este numărul x înmulțit cu el însuși n - 1 ori ( astfel încât x este folosit ca factor de n ori). Cu
alte cuvinte, puterea (2, 3) este 2 înmulțită cu ea însăși de două ori, sau 2 × 2 × 2 = 8.
Acest lucru este ușor de implementat.

putere def (x, n):


rezultat = 1
pentru i în intervalul(n):
rezultat *= x
returnează rezultatul

O mică funcție dulce și simplă, dar din nou puteți schimba definiția într-una recursivă:

•puterea(x, 0) este 1 pentru toate numerele x.

•puterea(x, n) pentru n > 0 este produsul dintre x și puterea(x, n - 1).

122
Machine Translated by Google

Capitolul 6 Abstrac ia

Din nou, după cum puteți vedea, acest lucru dă exact același rezultat ca în definiția mai simplă, iterativă.

Înțelegerea definiției este cea mai grea parte – implementarea acesteia este ușoară.

def putere(x, n): dacă


n == 0:
returnează
1 else: *
putere(x, n - 1) returnează x

Din nou, pur și simplu am tradus definiția mea dintr-o descriere textuală ușor formală într-un limbaj de programare (Python).

Sfat Dacă o funcție sau un algoritm este complex și greu de înțeles, definirea clară cu propriile cuvinte
înainte de implementarea efectivă a acestuia poate fi de mare ajutor. Programele din acest tip de „limbaj de
aproape programare” sunt adesea denumite pseudocod.

Deci, care este rostul recursiunii? Nu poți folosi pur și simplu bucle în schimb? Adevărul este că da, poți și, în majoritatea
cazurilor, probabil va fi (cel puțin puțin) mai eficient. Dar, în multe cazuri, recursiunea poate fi mai lizibilă – uneori mult
mai lizibilă – mai ales dacă se înțelege definiția recursivă a unei funcții. Și chiar dacă ați putea evita să scrieți vreodată o
funcție recursivă, ca programator, cel mai probabil, va trebui să înțelegeți algoritmii recursivi și funcțiile create de alții, cel
puțin.

Un alt clasic: Căutare binară


Ca exemplu final de recursivitate în practică, să aruncăm o privire la algoritmul numit căutare binară.
Probabil știți de jocul în care ar trebui să ghiciți la ce se gândește cineva punând 20 de întrebări da sau nu. Pentru a
profita la maximum de întrebările dvs., încercați să reduceți numărul de posibilități la (mai mult sau mai puțin) jumătate. De
exemplu, dacă știi că subiectul este o persoană, ai putea întreba: „Te gândești la o femeie?” Nu începi prin a întreba: „Te
gândești la John Cleese?” dacă nu ai o bănuială foarte puternică.
O versiune a acestui joc pentru cei mai înclinați numeric este să ghicească un număr. De exemplu, partenerul tău se gândește
la un număr între 1 și 100 și trebuie să ghiciți care este acesta. Desigur, ai putea face asta în 100 de ghiciri, dar de câte ai
nevoie cu adevărat?
După cum se dovedește, aveți nevoie de doar șapte întrebări. Prima este ceva de genul „Este numărul mai mare de
50?” Dacă este, atunci întrebați: „Este mai mare de 75?” Continuați să înjumătățiți intervalul (împărțind diferența) până când
găsiți numărul. Puteți face asta fără să vă gândiți prea mult.
Aceeași tactică poate fi folosită în multe contexte diferite. O problemă comună este de a afla dacă un număr trebuie
găsit într-o secvență (sortată) și chiar de a afla unde se află. Din nou, urmați aceeași procedură: „Numărul este în dreapta
mijlocului secvenței?” Dacă nu este, „Este în al doilea sfert (în dreapta mijlocului jumătății stângi)?” si asa mai departe.
Păstrați o limită superioară și o limită inferioară unde poate fi numărul și continuați să împărțiți acel interval în două cu
fiecare întrebare.

123
Machine Translated by Google

Capitolul 6 Abstrac ia

Ideea este că acest algoritm se pretează în mod natural la o definiție și implementare recursivă. hai sa
revizuiți mai întâi definiția, pentru a vă asigura că știm ce facem:
• Dacă limitele superioare și inferioare sunt aceleași, ambele se referă la poziția corectă a
numărului, așa că returnați-l.

• În caz contrar, găsiți mijlocul intervalului (media superioare și inferioare


legat) și aflați dacă numărul este în jumătatea dreaptă sau stângă. Continuați să căutați în
jumătatea potrivită.

Cheia cazului recursiv este că numerele sunt sortate, așa că atunci când ați găsit elementul din mijloc, îl
puteți compara cu numărul pe care îl căutați. Dacă numărul tău este mai mare, atunci trebuie să fie la dreapta, iar
dacă este mai mic, trebuie să fie la stânga. Partea recursivă este „Păstrați căutarea în jumătatea potrivită”, deoarece
căutarea va fi efectuată exact în modul descris în definiție. (Rețineți că algoritmul de căutare returnează poziția în care
ar trebui să fie numărul - dacă nu este prezent în secvență, această poziție va fi, în mod natural, ocupată de un alt
număr.)
Acum sunteți gata să implementați o căutare binară.

def search(secvență, număr, inferior, superior): dacă


mai jos == sus: afirmă numărul == secvență[sus]
returnează sus else: mijloc = (jos + sus) // 2
dacă număr > secvență[mijloc]: întoarce
căutare (secvență, număr, mijloc + 1, sus) altfel:
returnează căutare (secvență, număr, inferior,
mijloc)

Acest lucru face exact ceea ce a spus definiția că ar trebui: dacă inferior == superior, atunci reveniți în sus, care
este limita superioară. Rețineți că presupuneți (afirmați) că numărul pe care îl căutați (numărul) a fost de fapt găsit
(număr == secvență[sus]). Dacă nu ați ajuns încă la cazul dvs. de bază, găsiți mijlocul, verificați dacă numărul dvs. este
la stânga sau la dreapta și apelați căutare recursiv cu noi limite. Puteți chiar să faceți acest lucru mai ușor de utilizat
făcând ca specificațiile limită să fie opționale. Pur și simplu dați valori implicite inferioare și superioare și adăugați
următoarele condiționale la începutul definiției funcției:

def search(secvență, număr, inferior=0, superior=Niciun): dacă


superior este Nici unul: superior = len(secvență) - 1
...

Acum, dacă nu furnizați limitele, acestea sunt setate la prima și ultima poziție a secvenței. Să vedem dacă funcționează.

>>> seq = [34, 67, 8, 123, 4, 100, 95] >>> seq.sort()


>>> seq [4, 8, 34, 67, 95, 100, 123] >>> căutare
(secv. 34)

2
>>> căutare (seq, 100)
5

124
Machine Translated by Google

Capitolul 6 Abstrac ia

Dar de ce să mergi la toate aceste probleme? În primul rând, puteți utiliza pur și simplu indexul metodei listă și, dacă
doriți să implementați acest lucru singur, puteți face o buclă începând de la început și repetă până când găsiți numărul.

Sigur, folosirea indexului este bine. Dar utilizarea unei bucle simple poate fi puțin ineficientă. Îți amintești că
am spus că ai nevoie de șapte întrebări pentru a găsi un număr (sau o poziție) dintre 100? Și, evident, bucla are
nevoie de 100 de întrebări în cel mai rău caz. „Este mare”, spui tu. Dar dacă lista are 100.000.000.000.000.000.000.000,0
00.000.000.000 de elemente și are același număr de întrebări cu o buclă (poate o dimensiune oarecum nerealistă pentru
o listă Python), acest gen de lucruri începe să conteze. Căutarea binară ar avea nevoie de doar 117 întrebări.
Destul de eficient, nu?2

Sfat Puteți găsi de fapt o implementare standard a căutării binare în modulul bisect .

FUNCȚII DE ARUNCAT împrejur

Până acum, probabil că sunteți obișnuit să utilizați funcții la fel ca și alte obiecte (șiruri de caractere, număr,
secvențe și așa mai departe) atribuindu-le variabilelor, transmițându-le ca parametri și returnându-le de la alte
funcții. Unele limbaje de programare (cum ar fi Scheme sau Lisp) folosesc funcții în acest fel pentru a realiza
aproape totul. Chiar dacă de obicei nu vă bazați atât de mult pe funcțiile din Python (de obicei, vă creați
propriile tipuri de obiecte - mai multe despre asta în capitolul următor), puteți.

Python are câteva funcții care sunt utile pentru acest tip de „programare funcțională”: mapare, filtrare și
reducere. Funcțiile de hartă și de filtrare nu sunt chiar atât de utile în versiunile curente de Python și,
probabil, ar trebui să utilizați în schimb listele de înțelegere. Puteți folosi harta pentru a trece toate
elementele unei secvențe printr-o funcție dată.

>>> list(map(str, range(10))) # Echivalent cu [str(i) pentru i în interval(10)]


['0', '1', '2', '3', '4 „, „5”, „6”, „7”, „8”, „9”]

Folosiți filtrul pentru a filtra elementele bazate pe o funcție booleană.

>>> def func(x):


... return x.isalnum()
...
>>> seq = ["foo", "x41", "?!", "***"] >>>
list(filter(func, seq)) ['foo', 'x41']

Pentru acest exemplu, utilizarea unei liste de înțelegere ar însemna că nu trebuie să definiți funcția personalizată.

>>> [x pentru x în seq if x.isalnum()]


['foo', 'x41']

2
De fapt, cu numărul estimat de particule din universul observabil la 1087, veți avea nevoie doar de aproximativ 290 de
întrebări pentru a discerne între ele!

125
Machine Translated by Google

Capitolul 6 Abstrac ia

De fapt, există o caracteristică numită expresii lambda,3 care vă permite să definiți funcții simple în linie
(utilizate în principal cu harta, filtrarea și reducerea).

>>> filter(lambda x: x.isalnum(), seq) ['foo', 'x41']

Nu cumva, totuși, înțelegerea listei este mai lizibilă?

Funcția de reducere nu poate fi înlocuită cu ușurință de liste de înțelegere, dar probabil că nu veți
avea nevoie de funcționalitatea ei atât de des (dacă vreodată). Combină primele două elemente ale unei
secvențe cu o funcție dată, combină rezultatul cu al treilea element și așa mai departe, până când întreaga
secvență a fost procesată și rămâne un singur rezultat. De exemplu, dacă doriți să însumați toate numerele
unei secvențe, puteți utiliza reduce cu lambda x, y: x+y (folosind în continuare aceleași numere).4

>>> numere = [72, 101, 108, 108, 111, 44, 32, 119, 111, 114, 108, 100, 33] >>> din functools import
reduce >>> reduce(lambda x, y: x + y, numere) 1161

Desigur, aici ați putea la fel de bine să utilizați suma funcției încorporată.

Un rezumat rapid
În acest capitol, ați învățat câteva lucruri despre abstractizare în general și despre funcții în special:

Abstracția: Abstracția este arta de a ascunde detaliile inutile. Puteți face programul mai
abstract definind funcții care se ocupă de detalii.

Definiția funcției: Funcțiile sunt definite cu instrucțiunea def. Sunt blocuri de instrucțiuni
care primesc valori (parametri) din „lumea exterioară” și pot returna una sau mai multe
valori ca rezultat al calculului lor.

Parametri: Funcțiile primesc ceea ce trebuie să știe sub formă de parametri -


variabile care sunt setate atunci când funcția este apelată. Există două tipuri de parametri
în Python: parametri de poziție și parametri de cuvinte cheie.
Parametrii pot fi opționali dându-le valori implicite.

Domenii: Variabilele sunt stocate în domenii (numite și spații de nume). Există două
domenii principale în Python: domeniul global și domeniul local. Scopurile pot fi imbricate.

Recursie: o funcție se poate autodenomina și, dacă o face, se numește recursie.


Tot ceea ce puteți face cu recursiunea se poate face și prin bucle, dar uneori o funcție recursivă
este mai ușor de citit.

Programare funcțională: Python are unele facilități pentru programare într-un stil
funcțional. Printre acestea se numără expresiile lambda și funcțiile map, filter and reduce.

3 Numele „lambda” provine din litera greacă, care este folosită în matematică pentru a indica o funcție anonimă.
4
De fapt, în loc de această funcție lambda, puteți importa funcția add din modulul operator , care are o funcție
pentru fiecare dintre operatorii încorporați. Utilizarea funcțiilor din modulul operator este întotdeauna mai eficientă
decât utilizarea propriilor funcții.

126
Machine Translated by Google

Capitolul 6 Abstrac ia

Funcții noi în acest capitol


Func ie Descriere
map(func, seq[, seq, ...]) filter(func, Aplică funcția tuturor elementelor din secvențe

seq) reduce(func, seq[, initial]) Returnează o listă a acelor elemente pentru care funcția este adevărată

sum(seq) apply(func[, args[, kwargs]]) Echivalent cu func(func(func(seq[0], seq[1]), seq[2]), ...)

Returnează suma tuturor elementelor seq

Apelează funcția, furnizând opțional argument

Ce acum?
Următorul capitol duce abstracțiile la un alt nivel, prin programarea orientată pe obiecte. Învățați cum să vă creați
propriile tipuri (sau clase) de obiecte pe care să le utilizați alături de cele furnizate de Python (cum ar fi șiruri de
caractere, liste și dicționare) și învățați cum acest lucru vă permite să scrieți programe mai bune. Odată ce ți-ai
parcurs următorul capitol, vei putea scrie niște programe foarte mari fără să te pierzi în codul sursă.

127
Machine Translated by Google

CAPITOLUL 7

Mai multă abstracție

În capitolele anterioare, v-ați uitat la principalele tipuri de obiecte încorporate ale lui Python (numere, șiruri, liste, tupluri și
dicționare); ați aruncat o privire la bogăția de funcții încorporate și biblioteci standard; și chiar ți-ai creat propriile funcții.
Acum, un singur lucru pare să lipsească - să-ți faci propriile obiecte. Și asta faci în acest capitol.

S-ar putea să vă întrebați cât de util este acest lucru. Ar putea fi tare să-ți faci propriile tipuri de obiecte, dar ce ar fi
le folosesti pentru? Cu toate dicționarele și secvențele și numerele și șirurile disponibile, nu poți să le folosești și să faci
ca funcțiile să facă treaba? Bineînțeles, dar crearea propriilor obiecte (și în special tipuri sau clase de obiecte) este un
concept central în Python - atât de central, de fapt, încât Python este numit un limbaj orientat pe obiecte (împreună cu
Smalltalk, C++, Java și multe altele) . În acest capitol, înveți cum să faci obiecte. Înveți despre polimorfism și încapsulare,
metode și atribute, superclase și moștenire - înveți multe. Deci sa începem.

Notă Dacă sunteți deja familiarizat cu conceptele de programare orientată pe obiecte, probabil că știți
despre constructori. Constructorii nu vor fi tratați în acest capitol; pentru o discuție completă, vezi capitolul 9.

Magia obiectelor
În programarea orientată pe obiecte, termenul obiect înseamnă în mod vag o colecție de date (atribute) cu un set de
metode de accesare și manipulare a acestor date. Există mai multe motive pentru a folosi obiecte în loc să rămâneți cu
variabile și funcții globale. Unele dintre cele mai importante beneficii ale obiectelor includ următoarele:

• Polimorfism: Puteți utiliza aceleași operații pe obiecte din clase diferite și


vor funcționa ca „prin magie”.

• Încapsulare: ascundeți detalii neimportante despre modul în care funcționează obiectele din exterior
lume.

• Moștenire: Puteți crea clase specializate de obiecte din cele generale.

În multe prezentări ale programării orientate pe obiecte, ordinea acestor concepte este diferită.
Încapsularea și moștenirea sunt prezentate mai întâi, iar apoi sunt folosite pentru a modela obiecte din lumea reală.
Toate astea sunt bune, dar în opinia mea, cea mai interesantă caracteristică a programării orientate pe obiecte este
polimorfismul. Este, de asemenea, caracteristica care derutează majoritatea oamenilor (din experiența mea). Prin urmare,
încep cu polimorfismul și încerc să arăt că acest concept singur ar trebui să fie suficient pentru a vă face să vă placă
programarea orientată pe obiecte.

© Magnus Lie Hetland 2017 129


ML Hetland, Beginning Python, DOI 10.1007/978-1-4842-0028-5_7
Machine Translated by Google

Capitolul 7 Mai multă abstractizare

Polimorfism
Termenul polimorfism este derivat dintr-un cuvânt grecesc care înseamnă „având mai multe forme”. Practic, asta
înseamnă că, chiar dacă nu știți la ce fel de obiect se referă o variabilă, este posibil să puteți efectua operațiuni pe ea
care vor funcționa diferit în funcție de tipul (sau clasa) obiectului. De exemplu, să presupunem că creați un sistem de plată
online pentru un site web comercial care vinde produse alimentare. Programul dvs. primește un „coș de cumpărături” cu
mărfuri din altă parte a sistemului (sau alte sisteme similare care ar putea fi proiectate în viitor) — tot ce trebuie să vă
faceți griji este să însumați totalul și să facturați un card de credit.
Primul tău gând ar putea fi să specifici exact cum trebuie să fie reprezentate mărfurile atunci când programezi
le primește. De exemplu, este posibil să doriți să le primiți ca tupluri, astfel:

(„SPAM”, 2.50)

Dacă tot ce aveți nevoie este o etichetă descriptivă și un preț, este bine. Dar nu este foarte flexibil. Să presupunem că o
persoană inteligentă începe un serviciu de licitație ca parte a site-ului web – unde prețul unui articol este redus treptat până
când cineva îl cumpără. Ar fi bine dacă utilizatorul ar putea pune obiectul în coșul ei de cumpărături, ar putea trece la
finalizarea comenzii (partea dvs. din sistem) și să aștepte până când prețul a fost corect înainte de a face clic pe butonul Plătiți.
Dar asta nu ar funcționa cu schema de tuplu simplă. Pentru ca acest lucru să funcționeze, obiectul ar trebui să fie verificat
prețul său actual (printr-o magie de rețea) de fiecare dată când codul dvs. a cerut prețul - nu putea fi înghețat ca
într-un tuplu. Puteți rezolva asta făcând o funcție.

# Nu face asta...
def get_price(obiect): if
isinstance(obiect, tuplu): return
object[1] else:

return magic_network_method(obiect)

Notă Verificarea tip/clasă și utilizarea isinstance aici sunt menite să ilustreze un punct - și anume că
verificarea tipului nu este în general o soluție satisfăcătoare. Evitați verificarea tipului dacă este posibil. Funcția
isinstance este descrisă în secțiunea „Investigarea moștenirii” mai târziu în acest capitol.

În codul precedent, folosesc funcția isinstance pentru a afla dacă obiectul este un tuplu. Dacă este, al doilea element
al său este returnat; în caz contrar, se numește o metodă de rețea „magică”.
Presupunând că elementele din rețea există deja, ați rezolvat problema—deocamdată. Dar acest lucru încă nu
este foarte flexibil. Ce se întâmplă dacă un programator inteligent decide că va reprezenta prețul ca un șir cu o valoare
hexadecimală, stocată într-un dicționar sub cheia „preț”? Nicio problemă, doar actualizați funcția.

# Nu face asta...
def get_price(obiect): if
isinstance(obiect, tuplu): return
object[1] elif isinstance(obiect,
dict): return int(obiect['preț']) else:

return magic_network_method(obiect)

130
Machine Translated by Google

Capitolul 7 Mai multă abstractizare

Acum, sigur că ați acoperit toate posibilitățile? Dar să presupunem că cineva decide să adauge un nou tip de dicționar
cu prețul stocat sub o cheie diferită. Ce faci acum? Cu siguranță ai putea actualiza get_price din nou, dar pentru cât
timp ai putea continua să faci asta? De fiecare dată când cineva a vrut să implementeze un obiect cu preț diferit, ar
trebui să vă reimplementați modulul. Dar dacă ați vândut deja modulul și ați trece la alte proiecte mai interesante –
ce ar face clientul atunci? În mod clar, acesta este un mod inflexibil și nepractic de a codifica diferitele comportamente.

Deci ce faci in schimb? Lasă obiectele să se ocupe singure de operațiune. Sună cu adevărat evident, dar
gândește-te cât de mult vor deveni lucrurile. Fiecare tip de obiect nou își poate prelua sau calcula propriul preț și ți-l
poate returna — tot ce trebuie să faci este să-l ceri. Și aici intră în scenă polimorfismul (și, într-o oarecare măsură,
încapsularea).

Polimorfism și metode Primești un obiect și nu ai

idee despre cum este implementat - poate avea oricare dintre multele „forme”. Tot ce știi este că îi poți cere prețul și îți
este suficient. Modul în care faci asta ar trebui să fie familiar.

>>> object.get_price()
2.5

Funcțiile care sunt legate de atribute de obiect ca acesta sunt numite metode. Le-ați întâlnit deja sub formă de metode
șir, listă și dicționar. Și acolo ați văzut ceva polimorfism.

>>> 'abc'.count('a')
1
>>> [1, 2, 'a'].count('a')
1

Dacă ai avea o variabilă x, nu ar fi nevoie să știi dacă este un șir sau o listă pentru a apela metoda de numărare — ar
funcționa indiferent (atâta timp cât ai furnizat un singur caracter ca argument).
Să facem un experiment. Modulul bibliotecă standard random conține o funcție numită choice that
selectează un element aleatoriu dintr-o secvență. Să folosim asta pentru a da o valoare variabilei tale.

>>> din alegerea de import aleatoriu


>>> x = alegere(['Bună, lume!', [1, 2, 'e', 'e', 4]])

După efectuarea acestui lucru, x poate conține fie șirul „Bună ziua, lume!” sau lista [1, 2, „e”, „e”, 4] — nu știți și nu
trebuie să vă faceți griji pentru asta. Tot ce vă pasă este de câte ori găsiți „e” în x și puteți afla asta indiferent dacă x este
o listă sau un șir. Apelând metoda numărării ca înainte, afli tocmai asta.

>>> x.count('e')
2

În acest caz, se pare că lista a câștigat. Dar ideea este că nu ai nevoie să verifici. Singura dvs. cerință a fost ca
x să aibă o metodă numită count care ia un singur caracter ca argument și a returnat un număr întreg. Dacă altcineva și-
ar fi creat propria clasă de obiecte care avea această metodă, nu ar conta pentru tine - ai putea să-i folosești obiectele la
fel de bine ca șirurile și listele.

131
Machine Translated by Google

Capitolul 7 Mai multă abstractizare

Polimorfismul vine în multe forme


Polimorfismul este la lucru de fiecare dată când poți „face ceva” unui obiect fără a fi nevoie să știi exact ce fel de obiect este.
Acest lucru nu se aplică doar metodelor – am folosit deja polimorfismul mult sub formă de operatori și funcții încorporate. Luați
în considerare următoarele:

>>> 1 + 2
3
>>> „Pește” + „licență”
„Licență de pește”

Aici, operatorul plus (+) funcționează bine atât pentru numere (întregi în acest caz) cât și pentru șiruri (precum și pentru alte
tipuri de secvențe). Pentru a ilustra ideea, să presupunem că doriți să faceți o funcție numită adunare care a adăugat două lucruri
împreună. Puteți să o definiți pur și simplu astfel (echivalent cu, dar mai puțin eficient decât, funcția de adăugare din modulul
operator):

def add(x, y):


returnează x + y

Acest lucru ar funcționa și cu multe tipuri de argumente.

>>> adaugă (1, 2)


3
>>> add('Pește', 'licen ă')
„Licență de pește”

Acest lucru poate părea o prostie, dar ideea este că argumentele pot fi orice care sprijină adăugarea. 1 Dacă doriți să scrieți
o funcție care imprimă un mesaj despre lungimea unui obiect, tot ceea ce este necesar este ca aceasta să aibă o lungime (că
funcția len va funcționa pe ea).

def length_message(x):
print("Lungimea lui", repr(x), "este", len(x))

După cum puteți vedea, funcția folosește și repr, dar repr este unul dintre marii maeștri ai polimorfismului - funcționează cu
orice. Să vedem cum:

>>> length_message('Fnord')
Lungimea lui „Fnord” este 5 >>>
length_message([1, 2, 3])
Lungimea lui [1, 2, 3] este 3

Multe funcții și operatori sunt polimorfi – probabil că majoritatea dintre ai tăi vor fi, de asemenea, chiar dacă nu
intenționezi să fie. Doar folosind funcții și operatori polimorfi, polimorfismul „se îndepărtează”. De fapt, practic, singurul
lucru pe care îl puteți face pentru a distruge acest polimorfism este să faceți verificarea explicită a tipului cu funcții precum
type sau issubclass. Dacă puteți, chiar ar trebui să evitați distrugerea polimorfismului în acest fel.
Ceea ce contează ar trebui să fie că un obiect acționează așa cum doriți, nu dacă este de tipul (sau clasa) potrivit. Totuși, ordinul
împotriva verificării tipului nu este la fel de absolută ca odinioară. Odată cu introducerea claselor de bază abstracte și a modulului
abc, discutate mai târziu în acest capitol, funcția issubclass în sine a devenit polimorfă!

Rețineți că aceste obiecte trebuie să sprijine adăugarea între ele. Deci, apelarea add(1, 'licence') nu ar funcționa.
1

132
Machine Translated by Google

Capitolul 7 Mai multă abstractizare

Notă Forma de polimorfism discutată aici, care este atât de centrală pentru modul Python de programare,
este uneori numită tipărire duck. Termenul derivă din expresia „Dacă șarlatana ca o rață…” Pentru mai multe
informații, consultați http://en.wikipedia.org/wiki/Duck_typing.

Încapsulare Încapsularea

este principiul de a ascunde detaliile inutile de restul lumii. Acest lucru poate suna ca polimorfism - și acolo, folosiți un obiect fără
a-i cunoaște detaliile interioare. Cele două concepte sunt similare deoarece ambele sunt principii ale abstractizării. Ambele vă ajută
să vă ocupați de componentele programului fără să vă pese de detalii inutile, la fel ca și funcțiile.

Dar încapsularea nu este același lucru cu polimorfismul. Polimorfismul vă permite să apelați metodele
a unui obiect fără a-i cunoa te clasa (tipul de obiect). Încapsularea vă permite să utilizați obiectul fără să vă faceți griji cu
privire la modul în care este construit. Mai suna asemanator? Să construim un exemplu cu polimorfism dar fără încapsulare. Să
presupunem că aveți o clasă numită OpenObject (veți învăța cum să creați clase mai târziu în acest capitol).

>>> o = OpenObject() # Așa creăm obiecte... >>> o.set_name('Sir Lancelot') >>>


o.get_name()

„Domnule Lancelot”

Creați un obiect (prin apelarea clasei ca și cum ar fi o funcție) și legați variabila o la acesta. Puteți utiliza apoi metodele set_name și
get_name (presupunând că sunt metode care sunt acceptate de clasa OpenObject). Totul pare să funcționeze perfect. Cu toate acestea,
să presupunem că o stochează numele său în variabila globală global_name.

>>> nume_global
„Domnule Lancelot”

Aceasta înseamnă că trebuie să vă faceți griji cu privire la conținutul global_name atunci când utilizați instanțe (obiecte) ale clasei
OpenObject. De fapt, trebuie să te asiguri că nimeni nu o schimbă.

>>> global_name = 'Sir Gumby' >>>


o.get_name()
„Domnule Gumby”

Lucrurile devin și mai problematice dacă încercați să creați mai mult de un OpenObject, deoarece toate se vor încurca cu aceeași
variabilă.

>>> o1 = OpenObject() >>> o2


= OpenObject() >>>
o1.set_name('Robin Hood') >>> o2.get_name()

„Robin Hood”

După cum puteți vedea, setarea numelui unuia setează automat numele celuilalt, nu exact ceea ce doriți.

133
Machine Translated by Google

Capitolul 7 Mai multă abstractizare

Practic, vrei să tratezi obiectele ca fiind abstracte. Când apelați la o metodă, nu doriți să vă faceți griji
orice altceva, cum ar fi să nu deranjeze variabilele globale. Deci, cum poți „încapsula” numele în obiect? Nici o problema. Îl faci un
atribut.
Atributele sunt variabile care fac parte din obiect, la fel ca și metodele; de fapt, metodele sunt aproape ca
atribute legate de funcții. (Veți vedea o diferență importantă între metode și funcții în secțiunea „Atribute, funcții și metode” mai
târziu în acest capitol.) Dacă rescrieți clasa pentru a utiliza un atribut în loc de o variabilă globală și o redenumiți ClosedObject, aceasta
functioneaza astfel:

>>> c = ClosedObject() >>>


c.set_name('Sir Lancelot') >>> c.get_name()

„Domnule Lancelot”

Până acum, bine. Dar, din câte știți, acest lucru ar putea fi încă stocat într-o variabilă globală. Să facem un alt obiect.

>>> r = ClosedObject() >>>


r.set_name('Sir Robin') r.get_name()

„Domnule Robin”

Aici, puteți vedea că noul obiect are numele setat corect, ceea ce probabil este ceea ce vă așteptați. Dar ce s-a întâmplat acum cu
primul obiect?

>>> c.get_name()
„Domnule Lancelot”

Numele este încă acolo! Acest lucru se datorează faptului că obiectul are propria sa stare. Starea unui obiect este descrisă de
atributele sale (cum ar fi numele său, de exemplu). Metodele unui obiect pot modifica aceste atribute. Deci, este ca și cum ați aduna
o grămadă de funcții (metode) și le-ați oferi acces la unele variabile (atribute) unde pot păstra valorile stocate între apelurile de
funcții.
Veți vedea și mai multe detalii despre mecanismele de încapsulare ale lui Python în secțiunea „Privacy Revisited” mai târziu în
capitol.

Mo tenire
Moștenirea este un alt mod de a face față lenei (în sens pozitiv). Programatorii vor să evite să tasteze același cod de mai multe ori. Am
evitat asta mai devreme făcând funcții, dar acum voi aborda o problemă mai subtilă. Ce se întâmplă dacă ai deja o clasă și vrei să faci una
foarte asemănătoare? Poate unul care adaugă doar câteva metode? Când creați această nouă clasă, nu doriți să fie nevoie să copiați tot
codul de la cea veche în cea nouă.

De exemplu, este posibil să aveți deja o clasă numită Shape, care știe să se deseneze pe ecran.
Acum vrei să faci o clasă numită Rectangle, care știe și să se deseneze pe ecran dar care poate, în plus, să-și calculeze propria
zonă. Nu ați dori să faceți toată munca de a crea o nouă metodă de desen când Shape are una care funcționează foarte bine. Deci ce
faci? Lăsați Rectangle să moștenească metodele de la Shape. Puteți face acest lucru în așa fel încât atunci când desenul este apelat pe
un obiect Rectangle, metoda din clasa Shape este apelată automat (vezi secțiunea „Specificarea unei superclase” mai târziu în acest
capitol).

134
Machine Translated by Google

Capitolul 7 Mai multă abstractizare

Clase
Până acum, ai o idee despre ce sunt cursurile – sau s-ar putea să devii nerăbdător să-ți spun cum să fac lucrurile naibii.
Înainte de a sări în detalii tehnice, să aruncăm o privire la ce este o clasă.

Ce este o clasă, exact?


Am aruncat mult cuvantul clasa , folosindu-l mai mult sau mai putin sinonim cu cuvinte precum fel sau tip. În multe
privințe, asta este exact o clasă - un fel de obiect. Toate obiectele aparțin unei clase și se spune că sunt instanțe ale acelei
clase.
Deci, de exemplu, dacă te uiți în afara ferestrei și vezi o pasăre, acea pasăre este o instanță a clasei „păsări”.
Aceasta este o clasă foarte generală (abstractă) care are mai multe subclase; pasărea ta ar putea să aparțină
subclasei „laci”. Vă puteți gândi la clasa „păsări” ca la ansamblul tuturor păsărilor, în timp ce clasa „laci” este doar un
subset al acesteia. Când obiectele aparținând unei clase formează un subset al obiectelor aparținând unei alte clase,
prima se numește subclasă a celei de-a doua. Astfel, „lacărele” este o subclasă de „păsări”. Dimpotrivă, „păsările” este o
superclasă de „laci”.

Notă În vorbirea de zi cu zi, denotăm clase de obiecte cu substantive la plural, cum ar fi „păsări” și „larks”. În Python,
se obișnuiește să se folosească substantive singulare, cu majuscule, cum ar fi Bird și Lark.

Când sunt afirmate astfel, subclasele și superclasele sunt ușor de înțeles. Dar în programarea orientată pe
obiecte, relația subclasă are implicații importante deoarece o clasă este definită prin metodele pe care le suportă. Toate
instanțele unei clase au aceste metode, deci toate instanțele tuturor subclaselor trebuie să le aibă și ele. Definirea
subclaselor este atunci doar o chestiune de definire a mai multor metode (sau, poate, de a trece peste unele dintre cele
existente).
De exemplu, Bird ar putea furniza metoda fly, în timp ce Penguin (o subclasă a Bird) ar putea adăuga metoda
eat_fish. Atunci când faceți o clasă Penguin, probabil că ați dori, de asemenea, să suprascrieți o metodă a superclasei, și
anume metoda fly. Într-o instanță Pinguin, această metodă fie nu ar trebui să facă nimic, fie poate ridica o excepție (vezi
Capitolul 8), având în vedere că pinguinii nu pot zbura.

Notă În versiunile mai vechi de Python, a existat o distincție clară între tipuri și clase. Obiectele încorporate aveau tipuri;
obiectele tale personalizate aveau clase. Puteți crea clase, dar nu și tipuri. În versiunile recente ale Python 2, această
diferență este mult mai puțin pronunțată, iar în Python 3, distincția a fost eliminată.

Fă-ți propriile cursuri În sfârșit, poți să-ți faci

propriile clase! Iată un exemplu simplu:

__metaclass__ = tip # Includeți acest lucru dacă utilizați Python 2

Persoana clasa:

def set_name(self, name):


self.name = nume

135
Machine Translated by Google

Capitolul 7 Mai multă abstractizare

def get_name(self):
return self.name

def greet(self):
print("Bună ziua, lume! Sunt {}.".format(self.name))

Notă Există o diferență între așa-numitele clase în stil vechi și stilul nou. Nu mai există niciun motiv să mai folosiți
clasele de stil vechi, cu excepția faptului că sunt ceea ce obțineți în mod implicit înainte de Python 3. Pentru a obține clase

de stil noi în Python mai vechi, ar trebui să plasați alocația __metaclass__ = type la începutul lui. scriptul sau modulul dvs.
Nu voi include în mod explicit această afirmație în fiecare exemplu. Există și alte soluții, cum ar fi subclasarea unei clase de

stil nou (de exemplu, obiect). Aflați mai multe despre subclasare într-un minut. Dacă utilizați Python 3, nu este nevoie să vă
faceți griji pentru acest lucru, deoarece clasele în stil vechi nu există acolo. Găsiți mai multe informații despre acest lucru în
capitolul 9.

Acest exemplu conține trei definiții de metodă, care sunt ca definițiile de funcție, cu excepția faptului că sunt
scrise în interiorul unei instrucțiuni de clasă. Persoana este, desigur, numele clasei. Instrucțiunea de clasă își creează
propriul spațiu de nume în care sunt definite funcțiile. (Consultați secțiunea „Spațiul de nume al clasei” mai târziu în acest
capitol.) Toate acestea par în regulă, dar vă puteți întreba care este acest parametru de sine. Se referă la obiectul însuși.
Și ce obiect este acesta? Să facem câteva exemple și să vedem.

>>> foo = Person() >>>


bar = Person() >>>
foo.set_name('Luke Skywalker') >>>
bar.set_name('Anakin Skywalker') >>> foo.greet()

Salut Lume! Eu sunt Luke Skywalker. >>>


bar.greet()
Salut Lume! Eu sunt Anakin Skywalker.

Bine, deci acest exemplu poate fi puțin evident, dar poate clarifică ce este sinele. Când apel set_name și salut pe foo,
foo însuși este trecut automat ca prim parametru în fiecare caz - parametrul pe care l-am numit atât de potrivit self. Puteți,
de fapt, să îl numiți cum doriți, dar pentru că este întotdeauna obiectul în sine, este aproape întotdeauna numit sine, prin
convenție.
Ar trebui să fie evident de ce sinele este util, și chiar necesar, aici. Fără el, niciuna dintre metode nu ar avea
acces la obiectul în sine - obiectul ale cărui atribute ar trebui să le manipuleze. Ca și până acum, atributele sunt
accesibile și din exterior.

>>> foo.name
'Luke Skywalker'
>>> bar.name = 'Yoda' >>>
bar.greet()
Salut Lume! Eu sunt Yoda.

Sfat Un alt mod de a vedea acest lucru este că foo.greet() este pur și simplu o modalitate convenabilă de a scrie
Person.greet(foo), mai puțin polimorfă, dacă se întâmplă să știți că foo este o instanță a lui Person.

136
Machine Translated by Google

Capitolul 7 Mai multă abstractizare

Atribute, funcții și metode


Parametrul self (menționat în secțiunea anterioară) este, de fapt, cel care distinge metodele de
funcții. Metodele (sau, mai tehnic, metodele legate ) au primul parametru legat de instanța căreia îi
aparțin, așa că nu trebuie să-l furnizați. Deși cu siguranță puteți lega un atribut la o funcție simplă,
aceasta nu va avea acel parametru special de sine.

>>> clasa clasa:


... def method(self):
... print('Am un sine!')
...
>>> def function():
... print("Eu nu...")
...
>>> instance = Class()
>>> instance.method() Am un eu! >>>
instance.method = function
>>> instance.method() nu...

Rețineți că parametrul self nu depinde de apelarea metodei așa cum am procedat până acum, ca
instance.method. Sunteți liber să utilizați o altă variabilă care se referă la aceeași metodă.

>>> clasa Bird:


... cântec =
... „Squaawk!” def
... sing(self): print(self.song)
...
>>> bird = Bird()
>>> bird.sing()
Squaawk!
>>> birdsong = bird.sing
>>> birdsong()
Squaawk!

Chiar dacă ultimul apel de metodă arată exact ca un apel de funcție, variabila birdsong se referă la
metoda legată bird.sing, ceea ce înseamnă că încă are acces la parametrul self (adică este încă legat
la aceeași instanță a clasă).

Confidențialitate revizuită
În mod implicit, puteți accesa atributele unui obiect din „exterior”. Să revedem exemplul din discuția anterioară despre
încapsulare.

>>> c.nume
„Domnule Lancelot”

>>> c.name = 'Sir Gumby'


>>> c.get_name()
„Domnule Gumby”

137
Machine Translated by Google

Capitolul 7 Mai multă abstractizare

Unii programatori sunt de acord cu acest lucru, dar unii (cum ar fi creatorii Smalltalk, un limbaj în care atributele
unui obiect sunt accesibile doar metodelor aceluiași obiect) consideră că se rupe de principiul încapsulării. Ei cred
că starea obiectului ar trebui să fie complet ascunsă (inaccesibilă) lumii exterioare. S-ar putea să vă întrebați de ce
adoptă o poziție atât de extremă. Nu este suficient ca fiecare obiect să-și gestioneze propriile atribute? De ce ar
trebui să le ascunzi de lume? La urma urmei, dacă tocmai ați folosit atributul name direct pe ClosedObject (clasa c
în acest caz), nu ar fi nevoie să creați metodele setName și getName.

Ideea este că alți programatori ar putea să nu știe (și poate că nu ar trebui să știe) ce se întâmplă în
interiorul obiectului tău. De exemplu, ClosedObject poate trimite un mesaj de e-mail unui administrator de
fiecare dată când un obiect își schimbă numele. Aceasta ar putea face parte din metoda set_name. Dar ce se
întâmplă când setați direct c.name? Nu se întâmplă nimic — nu este trimis niciun mesaj de e-mail. Pentru a evita
astfel de lucruri, aveți atribute private . Acestea sunt atribute care nu sunt accesibile în afara obiectului; sunt
accesibile numai prin metode accesorii , cum ar fi get_name și set_name.

Notă În capitolul 9, învățați despre proprietăți, o alternativă puternică la accesorii.

Python nu acceptă confidențialitatea direct, dar se bazează pe programator pentru a ști când este sigur să
modifici un atribut din exterior. La urma urmei, ar trebui să știi cum să folosești un obiect înainte de a-l folosi. Cu
toate acestea, este posibil să obțineți ceva de genul atributelor private cu puțină șmecherie.
Pentru a face o metodă sau un atribut privat (inaccesibil din exterior), pur și simplu începeți numele cu două
subliniază.

clasa secreta:

def __inaccessible(self):
print(„Pariez că nu mă poți vedea...”)

def accesibil(self):
print("Mesajul secret este:")
self.__inaccessible()

Acum inaccesibil este inaccesibil pentru lumea exterioară, în timp ce poate fi încă folosit în interiorul clasei
(de exemplu, din accesibil).

>>> s = Secretive() >>>


s.__inaccessible()
Traceback (cel mai recent apel ultimul):
Fișierul „<stdin>”, linia 1, în <modul>
AttributeError: instanța secretă nu are atribut „__inaccessible” >>> s.accessible()

Mesajul secret este: Pariez


că nu mă poți vedea...

Deși liniuțele de subliniere duble sunt puțin ciudate, aceasta pare o metodă privată standard, așa cum se găsește în
alte limbi. Ceea ce nu este atât de standard este ceea ce se întâmplă de fapt. În cadrul unei definiții de clasă, toate
numele care încep cu o liniuță de subliniere dublă sunt „traduse” prin adăugarea unui singur caracter de subliniere și a
numelui clasei la început.

>>> Secretive._Secretive__inaccessible <metoda


nelegată Secretive.__inaccessible>

138
Machine Translated by Google

Capitolul 7 Mai multă abstractizare

Dacă știți cum funcționează acest lucru în culise, este totuși posibil să accesați metode private în afara clasei, chiar dacă
nu trebuie.

>>> s._Secretiv__inaccesibil()
Pariez că nu mă poți vedea...

Așadar, pe scurt, nu poți fi sigur că alții nu vor accesa metodele și atributele obiectelor tale, dar acest tip de alterare a
numelui este un semnal destul de puternic că nu ar trebui.
Dacă nu doriți efectul de alterare a numelui, dar doriți totuși să trimiteți un semnal pentru ca alte obiecte să stea
departe, puteți utiliza o singură liniuță inițială. Aceasta este în mare parte doar o convenție, dar are unele efecte practice.
De exemplu, numele cu un caracter de subliniere inițial nu sunt importate cu importuri marcate cu stea (din modulul import *).2

Spațiul de nume al clasei


Următoarele două afirmații sunt (mai mult sau mai puțin) echivalente:

* X
def foo(x): return x foo =
lambda x: x * X

Ambele creează o funcție care returnează pătratul argumentului său și ambele leagă variabila foo la acea
funcție. Numele foo poate fi definit în domeniul global (modulului) sau poate fi local pentru o anumită funcție sau
metodă. Același lucru se întâmplă atunci când definiți o clasă: tot codul din instrucțiunea de clasă este executat într-un
spațiu de nume special - spațiul de nume de clasă. Acest spațiu de nume este accesibil mai târziu de către toți membrii clasei.
Nu toți programatorii Python știu că definițiile claselor sunt pur și simplu secțiuni de cod care sunt executate, dar pot
fi informații utile. De exemplu, nu sunteți restricționat la instrucțiuni def din blocul de definire a clasei.

>>> clasa C:
... print('Clasa C fiind definită...')
...
Clasa C fiind definită...
>>>

Bine, a fost un pic prostesc. Dar luați în considerare următoarele:

clasa MemberCounter:
membri = 0 def
init(self):
MemberCounter.membri += 1

>>> m1 = MemberCounter()
>>> m1.init()
>>> MemberCounter.membri
1
>>> m2 = MemberCounter()
>>> m2.init()
>>> MemberCounter.membri
2

2
Unele limbi acceptă mai multe grade de confidențialitate pentru variabilele membre (atribute). Java, de exemplu, are patru
niveluri diferite. Python nu are cu adevărat un suport echivalent de confidențialitate, deși liniuțele inițiale simple și duble vă oferă
într-o oarecare măsură două niveluri de confidențialitate.

139
Machine Translated by Google

Capitolul 7 Mai multă abstractizare

În codul precedent, în domeniul de aplicare al clasei este definită o variabilă, care poate fi accesată de toți membrii
(instanțele), în acest caz pentru a număra numărul de membri ai clasei. Observați utilizarea lui init pentru a inițializa toate
instanțele: voi automatiza asta (adică, îl voi transforma într-un constructor adecvat) în Capitolul 9.
Această variabilă de domeniu de clasă este accesibilă și din fiecare instanță, la fel ca și metodele.

>>> m1.membri

2 >>> m2.membri
2

Ce se întâmplă când relegați atributul membrilor într-o instanță?

>>> m1.members = „Doi” >>>


m1.members „Doi” >>>
m2.members

Valoarea noilor membri a fost scrisă într-un atribut în m1, umbrind variabila la nivel de clasă. Aceasta oglindește
comportamentul variabilelor locale și globale în funcții, așa cum se discută în bara laterală „Problema umbririi” din capitolul
6.

Specificarea unei superclase După cum

am discutat mai devreme în capitol, subclasele extind definițiile din superclasele lor. Indicați superclasa într-o instrucțiune de
clasă scriind-o între paranteze după numele clasei.

class Filter: def


init(self): self.blocked
= [] def filter(self,
secvență):
returnează [x pentru x în secvență dacă x nu este în sine.blocat]

clasa SPAMFilter(Filter): # SPAMFilter este o subclasă a Filtrului


def init(self): # Ignoră metoda init din superclasa Filter self.blocked = ['SPAM']

Filter este o clasă generală pentru filtrarea secvențelor. De fapt, nu filtrează nimic.

>>> f = Filter() >>>


f.init() >>> f.filter([1,
2, 3]) [1, 2, 3]

Utilitatea clasei Filter este că poate fi folosită ca o clasă de bază (superclasă) pentru alte clase, cum ar fi SPAMFilter, care
filtrează „SPAM”-ul din secvențe.

>>> s = SPAMFilter() >>>


s.init() >>> s.filter(['SPAM',
'SPAM', 'SPAM', 'SPAM', 'ouă', 'slănină', 'SPAM' ']) ['ouă', 'slănină']

140
Machine Translated by Google

Capitolul 7 Mai multă abstractizare

Rețineți aceste două puncte importante în definiția SPAMFilter:


• Supliniez definiția init din Filter pur și simplu oferind o nouă definiție.

•Definiția metodei de filtrare este transferată (este moștenită) de la Filter, deci nu trebuie să
scrieți din nou definiția.

Al doilea punct demonstrează de ce moștenirea este utilă: acum pot face o serie de clase de filtre diferite,
toate subclasând Filter și pentru fiecare pot folosi pur și simplu metoda de filtrare pe care am implementat-
o deja. Vorbește despre lenea utilă. . .

Investigarea moștenirii Dacă doriți să

aflați dacă o clasă este o subclasă a alteia, puteți utiliza metoda încorporată issubclass.

>>> issubclass(SPAMFilter, Filter)


Adevărat

>>> issubclass(Filtru, SPAMFilter)


Fals

Dacă aveți o clasă și doriți să cunoașteți clasele de bază ale acesteia, puteți accesa bazele ei de atribute speciale.

>>> SPAMFilter.__bases__
(<class __main__.Filter at 0x171e40>,)
>>> Filter.__bases__
(<clasa „obiect”>,)

Într-o manieră similară, puteți verifica dacă un obiect este o instanță a unei clase utilizând isinstance.

>>> s = SPAMFilter() >>>


isinstance(e, SPAMFilter)
Adevărat

>>> esteinstanță(e, Filtru)


Adevărat

>>> isinstance(e, str)


Fals

Notă Utilizarea isinstance nu este de obicei o practică bună. A te baza pe polimorfism este aproape întotdeauna mai bine.

Principala excepție este atunci când utilizați clase de bază abstracte și modulul abc .

După cum puteți vedea, s este un membru (direct) al clasei SPAMFilter, dar este și un membru indirect al
Filterului, deoarece SPAMFilter este o subclasă a Filterului. Un alt mod de a spune este că toate filtrele SPAM sunt filtre.
După cum puteți vedea în exemplul precedent, isinstance funcționează și cu tipuri, cum ar fi tipul șir (str).

Dacă doriți doar să aflați cărei clase îi aparține un obiect, puteți utiliza atributul __class__.

>>> s.__class__
<clasa __main__.SPAMFilter la 0x1707c0>

141
Machine Translated by Google

Capitolul 7 Mai multă abstractizare

Notă Dacă aveți o clasă cu stil nou, fie prin setarea __metaclass__ = tip , fie prin subclasarea obiectului, puteți
utiliza și tipul(e) pentru a găsi clasa instanței dumneavoastră. Pentru clasele de stil vechi, type returnează pur și
simplu tipul instanței , indiferent de clasă pentru care este o instanță un obiect.

Superclase multiple Sunt sigur că ați

observat un mic detaliu în secțiunea anterioară care poate părea ciudat: forma pluralului în baze. Am spus că l-ai
putea folosi pentru a găsi clasele de bază ale unei clase, ceea ce înseamnă că poate avea mai multe. Acesta este, de
fapt, cazul. Pentru a arăta cum funcționează, să creăm câteva clase.

clasa Calculator: def


calculate(self, expression): self.value =
eval(expresie)

class Talker: def


talk(self): print('Bună,
valoarea mea este', self.value)

clasă TalkingCalculator(Calculator, Talker):


trece

Subclasa (TalkingCalculator) nu face nimic de la sine; își moștenește tot comportamentul de la superclasele sale.
Ideea este că moștenește atât calculul de la Calculator, cât și conversația de la Talker, făcându-l un calculator
vorbitor.

>>> tc = TalkingCalculator() >>>


tc.calculate('1 + 2 * 3') >>> tc.talk()

Salut, valoarea mea este 7

Aceasta se numește moștenire multiplă și poate fi un instrument foarte puternic. Cu toate acestea, cu excepția cazului în care
știți că aveți nevoie de moștenire multiplă, este posibil să doriți să stați departe de aceasta, deoarece poate duce, în unele cazuri, la
complicații neprevăzute.
Dacă utilizați moștenirea multiplă, trebuie să aveți în vedere un lucru: dacă o metodă este implementată
diferit de două sau mai multe dintre superclase (adică aveți două metode diferite cu același nume), trebuie să aveți
grijă. ordinea acestor superclase (în instrucțiunea de clasă). Metodele din clasele anterioare suprascriu metodele din
cele ulterioare. Deci, dacă clasa Calculator din exemplul precedent ar avea o metodă numită vorbire, ar suprascrie (și
ar face inaccesibilă) metoda de vorbire a Vorbitorului. Inversându-le ordinea, astfel:

clasa TalkingCalculator(Talker, Calculator): trece

ar face accesibilă metoda de vorbire a Vorbitorului. Dacă superclasele împărtășesc o superclasă comună, ordinea în
care superclasele sunt vizitate în timpul căutării unui anumit atribut sau metodă se numește ordinea de rezoluție a
metodei (MRO) și urmează un algoritm destul de complicat. Din fericire, funcționează foarte bine, așa că probabil nu
trebuie să vă faceți griji.

142
Machine Translated by Google

Capitolul 7 Mai multă abstractizare

Interfețe și introspecție
Conceptul de „interfață” este legat de polimorfism. Când manipulați un obiect polimorf, vă pasă doar de interfața lui (sau
„protocol”) – metodele și atributele cunoscute lumii. În Python, nu specificați în mod explicit ce metode trebuie să aibă un
obiect pentru a fi acceptabil ca parametru. De exemplu, nu scrieți interfețele în mod explicit (cum faceți în Java); presupui
doar că un obiect poate face ceea ce îi ceri tu. Dacă nu se poate, programul va eșua.

De obicei, pur și simplu cereți ca obiectele să se conformeze unei anumite interfețe (cu alte cuvinte, implementați
anumite metode), dar dacă doriți, puteți fi destul de flexibil în cerințele dvs. În loc să apelați doar metodele și să sperați
la ce este mai bun, puteți verifica dacă metodele necesare sunt prezente și, dacă nu, poate faceți altceva.

>>> hasattr(tc, 'vorbire')


Adevărat

>>> hasattr(tc, 'fnord')


Fals

În codul precedent, găsiți că tc (un TalkingCalculator, așa cum este descris mai devreme în acest capitol) are atributul
talk (care se referă la o metodă), dar nu și atributul fnord. Dacă doriți, ați putea chiar să verificați dacă atributul Talk poate
fi apelat.

>>> apelabil(getattr(tc, 'vorbire', Nici unul))


Adevărat

>>> apelabil(getattr(tc, 'fnord', None))


Fals

Rețineți că, în loc să folosesc hasattr într-o instrucțiune if și să accesez atributul direct, folosesc getattr, care îmi permite să
furnizez o valoare implicită (în acest caz Niciuna) care va fi folosită dacă atributul nu este prezent. Apoi folosesc callable pe
obiectul returnat.

Notă Inversul lui getattr este setattr, care poate fi folosit pentru a seta atributele unui obiect:

>>> setattr(tc, 'nume', 'domnul Gumby') >>>


tc.name
'Domnul. Gumby

Dacă doriți să vedeți toate valorile stocate într-un obiect, puteți examina atributul __dict__ al acestuia. Și dacă doriți
cu adevărat să aflați din ce este făcut un obiect, ar trebui să aruncați o privire la modulul de inspectare. Este destinat
utilizatorilor destul de avansați care doresc să creeze browsere de obiecte (programe care vă permit să răsfoiți obiectele
Python într-o manieră grafică) și alte programe similare care necesită o astfel de funcționalitate. Pentru mai multe
informații despre explorarea obiectelor și modulelor, consultați secțiunea „Explorarea modulelor” din Capitolul 10.

143
Machine Translated by Google

Capitolul 7 Mai multă abstractizare

Clase de bază abstracte


Cu toate acestea, puteți face mai bine decât verificarea manuală a metodelor individuale. Pentru o mare parte a istoriei
sale, Python s-a bazat aproape exclusiv pe tastarea de rață și doar presupunând că orice obiect pe care l-ați avea și-ar
putea face treaba, poate cu unele verificări folosind hasattr pentru a căuta prezența anumitor metode necesare. Ideea de
interfețe specificate în mod explicit, așa cum se găsește în multe alte limbi, cum ar fi Java și Go, cu unele module terțe
care oferă diverse implementări. În cele din urmă, totuși, soluția oficială Python a venit odată cu introducerea modulului
abc. Acest modul oferă suport pentru așa-numitele clase de bază abstracte. În general, o clasă abstractă este pur și simplu
una care nu poate sau cel puțin nu ar trebui să fie instanțiată. Sarcina sa este de a oferi un set de metode abstracte pe
care subclasele ar trebui să le implementeze. Iată un exemplu simplu:

din abc import ABC, abstractmethod

Clasă vorbitor(ABC):
@abstractmethod
def talk(self):
trece

Utilizarea așa-numiților decoratori care arată ca @this este descrisă mai detaliat în Capitolul 9. Lucrul important aici este
că utilizați @abstractmethod pentru a marca o metodă ca abstractă — o metodă care trebuie implementată într-o
subclasă.

Notă Dacă utilizați versiuni mai vechi de Python, nu veți găsi clasa ABC în modulul abc . Apoi,
trebuie să importați ABCMeta și să plasați linia (indentată) __metaclass__ = ABCMeta la începutul
definiției clasei, chiar sub linia instrucțiunii de clasă . Dacă utilizați Python 3 înainte de 3.4, puteți utiliza
și Talker(metaclass=ABCMeta) în loc de Talker(ABC).

Proprietatea cea mai de bază a unei clase abstracte (adică una cu metode abstracte) este că nu are instanțe.

>>> Vorbitor()
Traceback (cel mai recent apel ultimul):
Fișierul „<stdin>”, linia 1, în <modul>
TypeError: Nu se poate instanția clasa abstractă Talker cu metode abstracte Talk

Să presupunem că îl subclasăm după cum urmează:

clasa Knigget (vorbitor):


trece

Nu am suprascris metoda talk, așa că această clasă este , de asemenea, abstractă și nu poate fi instanțiată. Dacă
încercați, veți primi un mesaj de eroare similar cu cel anterior. Îl putem rescrie, totuși, pentru a implementa metodele
necesare.

clasa Knigget(Talker): def


talk(self): print("Ni!")

144
Machine Translated by Google

Capitolul 7 Mai multă abstractizare

Acum, instanțarea va funcționa bine. Și aceasta este una dintre principalele utilizări ale claselor de bază abstracte
– și poate singura utilizare adecvată a isinstance în Python: dacă verificăm mai întâi că o anumită instanță este într-
adevăr un Talker, putem fi siguri că atunci când avem nevoie de ea, instanța va au metoda vorbirii.

>>> k = Knigget() >>>


isinstance(k, Talker)

Adevarat >>> k.talk()


Nu!

Totuși, încă ne lipsește o parte importantă a imaginii - partea care, așa cum am sugerat mai devreme, face ca
instanța să fie mai polimorfă. Vedeți, mecanismul abstract al clasei de bază ne permite să folosim acest tip de verificare
a instanțelor în spiritul tastării de rață! Nu ne interesează ce ești , doar ce poți face (adică ce metode implementezi).
Deci, dacă implementați metoda Talk, dar nu sunteți o subclasă a Talker, ar trebui să treceți totuși verificarea de tip. Așa
că hai să organizăm o altă clasă.

clasa Hering: def


talk(self):
print("Blub.")

Acest lucru ar trebui să fie bine ca un vorbitor - și totuși, nu este un Vorbitor.

>>> h = Hering() >>>


isinstance(h, Vorbitor)
Fals

Sigur, ai putea pur și simplu subclasa Talker și ai terminat cu el, dar s-ar putea să importați Hering din modulul
altcuiva, caz în care aceasta nu este o opțiune. Mai degrabă decât, să zicem, să creați o subclasă atât pentru Hering,
cât și pentru Talker, puteți pur și simplu să înregistrați Hering ca Talker, după care toți heringii sunt recunoscuți
corespunzător ca vorbitori.

>>> Talker.register(Herring) <clasa


'__main__.Herring'> >>> isinstance(h,
Talker)

Adevărat >>> este subclasa (hering, vorbitor)


Adevărat

Există totuși o potențială slăbiciune aici, care subminează garanțiile pe care le-am văzut atunci când subclasăm
direct o clasă abstractă.

>>> clasa scoici:


... trece
...
>>> Talker.register(Clam) <clasa
'__main__.Clam'> >>>
issubclass(Clam, Talker)

Adevarat >>> c =
Clam() >>> isinstance(c, Talker)
Adevărat

145
Machine Translated by Google

Capitolul 7 Mai multă abstractizare

>>> c.talk()
Traceback (cel mai recent apel ultimul):
Fișierul „<stdin>”, linia 1, în <modul>
AttributeError: obiectul „Clam” nu are atributul „vorbire”

Cu alte cuvinte, faptul că isinstance returnează True ar trebui luat ca o expresie a intenției. În acest caz, Clam este
destinat să fie un Vorbitor. În spiritul tastării de rață, avem încredere că își va face treaba, ceea ce, din păcate, nu
reușește să o facă.

Biblioteca standard oferă mai multe clase de bază abstracte utile, de exemplu în modulul collections.abc. Pentru
mai multe detalii despre modulul abc, consultați referința bibliotecii standard Python.

Câteva gânduri despre proiectarea orientată pe obiecte


S-au scris multe cărți despre proiectarea programelor orientate pe obiecte și, deși nu acesta este punctul central al
acestei cărți, vă voi oferi câteva indicații:

•Strângeți ceea ce aparține împreună. Dacă o funcție manipulează o variabilă globală, cele două ar
putea fi mai bine într-o clasă, ca atribut și metodă.

•Nu lăsați obiectele să devină prea intime. Metodele ar trebui să se preocupe în principal de
atributele propriei instanțe. Lasă alte instanțe să-și gestioneze propriul stat.

• Mergeți ușor cu moștenirea, în special cu moștenirea multiplă. Moștenirea este utilă uneori,
dar poate complica lucrurile inutil în unele cazuri. Și moștenirea multiplă poate fi foarte
dificil de corectat și chiar mai greu de depanat.

•Nu te complica. Păstrați-vă metodele mici. Ca regulă generală, ar trebui să fie posibil să citiți (și să
înțelegeți) majoritatea metodelor dvs. în, de exemplu, 30 de secunde. În rest, încercați să le
păstrați mai scurte decât o pagină sau un ecran.

Când determinați ce clase aveți nevoie și ce metode ar trebui să aibă acestea, puteți încerca ceva de genul acesta:

1. Scrieți o descriere a problemei dvs. (ce ar trebui să facă programul?).


Subliniați toate substantivele, verbele și adjectivele.

2. Treceți prin substantive, căutând clase potențiale.

3. Parcurgeți verbele, căutând posibile metode.

4. Parcurgeți adjectivele, căutând potențiale atribute.

5. Alocați metode și atribute claselor dvs.

Acum aveți o primă schiță a unui model orientat pe obiecte. Poate doriți să vă gândiți și la ce responsabilități
și relații (cum ar fi moștenirea sau cooperarea) vor avea clasele și obiectele. Pentru a vă rafina modelul, puteți face
următoarele:

1. Notează (sau visează) un set de cazuri de utilizare - scenarii despre cum poate programul tău
fi folosit. Încercați să acoperiți toate funcționalitățile.

2. Gândiți-vă la fiecare caz de utilizare pas cu pas, asigurându-vă că tot ceea ce aveți nevoie este
acoperit de modelul dvs. Dacă lipsește ceva, adaugă-l. Dacă ceva nu este în regulă,
schimbați-l. Continuați până când sunteți mulțumit.

146
Machine Translated by Google

Capitolul 7 Mai multă abstractizare

Când aveți un model despre care credeți că va funcționa, puteți începe să piratați. Sunt șanse să fie necesar să vă
revizuiți modelul sau să revizuiți părți ale programului. Din fericire, acest lucru este ușor în Python, așa că nu vă faceți
griji. (Dacă doriți mai multe îndrumări în modalitățile de programare orientată pe obiecte, consultați lista de cărți sugerate
în capitolul 19.)

Un rezumat rapid
Acest capitol v-a oferit mai mult decât informații despre limbajul Python; v-a introdus în mai multe concepte care s-
ar putea să vă fi fost complet străine. Iată un rezumat:

Obiecte: Un obiect este format din atribute și metode. Un atribut este doar o variabilă
care face parte dintr-un obiect, iar o metodă este mai mult sau mai puțin o funcție care
este stocată într-un atribut. O diferență între metodele (legate) și alte funcții este că
metodele primesc întotdeauna obiectul din care fac parte ca prim argument, numit de
obicei self.

Clase: O clasă reprezintă un set (sau un fel) de obiecte, iar fiecare obiect (instanță) are o
clasă. Sarcina principală a clasei este de a defini metodele pe care le vor avea instanțele sale.

Polimorfism: Polimorfismul este caracteristica de a putea trata obiecte de diferite tipuri și


clase la fel - nu trebuie să știți cărei clase îi aparține un obiect pentru a apela una dintre
metodele sale.

Încapsulare: Obiectele își pot ascunde (sau încapsulă) starea lor internă. În unele limbi,
aceasta înseamnă că starea lor (atributele lor) este disponibilă numai prin metodele lor.
În Python, toate atributele sunt disponibile public, dar programatorii ar trebui să fie totuși
atenți la accesarea directă a stării unui obiect, deoarece ar putea, fără să vrea, să facă
starea inconsistentă într-un fel.

Moștenire: o clasă poate fi subclasa uneia sau mai multor alte clase. Subclasa
moștenește apoi toate metodele superclaselor. Puteți utiliza mai mult de o superclasă,
iar această caracteristică poate fi utilizată pentru a compune piese de funcționalitate
ortogonale (independente și fără legătură). O modalitate obișnuită de implementare
este utilizarea unei superclase de bază împreună cu una sau mai multe superclase mix-
in .

Interfețe și introspecție: în general, nu doriți să împingeți un obiect prea profund. Te


bazezi pe polimorfism și apelezi la metodele de care ai nevoie. Cu toate acestea, dacă
doriți să aflați ce metode sau atribute are un obiect, există funcții care vă vor face treaba.

Clase de bază abstracte: Folosind modulul abc, puteți crea așa-numitele clase de bază
abstracte, care servesc la identificarea tipului de funcționalitate pe care ar trebui să o
ofere o clasă, fără a o implementa efectiv.

Design orientat pe obiecte: Există multe opinii despre cum (sau dacă!) să faci design orientat
pe obiecte. Indiferent de locul în care vă aflați în privința problemei, este important să vă
înțelegeți problema în detaliu și să creați un design ușor de înțeles.

147
Machine Translated by Google

Capitolul 7 Mai multă abstractizare

Funcții noi în acest capitol


Func ie Descriere
apelabil (obiect) Stabilește dacă obiectul poate fi apelat (cum ar fi o funcție sau o
metodă)

getattr(obiect, nume[, implicit]) Obține valoarea unui atribut, oferind opțional o valoare implicită

hasattr(obiect, nume) isinstance(obiect, Determină dacă obiectul are atributul dat

clasă) issubclass(A, B) Determină dacă obiectul este o instanță a clasei

aleatoriu.choice(secvență) setattr(obiect, Determină dacă A este o subclasă a lui B

nume, valoare) tip(obiect) Alege un element aleatoriu dintr-o secvență nevidă

Setează atributul dat al obiectului la valoare

Returnează tipul obiectului

Ce acum?
Ați învățat multe despre crearea propriilor obiecte și cât de util poate fi. Înainte de a vă scufunda cu capul cap în magia
metodelor speciale ale lui Python (Capitolul 9), haideți să luăm o pauză cu un mic capitol despre gestionarea excepțiilor.

148
Machine Translated by Google

CAPITOLUL 8

Excepții

Atunci când scrieți programe pentru calculator, de obicei este posibil să discerneți între un curs normal al evenimentelor
și ceva excepțional (ieșit din comun). Astfel de evenimente excepționale pot fi erori (cum ar fi încercarea de a împărți un
număr la zero) sau pur și simplu ceva la care s-ar putea să nu vă așteptați să se întâmple foarte des. Pentru a gestiona
astfel de evenimente excepționale, puteți folosi condiționale oriunde ar putea avea loc evenimentele (de exemplu,
solicitați programului să verifice dacă numitorul este zero pentru fiecare diviziune). Cu toate acestea, acest lucru nu
numai că ar fi ineficient și inflexibil, dar ar face și programele ilizibile. Ați putea fi tentat să ignorați aceste evenimente
excepționale și să sperați doar să nu se producă, dar Python oferă un mecanism de gestionare a excepțiilor ca alternativă
puternică.
În acest capitol, veți învăța cum să creați și să ridicați propriile excepții, precum și cum să gestionați
excepții în diverse moduri.

Ce este o excepție?
Pentru a reprezenta condiții excepționale, Python folosește obiecte excepționale. Când întâlnește o eroare, ridică o
excepție. Dacă un astfel de obiect excepție nu este gestionat (sau prins), programul se termină cu un așa-numit traceback
(un mesaj de eroare).

>>> 1/0
Traceback (cel mai recent apel ultimul):
fișierul „<stdin>”, linia 1, în ?
ZeroDivisionError: diviziune întreagă sau modulo cu zero

Dacă astfel de mesaje de eroare ar fi tot pentru care ai putea folosi excepții, nu ar fi foarte interesante. Faptul este,
totuși, că fiecare excepție este o instanță a unei clase (în acest caz, ZeroDivisionError), iar aceste instanțe pot fi ridicate
și capturate în diferite moduri, permițându-vă să captați eroarea și să faceți ceva în privința ei în loc să lăsați doar întregul
program eșuează.

A face lucrurile să meargă greșit. . . Calea ta


După cum ați văzut, excepțiile sunt ridicate automat atunci când ceva nu este în regulă. Înainte de a analiza cum să tratați
aceste excepții, să aruncăm o privire asupra modului în care puteți ridica singuri excepții – și chiar să vă creați propriile
tipuri de excepții.

© Magnus Lie Hetland 2017 149


ML Hetland, Beginning Python, DOI 10.1007/978-1-4842-0028-5_8
Machine Translated by Google

Capitolul 8 Excep ii

Declarația de ridicare

Pentru a ridica o excepție, utilizați instrucțiunea raise cu un argument care este fie o clasă (care ar trebui să subclaseze
Excepție) fie o instanță. Când utilizați o clasă, o instanță este creată automat Iată un exemplu, folosind clasa de excepție
încorporată Exception:

>>> ridică Exception


Traceback (cel mai recent apel ultimul): Fișier
„<stdin>”, linia 1, în ?
Excepție >>>
ridică Excepție('supraîncărcare hiperdrive')
Traceback (cel mai recent apel ultimul): fișierul
„<stdin>”, linia 1, în ?
Excepție: supraîncărcare hiperdrive

Primul exemplu, ridică excepție, ridică o excepție generică fără informații despre ce a mers prost.
În exemplul anterior, am adăugat mesajul de eroare supraîncărcare hiperdrive.
Sunt disponibile multe clase încorporate. Tabelul 8-1 descrie unele dintre cele mai importante. Puteți găsi o descriere a
tuturor acestora în Python Library Reference, în secțiunea „Excepții încorporate”. Toate aceste clase de excepție pot fi folosite în
declarațiile de creștere.

>>> ridică ArithmeticError


Traceback (cel mai recent apel ultimul): fișierul
„<stdin>”, linia 1, în ?
ArithmeticError

Tabelul 8-1. Unele excepții încorporate

Numele clasei Descriere

Excepție Clasa de bază pentru aproape toate excepțiile.


AttributeError Afișat atunci când referința sau atribuirea atributului eșuează.
OSError Apare atunci când sistemul de operare nu poate îndeplini o sarcină, cum ar fi un fișier,
de exemplu. Are mai multe subclase specifice.
IndexError Ridicat atunci când se folosește un index inexistent pe o secvență. Subclasa LookupError.

KeyError Ridicată atunci când se utilizează o cheie inexistentă pe o mapare. Subclasa LookupError.
NameErrore Ridicat atunci când nu este găsit un nume (variabilă).

Eroare de sintaxă Ridicat când codul este prost format.

Eroare de scris Ridicată atunci când o operație sau o funcție încorporată este aplicată unui obiect de tip greșit.

ValueError Ridicată atunci când o operație sau o funcție încorporată este aplicată unui obiect cu tipul
corect, dar cu o valoare inadecvată.

ZeroDivisionError Se ridică atunci când al doilea argument al unei operații de divizare sau modulo este zero.

150
Machine Translated by Google

Capitolul 8 Excep ii

Clase de excepție personalizate


Deși excepțiile încorporate acoperă o mulțime de teren și sunt suficiente pentru multe scopuri, există momente în care s-ar
putea să doriți să vă creați propria. De exemplu, în exemplul de supraîncărcare hyperdrive, nu ar fi mai natural să existe o
anumită clasă HyperdriveError care să reprezinte condiții de eroare în hyperdrive?
S-ar putea părea că mesajul de eroare este suficient, dar după cum veți vedea în secțiunea următoare („Captarea
excepțiilor”), puteți gestiona selectiv anumite tipuri de excepții în funcție de clasa lor. Astfel, dacă doriți să gestionați erorile
hyperdrive cu un cod special de gestionare a erorilor, veți avea nevoie de o clasă separată pentru excepții.

Deci, cum creezi clase de excepție? La fel ca orice altă clasă, dar asigurați-vă că ați subclasat Excepție
(direct sau indirect, ceea ce înseamnă că subclasarea oricărei alte excepții încorporate este în regulă). Astfel, scrierea unei
excepții personalizate înseamnă, practic, ceva de genul acesta:

clasa SomeCustomException(Excepție): trece

Chiar nu e mult de lucru, nu-i așa? (Dacă doriți, cu siguranță puteți adăuga metode și la clasa dvs. de excepție.)

Prinderea excepțiilor
După cum am menționat mai devreme, lucrul interesant despre excepții este că le puteți gestiona (deseori numit captare
sau prinderea excepțiilor). Faceți acest lucru cu declarația try/except. Să presupunem că ați creat un program care permite
utilizatorului să introducă două numere și apoi să le împartă unul la altul, astfel:

x = int(input('Introduceți primul număr: ')) y =


int(input('Introduceți al doilea număr: ')) print(x / y)

Acest lucru ar funcționa bine până când utilizatorul introduce zero ca al doilea număr.

Introduceți primul număr: 10


Introduceți al doilea număr: 0
Traceback (cel mai recent apel ultimul):
Fișierul „exceptions.py”, rândul 3, în ? print(x/y)

ZeroDivisionError: diviziune întreagă sau modulo cu zero

Pentru a prinde excepția și a efectua o anumită gestionare a erorilor (în acest caz pur și simplu imprimând un mesaj de eroare
mai ușor de utilizat), puteți rescrie programul astfel:

încercați: x = int(input('Introduceți primul număr: ')) y =


int(input('Introduceți al doilea număr: ')) print(x / y) cu
excepția ZeroDivisionError: print("Al doilea număr nu poate
fi zero!")

S-ar putea părea că o instrucțiune if simplă care verifică valoarea lui y ar fi mai ușor de utilizat și, în acest caz, ar putea fi într-
adevăr o soluție mai bună. Dar dacă ați adăugat mai multe divizii la programul dvs., veți avea nevoie de o declarație if per
diviziune; folosind try/except, aveți nevoie de un singur handler de erori.

151
Machine Translated by Google

Capitolul 8 Excep ii

Notă Excepțiile se propagă din funcții acolo unde sunt apelate și, dacă nici nu sunt prinse acolo, excepțiile vor „va
urca” la nivelul superior al programului. Aceasta înseamnă că puteți folosi try/except pentru a prinde excepții care
sunt ridicate în funcțiile altor persoane. Pentru mai multe detalii, consultați secțiunea „Excepții și funcții” mai târziu în
acest capitol.

Uite, mamă, fără argumente!


Dacă ați prins o excepție, dar doriți să o ridicați din nou (dați-o mai departe, ca să spunem așa), puteți apela raise
fără niciun argument. (De asemenea, puteți furniza excepția în mod explicit dacă o prindeți, așa cum este explicat în
secțiunea „Prinderea obiectului” mai târziu în acest capitol.)
Ca exemplu despre cum ar putea fi util acest lucru, luați în considerare o clasă de calculatoare care are capacitatea de a „înăbuși”
Excepții ZeroDivisionError. Dacă acest comportament este activat, calculatorul imprimă un mesaj de eroare în loc
să permită propagarea excepției. Acest lucru este util dacă calculatorul este utilizat într-o sesiune interactivă cu un
utilizator, dar dacă este utilizat intern într-un program, ar fi mai bine să ridicați o excepție. Prin urmare, suflarea poate fi
oprită. Iată codul pentru o astfel de clasă:

class MuffledCalculator: muffled


= False def calc(self, expr):
try: return eval(expr) except
ZeroDivisionError: if
self.muffled:
print('Diviziunea la zero este
ilegală') else: raise

Notă Dacă are loc împărțirea la zero și suprimarea este activată, metoda calc va returna (implicit) Niciunul. Cu alte
cuvinte, dacă activați suprimarea, nu ar trebui să vă bazați pe valoarea returnată.

Următorul este un exemplu despre cum poate fi utilizată această clasă, atât cu, cât și fără suflare:

>>> calculator = MuffledCalculator() >>>


calculator.calc('10 / 2')
5.0
>>> calculator.calc('10 / 0') # Fără suprimare Traceback
(cel mai recent apel ultimul): Fișier „<stdin>”, linia 1, în ?
Fișierul „MuffledCalculator.py”, rândul 6, în calc return
eval(expr)
Fișierul „<șir>”, linia 0, în ?
ZeroDivisionError: diviziune întreagă sau modulo cu zero >>>
calculator.muffled = True >>> calculator.calc('10 / 0')

Împărțirea la zero este ilegală

152
Machine Translated by Google

Capitolul 8 Excep ii

După cum puteți vedea, atunci când calculatorul nu este înfundat, ZeroDivisionError este capturat, dar transmis.
Folosirea ridicării fără argumente este adesea o alegere bună într-o clauză except, dacă nu reușiți să gestionați
excepția. Uneori poate doriți să ridicați o altă excepție, totuși. În acest caz, excepția care v-a condus în cauza exceptării
va fi stocată ca context pentru excepția dvs. și va face parte din mesajul de eroare final, de exemplu:

>>> incearca:
... 1/0

... cu excepția ZeroDivisionError:


... ridicați ValueError
...
Traceback (cel mai recent apel ultimul):
Fișierul „<stdin>”, linia 2, în <modul>
ZeroDivisionError: împărțire la zero

În timpul gestionării excepției de mai sus, a apărut o altă excepție:

Traceback (cel mai recent apel ultimul):


Fișierul „<stdin>”, linia 4, în <modul>
ValueError

Puteți furniza propria excepție de context utilizând ridicarea ... utilizați din ... versiunea declarației sau
Niciunul pentru a suprima contextul.

>>> incearca:
... 1/0

... cu excepția ZeroDivisionError:


... ridicați ValueError de la None
...
Traceback (cel mai recent apel ultimul):
Fișierul „<stdin>”, linia 4, în <modul>
ValueError

Mai mult decât unul, cu excepția clauzei Dacă


rulați din nou programul din secțiunea anterioară și introduceți o valoare nenumerică la prompt,
apare o altă excepție.

Introduceți primul număr: 10


Introduceți al doilea număr: "Bună, lume!"
Traceback (cel mai recent apel ultimul):
Fișierul „exceptions.py”, rândul 4, în ?
print(x/y)
TypeError: tip(uri) de operand neacceptat pentru /: „int” și „str”

Deoarece clauza except a căutat doar excepții ZeroDivisionError, aceasta a strecurat și a oprit
programul. Pentru a prinde și această excepție, puteți adăuga pur și simplu o altă clauză except la
aceeași instrucțiune try/except.

153
Machine Translated by Google

Capitolul 8 Excep ii

încercați: x = int(input('Introduceți primul număr: ')) y =


int(input('Introduceți al doilea număr: ')) print(x / y) cu
excepția ZeroDivisionError: print("Al doilea număr nu poate
fie zero!"), cu excepția TypeError: print("Nu a fost un număr, nu-i
așa?")

De data aceasta, folosirea unei declarații if ar fi mai dificilă. Cum verifici dacă o valoare poate fi folosită în diviziune? Există o
serie de moduri, dar de departe cea mai bună modalitate este, de fapt, să împărțiți pur și simplu valorile pentru a vedea dacă
funcționează.
De asemenea, observați cum gestionarea excepțiilor nu aglomera codul original. Adăugând o mulțime de afirmații if
pentru a verifica eventualele condiții de eroare ar fi putut face codul destul de ilizibil.

Prinderea a două excepții cu un singur bloc


Dacă doriți să capturați mai mult de un tip de excepție cu un singur bloc, le puteți specifica pe toate într-un tuplu, după cum
urmează:

încercați: x = int(input('Introduceți primul număr: ')) y =


int(input('Introduceți al doilea număr: ')) print(x / y) cu
excepția (ZeroDivisionError, TypeError, NameError): print('
Numerele tale erau false...')

În codul precedent, dacă utilizatorul introduce fie un șir, fie altceva decât un număr sau dacă al doilea număr este
zero, este tipărit același mesaj de eroare. Pur și simplu imprimarea unui mesaj de eroare nu este foarte utilă, desigur.
O alternativă ar putea fi să continui să ceri numere până când diviziunea funcționează. Vă arăt cum să faceți asta în
secțiunea „Când totul este bine” mai târziu în acest capitol.
Rețineți că parantezele din jurul excepțiilor din clauza except sunt importante. O eroare comună este să omiteți aceste
paranteze, caz în care s-ar putea să ajungeți la altceva decât ceea ce doriți. Pentru o explicație, consultați secțiunea următoare,
„Prinderea obiectului”.

Prinderea obiectului Dacă doriți acces

la obiectul excepție în sine într-o clauză except, puteți utiliza două argumente în loc de unul. (Rețineți că, chiar și atunci când
detectați mai multe excepții, furnizați cu excepția unui singur argument - un tuplu.) Acest lucru poate fi util (de exemplu) dacă
doriți ca programul dvs. să continue să ruleze, dar doriți să înregistrați eroarea cumva (poate că doar imprimându-l
utilizatorului). Următorul este un exemplu de program care imprimă excepția (dacă apare) dar continuă să ruleze:

încercați: x = int(input('Introduceți primul număr: ')) y =


int(input('Introduceți al doilea număr: ')) print(x / y) cu
excepția (ZeroDivisionError, TypeError) ca e: print(e )

154
Machine Translated by Google

Capitolul 8 Excep ii

Clauza excepție din acest mic program prinde din nou două tipuri de excepții, dar pentru că de asemenea prinzi în mod
explicit obiectul în sine, îl poți tipări astfel încât utilizatorul să poată vedea ce sa întâmplat. (Veți o aplicație mai utilă a
acesteia mai târziu în acest capitol, în secțiunea „Când totul este bine.”)

O adevărată captură
Chiar dacă programul gestionează mai multe tipuri de excepții, unele pot trece. De exemplu, folosind același program
de divizare, pur și simplu încercați să apăsați Enter la prompt, fără a scrie nimic. Ar trebui să primiți un mesaj de eroare
și câteva informații despre ceea ce a mers prost (o urmă de stivă), cam așa:

Traceback (cel mai recent apel ultimul):


...
''
ValueError: literal invalid pentru int() cu baza 10:

Această excepție a ajuns prin declarația try/except - și pe bună dreptate. Nu ați prevăzut că acest lucru s-ar putea
întâmpla și nu ați fost pregătiți pentru asta. În aceste cazuri, este mai bine ca programul să se blocheze imediat (ca
să puteți vedea ce este în neregulă) decât să ascundă pur și simplu excepția cu o declarație try/except care nu este
menită să o prindă.
Cu toate acestea, dacă doriți să capturați toate excepțiile dintr-o bucată de cod, puteți pur și simplu să omiteți
clasa de excepție din clauza except.

încercați: x = int(input('Introduceți primul număr: ')) y =


int(input('Introduceți al doilea număr: ')) print(x / y) cu
excepția: print('S-a întâmplat ceva în neregulă...' )

Acum poți face practic orice vrei.

Introduceți primul număr: „Acesta” este *complet* ilegal 123 Sa întâmplat


ceva în neregulă...

Prinderea tuturor excepțiilor de acest fel este o afacere riscantă, deoarece va ascunde erorile la care nu te-ai
gândit la fel de bine ca și cele pentru care ești pregătit. De asemenea, va capta încercările utilizatorului de a
termina execuția prin Ctrl-C, încercările de la funcțiile pe care le apelați pentru a termina prin sys.exit și așa mai
departe. În cele mai multe cazuri, ar fi mai bine să utilizați except Exception ca e și, poate, să verificați obiectul
excepție, e. Acest lucru va permite apoi să treacă prin acele foarte puține excepții care nu fac parte din subclasa
Excepție. Aceasta include SystemExit și KeyboardInterrupt, care subclasă BaseException, superclasa Exception în sine.

Când totul este bine


În unele cazuri, poate fi util să existe un bloc de cod care să fie executat dacă nu se întâmplă ceva rău; ca și în cazul
condiționalelor și buclelor, puteți adăuga o clauză else la instrucțiunea try/except.

try:
print('O sarcină simplă') cu
excepția: print('Ce? Ceva a mers
prost?') altfel: print('Ah...

A mers conform planului.')

155
Machine Translated by Google

Capitolul 8 Excep ii

Dacă rulați acest lucru, obțineți următoarea ieșire:

O sarcină simplă
Ah A...mersconform planului.

Cu această clauză else, puteți implementa bucla indicată în secțiunea „Capturarea a două excepții cu un singur bloc” mai
devreme în acest capitol.

while True:
try: x =
int(input('Introduceți primul număr: ')) y =
int(input('Introduceți al doilea număr: ')) value = x / y
print('x / y is', value ) cu excepția: print('Intrare nevalidă.
Vă rugăm să încercați din nou.') else: break

Aici, bucla este întreruptă (prin instrucțiunea break din clauza else) numai atunci când nu este ridicată nicio excepție. Cu
alte cuvinte, atâta timp cât se întâmplă ceva greșit, programul continuă să solicite noi intrări. Următorul este un exemplu de
rulare:

Introduceți primul număr: 1


Introduceți al doilea număr: 0
Intrare nevalidă. Vă rugăm să încercați din nou.
Introduceți primul număr: „foo”
Introduceți al doilea număr: „bara”
Intrare nevalidă. Vă rugăm să încercați din nou.
Introduceți primul număr: baz
Intrare nevalidă. Vă rugăm să încercați din nou.
Introduceți primul număr: 10
Introduceți al doilea număr: 2 x / y
este 5

După cum s-a menționat anterior, o alternativă preferabilă la utilizarea unei clauze cu excepția goală este să prindeți toate
excepțiile clasei Exception (care va prinde și toate excepțiile oricărei subclase). Nu poți fi 100% sigur că vei înțelege totul
atunci, deoarece codul din declarația ta try/except poate fi obraznic și poate folosi excepțiile de șir de modă veche sau poate
crea o excepție personalizată care nu subclasa Excepție. Cu toate acestea, dacă utilizați versiunea cu excepția excepției,
puteți utiliza tehnica din secțiunea „Prinderea obiectului” de mai devreme în acest capitol pentru a imprima un mesaj de
eroare mai instructiv în programul dumneavoastră mic de diviziune.

while True:
try: x =
int(input('Introduceți primul număr: ')) y =
int(input('Introduceți al doilea număr: ')) value = x / y
print('x / y is', value )

156
Machine Translated by Google

Capitolul 8 Excep ii

cu excepția excepției ca e:
print('Intrare nevalidă:', e) print('Vă
rugăm să încercați din nou') else: break

Următorul este un exemplu de rulare:

Introduceți primul număr: 1


Introduceți al doilea număr: 0

Intrare nevalidă: diviziune întregă sau modulo cu zero


Vă rugăm să încercați din nou

Introduceți primul număr: „x” Introduceți al doilea număr: „y”


Intrare nevalidă: tipuri de operanzi neacceptate pentru /: „str” și „str”
Vă rugăm să încercați din nou

Introduceți primul număr: quuux


Intrare nevalidă: numele „quuux” nu este definit
Vă rugăm să încercați din nou
Introduceți primul număr: 10
Introduceți al doilea număr: 2

x/y este 5

Și, în sfârșit . . .
În cele din urmă, există clauza finală. Îl folosești pentru a face menaj după o posibilă excepție. Este combinată cu o clauză de încercare.

x = Nici unul

încercați: x = 1 / 0

în sfârșit:
print('Curățenie...') del x

În exemplul precedent, vi se garantează că clauza finally va fi executată, indiferent de excepțiile care apar în clauza try. Motivul
inițializării x înainte de clauza try este că, altfel, nu i-ar fi atribuită niciodată o valoare din cauza ZeroDivisionError. Acest lucru ar
duce la o excepție atunci când utilizați del pe acesta în clauza finally, pe care nu o veți prinde .

Dacă rulați acest lucru, curățarea vine înainte ca programul să se blocheze și să se ardă.

A curăța ...
Traceback (cel mai recent apel ultimul):
Fișierul „C:\python\div.py”, rândul 4, în ? x = 1 / 0

ZeroDivisionError: diviziune întreagă sau modulo cu zero

În timp ce folosirea del pentru a elimina o variabilă este un fel de curățare destul de stupidă, clauza final poate fi destul de utilă pentru
închiderea fișierelor sau a soclurilor de rețea și altele asemenea. (Aflați mai multe despre acestea în capitolul 14.)

157
Machine Translated by Google

Capitolul 8 Excep ii

Puteți, de asemenea, să combinați încercarea, cu excepția, în cele din urmă, și altceva (sau doar trei dintre ele) într-o singură declarație.

încercați:
1 / 0, cu excepția
NameError: print("Variabilă
necunoscută") else: print("A mers
bine!") în cele din urmă: print("În
curs de curățare")

Excepții și funcții
Excepțiile și funcțiile funcționează împreună destul de natural. Dacă o excepție este ridicată în interiorul unei funcții și
nu este gestionată acolo, aceasta se propagă (bubbles up) în locul în care funcția a fost apelată. Dacă nici acolo nu este
gestionat, continuă să se propage până când ajunge la programul principal (sfera globală), iar dacă nu există niciun
handler de excepție acolo, programul se oprește cu o urmărire a stivei. Să aruncăm o privire la un exemplu:

>>> implicit
... defect(): ridică excepția („Ceva este în neregulă”)
...
>>> def ignore_exception():
... defect()
...
>>> def handle_exception():
...
... încercați:
... defect() cu
... excepția: print('Excepție gestionată')
...
>>> ignore_exception()
Traceback (cel mai recent apel
ultimul): fișierul „<stdin>”, linia 1, în ?
Fișierul „<stdin>”, linia 2, în ignore_exception
Fișierul „<stdin>”, linia 2, este defect
Excepție: ceva nu este în regulă
>>> handle_exception()
Excepție gestionată

După cum puteți vedea, excepția ridicată în faulty se propagă prin fault și ignore_exception și în cele din urmă provoacă
o urmărire a stivei. În mod similar, se propagă la handle_exception, dar acolo este tratat cu o instrucțiune try/except.

Zenul excepțiilor
Gestionarea excepțiilor nu este foarte complicată. Dacă știți că o parte a codului dvs. poate provoca un anumit
tip de excepție și nu doriți pur și simplu ca programul dvs. să se termine cu o urmărire a stivei dacă și când se
întâmplă asta, atunci adăugați instrucțiunile necesare try/except sau try/finally. (sau o combinație a acestora) pentru
a face față, după cum este necesar.

158
Machine Translated by Google

Capitolul 8 Excep ii

Uneori, puteți realiza același lucru cu instrucțiunile condiționate ca și cu gestionarea excepțiilor, dar declarațiile
condiționate vor ajunge probabil să fie mai puțin naturale și mai puțin lizibile. Pe de altă parte, unele lucruri care ar putea
părea a fi aplicații naturale ale if/else pot fi de fapt implementate mult mai bine cu try/except. Să aruncăm o privire la
câteva exemple.
Să presupunem că aveți un dicționar și doriți să tipăriți valoarea stocată sub o anumită cheie, dacă este acolo.
Dacă nu este acolo, nu vrei să faci nimic. Codul ar putea fi cam așa:

def descrie_persoana(persoana):
print('Descrierea lui', persoana['numele'])
print('Vârsta:', persoana['vârsta']) dacă 'ocupa ia'
în persoană: print('Ocupa ia:', persoana[ 'ocupa ie'])

Dacă furnizați acestei funcții un dicționar care conține numele Throatwobbler Mangrove și vârsta de 42 de ani (dar fără
ocupație), obțineți următorul rezultat:

Descrierea Throatwobbler Mangrove


Vârsta: 42

Dacă adăugați ocupația „camper”, obțineți următorul rezultat:

Descrierea Throatwobbler Mangrove


Vârsta: 42
Ocupatie: camper

Codul este intuitiv, dar puțin ineficient (deși principala preocupare aici este cu adevărat simplitatea codului). Trebuie să
caute cheia „ocupație” de două ori – o dată pentru a vedea dacă cheia există (în stare) și o dată pentru a obține valoarea
(pentru a o tipări). O definiție alternativă este următoarea:

def descrie_persoana(persoana):
print('Descrierea lui', persoana['numele'])
print('Vârsta:', persoana['vârsta']) try: print('Ocupația:',
persoana['ocupația']) cu excepția KeyError: pass

Aici, funcția presupune pur și simplu că „ocupația” cheie este prezentă. Dacă presupuneți că este în mod normal, acest
lucru economisește un efort. Valoarea va fi preluată și tipărită - nu există o preluare suplimentară pentru a verifica dacă
este într-adevăr acolo. Dacă cheia nu există, este generată o excepție KeyError, care este blocată de clauza except.
De asemenea, puteți găsi try/except util atunci când verificați dacă un obiect are un anumit atribut. Sa spunem
doriți să verificați dacă un obiect are un atribut de scriere, de exemplu. Atunci ai putea folosi cod ca acesta:

try:
obj.write
except AttributeError:
print('Obiectul nu poate fi scris') else:
print('Obiectul poate fi scris')

159
Machine Translated by Google

Capitolul 8 Excep ii

Aici clauza try accesează pur și simplu atributul fără a face nimic util cu el. Dacă apare un AttributeError,
obiectul nu are atributul; altfel, are atributul. Aceasta este o alternativă naturală la soluția getattr introdusă în Capitolul
7 (în secțiunea „Interfețe și introspecție”). Pe care o preferați este în mare măsură o chestiune de gust.

Rețineți că câștigul în eficiență aici nu este mare. (Este mai degrabă foarte, foarte mic.) În general (cu excepția cazului în
care programul dvs. are probleme de performanță), nu ar trebui să vă faceți griji prea mult cu privire la acest tip de optimizare.
Ideea este că utilizarea declarațiilor try/except este în multe cazuri mult mai naturală (mai „Pythonic”) decât if/else și ar trebui
să vă obișnuiți să le folosiți acolo unde puteți.1

Nu atât de excepțional
Dacă doriți doar să oferiți un avertisment că lucrurile nu sunt exact așa cum ar trebui să fie, puteți utiliza funcția de
avertizare din modulul de avertismente.

>>> din avertismente import warn >>>


warn("Am un sentiment prost despre asta.") __main__:1:
UserWarning: Am un sentiment prost despre asta.
>>>

Avertismentul va fi afișat o singură dată. Dacă rulați din nou ultima linie, nu se va întâmpla nimic.
Alt cod care utilizează modulul vă poate suprima avertismentele, sau numai anumite tipuri de avertismente, folosind
funcția filterwarnings din același modul, specificând una dintre mai multe acțiuni posibile de întreprins, inclusiv „eroare”
și „ignore”.

>>> din avertismente import filterwarnings >>>


filterwarnings(„ignora”) >>> warn(„Pe cineva acolo?”)
>>> filterwarnings(„eroare”) >>> warn(„Ceva este
foarte greșit!”)

Traceback (cel mai recent apel ultimul):


Fișierul „<stdin>”, linia 1, în <modul> Avertisment
utilizator: Ceva este foarte greșit!

După cum puteți vedea, excepția ridicată este UserWarning. Puteți specifica o altă excepție sau categorie de avertisment
atunci când emiteți avertismentul. Această excepție ar trebui să fie o subclasă de Avertizare. Excepția pe care o furnizați va
fi utilizată dacă transformați avertismentul într-o eroare, dar o puteți folosi și pentru a filtra în mod specific un anumit tip de
avertismente.

>>> filterwarnings(„eroare”) >>>


warn(„Această funcție este foarte veche...”, Avertisment de depreciere)
Traceback (cel mai recent apel ultimul):
Fișier „<stdin>”, linia 1, în <modul>
DeprecationWarning: Această funcție este foarte veche... >>>
filterwarnings(„ignore”, category=DeprecationWarning) >>> warn(„Un alt
avertisment de depreciere.”, DeprecationWarning )

1 Preferința pentru încercare/cu excepția în Python este adesea explicată prin cuvintele de înțelepciune ale contraamiralului Grace
Hopper: „Este mai ușor să ceri iertare decât permisiunea”. Această strategie de a încerca pur și simplu să faci ceva și de a face față oricăror
erori, mai degrabă decât a face o mulțime de verificări dinainte, se numește expresia Leap Before You Look .

160
Machine Translated by Google

Capitolul 8 Excep ii

>>> warn ("Încă ceva.")


Traceback (cel mai recent apel ultimul):
Fișierul „<stdin>”, linia 1, în <modul> UserWarning:
Altceva.

Modulul de avertismente are câteva clopote și fluiere avansate dincolo de această utilizare de bază. Consultați referința
bibliotecii dacă sunteți curios.

Un rezumat rapid
Principalele subiecte abordate în acest capitol sunt următoarele:

Obiecte excepție: situațiile excepționale (cum ar fi atunci când a apărut o eroare) sunt
reprezentate de obiecte excepție. Acestea pot fi manipulate în mai multe moduri, dar dacă sunt
ignorate, vă termină programul.

Generarea de excepții: puteți ridica excepții cu declarația de ridicare. Acceptă fie o clasă
de excepție, fie o instanță de excepție ca argument. De asemenea, puteți furniza două
argumente (o excepție și un mesaj de eroare). Dacă apelați raise fără argumente într-o clauză
except, aceasta „reclamă” excepția prinsă de acea clauză.

Clase de excepții personalizate: puteți crea propriile tipuri de excepții prin subclasarea
Excepție.

Prinderea excepțiilor: Prindeți excepții cu clauza except a unei instrucțiuni try. Dacă nu
specificați o clasă în clauza except, toate excepțiile sunt prinse. Puteți specifica mai multe
clase punându-le într-un tuplu. Dacă dați două argumente pentru except, al doilea este
legat de obiectul excepție. Puteți avea mai multe clauze except în aceeași instrucțiune try/
except, pentru a reacționa diferit la diferite excepții.

clauze else: puteți folosi o clauză else în plus față de except. Clauza else este executată dacă
nu sunt ridicate excepții în blocul principal de încercare.

finally: Puteți folosi try/finally dacă trebuie să vă asigurați că un anumit cod (de exemplu,
codul de curățare) este executat, indiferent dacă este ridicată sau nu o excepție. Acest cod este
apoi introdus în clauza finally.

Excepții și funcții: Când ridicați o excepție în interiorul unei funcții, aceasta se propagă
în locul în care a fost apelată funcția. (Același lucru este valabil și pentru metode.)

Avertismente: Avertismentele sunt similare cu excepțiile, dar (în general) vor tipări doar
un mesaj de eroare. Puteți specifica o categorie de avertizare, care este o subclasă de
avertisment.

161
Machine Translated by Google

Capitolul 8 Excep ii

Funcții noi în acest capitol


Func ie Descriere
warnings.filterwarnings(action, category=Warning, ...) Folosit pentru a filtra avertismente

warnings.warn(message, category=None) Folosit pentru a emite avertismente

Ce acum?
Deși ați putea crede că materialul din acest capitol a fost excepțional (iertați jocul de cuvinte), următorul capitol este cu
adevărat magic. Ei bine, aproape magic.

162
Machine Translated by Google

CAPITOLUL 9

Metode magice, proprietăți


și iteratori

În Python, unele nume sunt scrise într-un mod deosebit, cu două liniuțe de subliniere de început și două de subliniere.
Ați întâlnit deja unele dintre acestea (__viitorul__, de exemplu). Această ortografie semnalează că numele are o semnificație
specială - nu ar trebui să inventați niciodată astfel de nume pentru propriile programe. Un set foarte proeminent de astfel
de nume în limbă este format din numele metodelor magice (sau speciale). Dacă obiectul dumneavoastră implementează
una dintre aceste metode, acea metodă va fi apelată în circumstanțe specifice (exact care va depinde de nume) de către
Python. Rareori este nevoie să apelați direct aceste metode.
Acest capitol tratează câteva metode magice importante (mai ales metoda __init__ și unele
metode care se ocupă cu accesul la articole, permițându-vă să creați secvențe sau mapări proprii). De asemenea,
abordează două subiecte conexe: proprietăți (abordate prin metode magice în versiunile anterioare de Python, dar acum
gestionate de funcția de proprietate) și iteratoare (care folosesc metoda magică __iter__ pentru a le permite să fie utilizate
în bucle for). Veți găsi un exemplu de carne la sfârșitul capitolului, care folosește unele dintre lucrurile pe care le-ați învățat
până acum pentru a rezolva o problemă destul de dificilă.

Dacă nu utilizați Python 3


Cu ceva timp în urmă (în versiunea 2.2), modul în care funcționează obiectele Python s-a schimbat destul de mult.
Această schimbare are mai multe consecințe, dintre care majoritatea nu vor fi importante pentru tine ca programator
Python începător. Totuși, un lucru merită remarcat: chiar dacă utilizați o versiune recentă a Python 2, unele
caracteristici (cum ar fi proprietățile și funcția super) nu vor funcționa pe clasele „în stil vechi”. Pentru a face clasele
dvs. în „stil nou”, ar trebui fie să puneți alocația __metaclass__ = tip în partea de sus a modulelor (așa cum este
menționat în Capitolul 7) sau (direct sau indirect) subclasă obiectul clasa încorporat, sau un alt nou -clasa de stil. Luați
în considerare următoarele două clase:

clasa NewStyle(obiect):
more_code_here

clasa OldStyle:
more_code_here

Dintre acestea două, NewStyle este o clasă cu stil nou; OldStyle este o clasă în stil vechi. Dacă fișierul
începe cu __metaclass__ = type, ambele clase ar fi de tip nou.

© Magnus Lie Hetland 2017 163


ML Hetland, Beginning Python, DOI 10.1007/978-1-4842-0028-5_9
Machine Translated by Google

Capitolul 9 Metode magice, proprietă i i iteratoare

Notă Puteți, de asemenea, să alocați variabilei __metaclass__ în domeniul de aplicare al clasei dumneavoastră.
Asta ar stabili metaclasa doar a acelei clase. Metaclasele sunt clasele altor clase — un subiect destul de avansat.

Nu am setat în mod explicit metaclasa (sau obiectul subclasei) în toate exemplele din această carte. Cu toate acestea, dacă
nu trebuie să vă faceți programele compatibile cu versiunile vechi de Python, vă sfătuiesc să faceți toate clasele în stil nou
și să utilizați în mod constant funcții precum super-funcția (descrisă în secțiunea „Folosirea super-funcției” mai târziu în
acest capitol).
Rețineți că nu există clase „în stil vechi” în Python 3 și nu este nevoie să subclasați în mod explicit obiectul sau setul
metaclasa de tipat. Toate clasele vor fi implicit subclase de obiect - direct, dacă nu specificați o superclasă, sau indirect
altfel.

Constructorii
Prima metodă magică la care vom arunca o privire este constructorul. În cazul în care nu ați auzit niciodată cuvântul
constructor înainte, este practic un nume de lux pentru tipul de metodă de inițializare pe care l-am folosit deja în unele
dintre exemple, sub numele __init__. Totuși, ceea ce separă constructorii de metodele obișnuite este că constructorii sunt
apelați automat imediat după ce a fost creat un obiect. Astfel, în loc să fac ceea ce am făcut până acum:

>>> f = FooBar() >>>


f.init()

constructorii fac posibilă pur și simplu acest lucru:

>>> f = FooBar()

Crearea de constructori în Python este foarte ușoară; pur și simplu schimbați numele metodei init de la vechiul init
simplu la versiunea magică, __init__.

clasa FooBar: def


__init__(self): self.somevar
= 42

>>> f = FooBar() >>>


f.somevar 42

Acum, asta e destul de frumos. Dar s-ar putea să vă întrebați ce se întâmplă dacă îi oferiți constructorului niște parametri cu care
să lucreze. Luați în considerare următoarele:

clasa FooBar:
def __init__(self, value=42): self.somevar
= value

164
Machine Translated by Google

Capitolul 9 Metode magice, proprietă i i iteratoare

Cum crezi că ai putea folosi asta? Deoarece parametrul este opțional, cu siguranță ai putea continua ca și cum nimic nu s-
ar fi întâmplat. Dar dacă ai fi vrut să-l folosești (sau nu ai fi făcut-o opțional)? Sunt sigur că ai ghicit, dar lasă-mă să îți arăt
oricum.

>>> f = FooBar('Acesta este un argument constructor') >>> f.somevar


'Acesta este un argument constructor'

Dintre toate metodele magice din Python, __init__ este cu siguranță cea pe care o vei folosi cel mai mult.

Notă Python are o metodă magică numită __del__, cunoscută și ca destructor. Se numește chiar înainte ca obiectul
să fie distrus (colectat de gunoi), dar pentru că nu poți ști cu adevărat când (sau dacă) se întâmplă acest lucru, te
sfătuiesc să stai departe de __del__ dacă este posibil.

Metodele de suprascrie în general și constructorul în particular


În capitolul 7, ați învățat despre moștenire. Fiecare clasă poate avea una sau mai multe superclase, de la care moștenesc
comportamentul. Dacă o metodă este apelată (sau este accesat un atribut) pe o instanță a clasei B și nu este găsită, va fi căutată
superclasa A. Luați în considerare următoarele două clase:

clasa A:
def hello(self):
print("Bună, eu sunt A.")

clasa B(A): trece

Clasa A definește o metodă numită hello, care este moștenită de clasa B. Iată un exemplu despre cum funcționează aceste clase:

>>> a = A() >>>


b = B() >>>
a.salut()
Bună, sunt A. >>>
b.hello()
Salut, sunt A.

Deoarece B nu definește o metodă hello proprie, mesajul original este tipărit atunci când este apelat hello.
O modalitate de bază de a adăuga funcționalități în subclasă este pur și simplu adăugarea de metode. Cu toate
acestea, poate doriți să personalizați comportamentul moștenit prin suprascrierea unora dintre metodele superclasei. De
exemplu, este posibil ca B să suprascrie metoda hello. Luați în considerare această definiție modificată a lui B:

clasa B(A): def


hello(self): print("Bună,
sunt B.")

165
Machine Translated by Google

Capitolul 9 Metode magice, proprietă i i iteratoare

Folosind această definiție, b.hello() va da un rezultat diferit.

>>> b = B()
>>> b.salut()
Salut, sunt B.

Suprascrierea este un aspect important al mecanismului de moștenire în general și poate fi deosebit de importantă pentru
constructori. Constructorii sunt acolo pentru a inițializa starea obiectului nou construit și majoritatea subclaselor vor trebui
să aibă propriul cod de inițializare, în plus față de cel al superclasei. Chiar dacă mecanismul de suprascrie este același pentru
toate metodele, cel mai probabil veți întâmpina o anumită problemă mai des atunci când aveți de-a face cu constructori
decât atunci când înlocuiți metode obișnuite: dacă suprascrieți constructorul unei clase, trebuie să apelați constructorul
clasei. superclasă (clasa de la care moșteniți) sau riscați să aveți un obiect care nu este inițializat corect.

Luați în considerare următoarea clasă, Bird:

clasa Bird: def


__init__(self): self.hungry
= Adevărat def eat(self):
if self.hungry: print('Aaaah ...')
self.hungry = False Else:
print('Nu, mulțumesc!')

Această clasă definește una dintre cele mai de bază capacități ale tuturor păsărilor: mâncarea. Iată un exemplu despre cum l-ați putea
folosi:

>>> b = Bird() >>>


b.eat()
Aaaah ...
>>> b.eat()
Nu, mulțumesc!

După cum puteți vedea din acest exemplu, odată ce pasărea a mâncat, nu mai este foame. Acum luați în considerare
subclasa SongBird, care adaugă cântatul la repertoriul de comportamente.

clasa SongBird(Bird): def


__init__(self): self.sound
= 'Squawk!' def sing(self):
print(self.sound)

Clasa SongBird este la fel de ușor de utilizat ca și Bird.

>>> sb = SongBird() >>>


sb.sing()
ipăt!

166
Machine Translated by Google

Capitolul 9 Metode magice, proprietă i i iteratoare

Deoarece SongBird este o subclasă a Bird, moștenește metoda eat, dar dacă încerci să o numești, vei descoperi o
problemă.

>>> sb.eat()
Traceback (cel mai recent apel ultimul):
fișierul „<stdin>”, linia 1, în ?
Fișierul „birds.py”, rândul 6, în eat if
self.hungry: AttributeError: instanța
SongBird nu are atributul „foame”

Excepția este destul de clară cu privire la ceea ce este în neregulă: SongBird nu are un atribut numit foame. De ce ar trebui?
În SongBird, constructorul este suprascris, iar noul constructor nu conține niciun cod de inițializare care să se ocupe
de atributul hungry. Pentru a remedia situația, constructorul SongBird trebuie să apeleze constructorul superclasei
sale, Bird, pentru a se asigura că inițializarea de bază are loc. Practic, există două moduri de a face acest lucru: apelând
versiunea nelegată a constructorului superclasei sau folosind funcția super. În următoarele două secțiuni, explic
ambele tehnici.

Apelarea constructorului de superclasă nelegată Abordarea descrisă în această

secțiune este, probabil, în principal de interes istoric. Cu versiunile actuale de Python, utilizarea super-funcției (după
cum este explicat în secțiunea următoare) este în mod clar calea de urmat. Cu toate acestea, mult codul existent
utilizează abordarea descrisă în această secțiune, așa că trebuie să știți despre el. De asemenea, poate fi destul de instructiv
- este un exemplu frumos al diferenței dintre metodele legate și cele nelegate.
Acum, să trecem la treabă. Dacă ți se pare puțin intimidant titlul acestei secțiuni, relaxează-te. Apelarea
constructorului unei superclase este, de fapt, foarte ușoară (și utilă). Voi începe prin a vă oferi soluția la problema pusă
la sfârșitul secțiunii anterioare.

clasa SongBird(Bird): def


__init__(self):
Bird.__init__(self)
self.sound = 'Squawk!' def
sing(self): print(self.sound)

Doar o linie a fost adăugată la clasa SongBird, care conține codul Bird.__init__(self). Înainte de a explica ce înseamnă
asta cu adevărat, permiteți-mi doar să vă arăt că acest lucru funcționează cu adevărat.

>>> sb = SongBird() >>>


sb.sing()
ipăt!
>>> sb.eat()
Aaaah ...
>>> sb.eat()
Nu, mulțumesc!

167
Machine Translated by Google

Capitolul 9 Metode magice, proprietă i i iteratoare

Dar de ce funcționează asta? Când preluați o metodă dintr-o instanță, argumentul de sine al metodei este legat
automat de instanță (o așa-numită metodă legată). Ați văzut mai multe exemple în acest sens.
Cu toate acestea, dacă preluați metoda direct din clasă (cum ar fi în Bird.__init__), nu există nicio instanță la care să vă
legați. Prin urmare, sunteți liber să vă furnizați orice sine doriți. O astfel de metodă se numește nelegat, ceea ce explică
titlul acestei secțiuni.
Prin furnizarea instanței curente ca argument de sine pentru metoda nelegată, pasărea cântătoare primește
tratarea completă de la constructorul superclasei sale (ceea ce înseamnă că are setul de atribute foame).

Utilizarea super-funcției Dacă nu sunteți

blocat cu o versiune veche de Python, super-funcția este într-adevăr calea de urmat. Funcționează numai cu clase de stil
nou, dar ar trebui să le folosiți oricum. Este apelat cu clasa și instanța curentă ca argumente, iar orice metodă pe care o
apelați pe obiectul returnat va fi preluată din superclasă și nu din clasa curentă. Deci, în loc să utilizați Bird în constructorul
SongBird, puteți folosi super(SongBird, self). De asemenea, metoda __init__ poate fi apelată într-un mod normal (legat). În
Python 3, super poate – și în general ar trebui – să fie numit fără niciun argument și își va face treaba ca „prin magie”.

Următoarea este o versiune actualizată a exemplului de pasăre:

clasa Bird: def


__init__(self): self.hungry
= Adevărat def eat(self):
if self.hungry: print('Aaaah ...')
self.hungry = False
Else: print('Nu,
mulțumesc!')

clasa SongBird(Bird): def


__init__(self):
super().__init__()
self.sound = 'Squawk!' def
sing(self): print(self.sound)

Această versiune în stil nou funcționează la fel ca cea în stil vechi:

>>> sb = SongBird() >>>


sb.sing()
ipăt!
>>> sb.eat()
Aaaah ...
>>> sb.eat()
Nu, mulțumesc!

168
Machine Translated by Google

Capitolul 9 Metode magice, proprietă i i iteratoare

CE ESTE ATÂT DE SUPER LA SUPER?

După părerea mea, funcția super este mai intuitivă decât apelarea directă a metodelor nelegate pe superclasă, dar acesta
nu este singurul ei punct forte. Funcția super este de fapt destul de inteligentă, așa că, chiar dacă aveți mai multe superclase,
trebuie să utilizați super o singură dată (cu condiția ca toți constructorii de superclase să folosească și super). De asemenea,
unele situații obscure care sunt dificile atunci când folosiți clase în stil vechi (de exemplu, când două dintre superclasele dvs.
împart o superclasă) sunt rezolvate automat de clasele de stil nou și super. Nu trebuie să înțelegeți exact cum funcționează intern,
dar ar trebui să știți că, în cele mai multe cazuri, este net superior apelării constructorilor nelegați (sau a altor metode) ai
superclaselor dvs.

Deci, ce se întoarce super , de fapt? În mod normal, nu trebuie să vă faceți griji pentru asta și vă puteți preface doar că vă întoarce
superclasa de care aveți nevoie. Ceea ce face de fapt este să returneze un super obiect, care se va ocupa de rezolvarea metodei
pentru tine. Când accesați un atribut al acestuia, acesta va căuta prin toate superclasele (și super-superclasele și așa mai departe)
până când găsește atributul sau afișează un AttributeError.

Acces articol
Deși __init__ este de departe cea mai importantă metodă specială pe care o veți întâlni, multe altele sunt disponibile pentru a vă
permite să realizați o mulțime de lucruri interesante. Un set util de metode magice descrise în această secțiune vă permite să creați
obiecte care se comportă ca secvențe sau mapări.
Secvența de bază și protocolul de mapare sunt destul de simple. Cu toate acestea, pentru a implementa toate funcționalitățile
de secvențe și mapări, există multe metode magice de implementat. Din fericire, există câteva comenzi rapide, dar voi
ajunge la asta.

Notă Cuvântul protocol este adesea folosit în Python pentru a descrie regulile care guvernează o anumită formă de comportament.

Acest lucru este oarecum similar cu noțiunea de interfețe menționată în Capitolul 7. Protocolul spune ceva despre metodele pe care ar

trebui să le implementați și ce ar trebui să facă acele metode. Deoarece polimorfismul în Python se bazează doar pe comportamentul

obiectului (și nu pe strămoșii acestuia, de exemplu, clasa sau superclasa și așa mai departe), acesta este un concept important: în cazul în

care alte limbi ar putea cere ca un obiect să aparțină unei anumite clase. sau pentru a implementa o anumită interfață, Python îi cere

adesea pur și simplu să urmeze un anumit protocol. Deci, pentru a fi o secvență, tot ce trebuie să faci este să urmezi protocolul secvenței.

169
Machine Translated by Google

Capitolul 9 Metode magice, proprietă i i iteratoare

Secvența de bază și protocolul de cartografiere Secvențele și mapările

sunt în principiu colecții de articole. Pentru a implementa comportamentul lor de bază (protocol), aveți nevoie de
două metode magice dacă obiectele dvs. sunt imuabile, sau patru dacă sunt mutabile.

__len__(self): Această metodă ar trebui să returneze numărul de articole conținute în


colecție. Pentru o secvență, acesta ar fi pur și simplu numărul de elemente. Pentru o
mapare, ar fi numărul de perechi cheie-valoare. Dacă __len__ returnează zero (și nu
implementați __nonzero__, care suprascrie acest comportament), obiectul este tratat ca
fals într-un context boolean (ca și în cazul listelor goale, tuplurilor, șirurilor și dicționarelor).

__getitem__(self, key): Aceasta ar trebui să returneze valoarea corespunzătoare cheii


date. Pentru o secvență, cheia ar trebui să fie un număr întreg de la zero la n–1 (sau, ar
putea fi negativă, după cum s-a menționat mai târziu), unde n este lungimea secvenței.
Pentru o mapare, ai putea avea într-adevăr orice fel de chei.

__setitem__(self, key, value): Aceasta ar trebui să stocheze valoarea într-un mod


asociat cu cheia, astfel încât să poată fi preluată ulterior cu __getitem__. Desigur, definiți
această metodă numai pentru obiectele mutabile.

__delitem__(self, key): Acesta este numit atunci când cineva folosește instrucțiunea
__del__ pe o parte a obiectului și ar trebui să ștergă elementul asociat cu cheia. Din nou,
numai obiectele mutabile (și nu toate - doar acelea pentru care doriți să lăsați elementele
să fie eliminate) ar trebui să definească această metodă.

Aceste metode sunt impuse câteva cerințe suplimentare.

•Pentru o secvență, dacă cheia este un număr întreg negativ, ar trebui folosită pentru a număra de la
Sfâr it. Cu alte cuvinte, tratați x[-n] la fel ca x[len(x)-n].
• Dacă cheia este de tip nepotrivit (cum ar fi o cheie cu șir folosită într-o secvență), poate fi
generată o TypeError.
• Dacă indexul unei secvențe este de tipul corect, dar în afara intervalului permis, ar
trebui să apară o IndexError.

Pentru o interfață mai extinsă, împreună cu o clasă de bază abstractă adecvată (Sequence), consultați
documentație pentru modulul de colecții.

Hai să încercăm - să vedem dacă putem crea o secvență infinită.

def check_index(cheie):
"""

Este cheia dată un index acceptabil?

Pentru a fi acceptabilă, cheia ar trebui să fie un număr întreg nenegativ. Dacă nu este
un număr întreg, apare o TypeError; dacă este negativă, apare o IndexError (deoarece
secvența este de lungime infinită).
"""

dacă nu isinstance(key, int): ridică TypeError dacă cheie <


0: ridică IndexError

170
Machine Translated by Google

Capitolul 9 Metode magice, proprietă i i iteratoare

clasa ArithmeticSequence:

def __init__(self, start=0, step=1):


"""

Inițializați șirul aritmetic.

start - prima valoare din secvență - diferența


dicționar de
dintre
valori
două
carevalori
au fost
adiacente
modificate
pas de
modificate
utilizator- un

"""
self.start = începe # Stocați valoarea de pornire
self.step = pas # Stocați valoarea pasului
self.changed = {} # Niciun articol nu a fost modificat

def __getitem__(self, key):


"""

Obțineți un element din șirul aritmetic.


"""

check_index(cheie)

try: return self.changed[key] # Modificat? #


except KeyError: return self.start în caz contrar ... ...
+ key * self.step # calculați valoarea

def __setitem__(self, key, value):


"""

Schimbați un element din succesiunea aritmetică.


"""

check_index(cheie)

self.changed[cheie] = valoare # Stocați valoarea modificată

Aceasta implementează o secvență aritmetică - o secvență de numere în care fiecare este mai mare decât
precedentul cu o cantitate constantă. Prima valoare este dată de parametrul constructor start (implicit la
zero), în timp ce pasul dintre valori este dat de pas (implicit la unu). Permiteți utilizatorului să modifice unele
dintre elemente păstrând excepțiile de la regula generală într-un dicționar numit modificat. Dacă elementul
nu a fost modificat, acesta este calculat ca self.start + tastă * self.step.
Iată un exemplu despre cum puteți folosi această clasă:

>>> s = ArithmeticSequence(1, 2) >>>


s[4] 9 >>> s[4] = 2 >>> s[4] 2 >>> s[5]
11

171
Machine Translated by Google

Capitolul 9 Metode magice, proprietă i i iteratoare

Rețineți că vreau să fie ilegal să ștergeți elemente, motiv pentru care nu am implementat __del__:

>>> del s[4]


Traceback (cel mai recent apel ultimul):
fișierul „<stdin>”, linia 1, în ?
AttributeError: instanța ArithmeticSequence nu are atributul „__delitem__”

De asemenea, clasa nu are metoda __len__ deoarece este de lungime infinită.


Dacă se folosește un tip ilegal de index, se afișează o TypeError, iar dacă indexul este tipul corect, dar în afara
intervalului (adică negativ în acest caz), este generată o IndexError.

>>> s[„patru”]
Traceback (cel mai recent apel ultimul):
fișierul „<stdin>”, linia 1, în ?
Fișierul „arithseq.py”, rândul 31, în __getitem__
check_index(key)
Fișierul „arithseq.py”, rândul 10, în checkIndex
dacă nu isinstance(key, int): ridică TypeError TypeError
>>> s[-42]

Traceback (cel mai recent apel ultimul):


fișierul „<stdin>”, linia 1, în ?
Fișierul „arithseq.py”, rândul 31, în __getitem__
check_index(key)
Fișierul „arithseq.py”, linia 11, în checkIndex dacă cheia
< 0: ridicați IndexError IndexError

Verificarea indexului este asigurată de o funcție de utilitate pe care am scris-o în acest scop, check_index.

Lista de subclasare, dict și str În timp ce cele

patru metode ale protocolului de secvență/mapping de bază vă vor duce departe, secvențele pot avea multe alte metode
magice și obișnuite utile, inclusiv metoda __iter__, pe care o descriu în secțiunea „Iteratoare” mai târziu în acest capitol.
Implementarea tuturor acestor metode este multă muncă și greu de realizat corect. Dacă doriți un comportament
personalizat doar în una dintre operațiuni, nu are sens să trebuiască să le reimplementați pe toate celelalte. Este doar
lenea programatorului (numită și bun simț).
Deci ce ar trebui să faci? Cuvântul magic este moștenire. De ce să reimplementați toate aceste lucruri când le
puteți moșteni? Biblioteca standard vine cu clase de bază abstracte și concrete în modulul de colecții, dar puteți, de
asemenea, pur și simplu subclasifica tipurile încorporate în sine. Deci, dacă doriți să implementați un tip de secvență
care se comportă similar listelor încorporate, puteți pur și simplu să subclasați lista.
Să facem doar un exemplu rapid - o listă cu un contor de acces.

clasa CounterList(listă): def


__init__(self, *args):
super().__init__(*args)
self.counter = 0 def
__getitem__(self, index): self.counter
+= 1 return super(CounterList,
self) .__getitem__(index)

172
Machine Translated by Google

Capitolul 9 Metode magice, proprietă i i iteratoare

Clasa CounterList se bazează în mare măsură pe comportamentul superclasei (listei) subclasei sale. Orice metodă care
nu este suprascrisă de CounterList (cum ar fi adăugarea, extinderea, indexarea și așa mai departe) poate fi utilizată
direct. În cele două metode care sunt suprascrise, super este folosit pentru a apela versiunea superclasă a metodei,
adăugând doar comportamentul necesar de inițializare a atributului counter (în __init__) și actualizare a atributului counter
(în __getitem__).

Notă Suprascrierea __getitem__ nu este o modalitate sigură de a capta accesul utilizatorilor, deoarece există și
alte modalități de accesare a conținutului listei, cum ar fi prin metoda pop .

Iată un exemplu despre cum poate fi utilizat CounterList:

>>> cl = CounterList(range(10)) >>> cl [0, 1,


2, 3, 4, 5, 6, 7, 8, 9] >>> cl.reverse() >>> cl
[ 9, 8, 7, 6, 5, 4, 3, 2, 1, 0] >>> del cl[3:6]
>>> cl [9, 8, 7, 3, 2, 1, 0] > >> cl.counter

0 >>> cl[4] + cl[2] 9 >>>


cl.contor

După cum puteți vedea, CounterList funcționează la fel ca lista în majoritatea privințelor. Cu toate acestea, are un
atribut counter (inițial zero), care este incrementat de fiecare dată când accesați un element de listă. După efectuarea
adunării cl[4] + cl[2], contorul a fost mărit de două ori, până la valoarea 2.

Mai multă magie


Nume speciale (magice) există pentru mai multe scopuri – ceea ce v-am arătat până acum este doar o mică mostră a ceea
ce este posibil. Cele mai multe dintre metodele magice disponibile sunt destinate utilizării destul de avansate, așa că nu voi
intra în detalii aici. Cu toate acestea, dacă sunteți interesat, este posibil să emulați numere, să creați obiecte care pot fi
numite ca și cum ar fi funcții, să influențați modul în care sunt comparate obiectele și multe altele. Pentru mai multe
informații despre metodele magice disponibile, consultați secțiunea „Nume speciale de metode” din Manualul de referință Python.

173
Machine Translated by Google

Capitolul 9 Metode magice, proprietă i i iteratoare

Proprietăți
În capitolul 7, am menționat metodele accesorii. Accesorii sunt pur și simplu metode cu nume precum getHeight și
setHeight și sunt folosite pentru a prelua sau relega un anumit atribut (care poate fi privat pentru clasă - vezi secțiunea
„Confidențialitate Revizuită” din Capitolul 7). Încapsularea variabilelor de stare (atributelor) ca aceasta poate fi
importantă dacă anumite acțiuni trebuie întreprinse la accesarea atributului dat. De exemplu, luați în considerare
următoarea clasă Rectangle:

class Dreptunghi:
def __init__(self):
self.width = 0
self.height = 0 def
set_size(self, size): self.width,
self.height = dimensiune def
get_size(self): return self.width, self.height

Iată un exemplu despre cum puteți folosi clasa:

>>> r = Dreptunghi() >>>


r.lățime = 10
>>> r.height = 5 >>>
r.get_size() (10, 5)
>>> r.set_size((150,
100)) >>> r.width

150

Metodele get_size și set_size sunt accesorii pentru un atribut fictiv numit dimensiune, care este pur și simplu tuplu
format din lățime și înălțime. (Nu ezitați să înlocuiți acest lucru cu ceva mai interesant, cum ar fi aria dreptunghiului
sau lungimea diagonalei acestuia.) Acest cod nu este direct greșit, dar este defectuos. Programatorul care folosește
această clasă nu ar trebui să-și facă griji cu privire la modul în care este implementată (încapsulare). Dacă într-o zi ați
vrut să schimbați implementarea, astfel încât dimensiunea să fie un atribut real și lățimea și înălțimea să fie calculate
din mers, ar trebui să le înfășurați în accesorii și orice programe care folosesc clasa ar trebui, de asemenea, rescrise.
Codul client (codul care utilizează codul dvs.) ar trebui să poată trata toate atributele dvs. în același mod.

Deci care este soluția? Ar trebui să-ți împachetezi toate atributele în accesorii? Aceasta este o posibilitate, desigur.
Cu toate acestea, ar fi nepractic (și cam prostesc) dacă ai avea o mulțime de atribute simple, pentru că ar trebui să scrii
mulți accesori care nu au făcut altceva decât să recupereze sau să stabilească aceste atribute, fără a fi luată nicio
acțiune utilă. Acest lucru miroase a programare copy-paste , sau cod cookiecutter, care este în mod clar un lucru rău
(deși destul de comun pentru această problemă specifică în anumite limbi). Din fericire, Python vă poate ascunde
accesoriile pentru dvs., făcând ca toate atributele dvs. să semene. Acele atribute care sunt definite prin accesorii lor
sunt adesea numite proprietăți.
Python are de fapt două mecanisme pentru a crea proprietăți în Python. Mă voi concentra pe cea mai recentă,
funcția de proprietate, care funcționează doar pe clase de stil nou. Apoi vă voi oferi o scurtă descriere a modului de
implementare a proprietăților cu metode magice.

174
Machine Translated by Google

Capitolul 9 Metode magice, proprietă i i iteratoare

Proprietatea Funcție
Utilizarea funcției de proprietate este încântător de simplă. Dacă ați scris deja o clasă precum Rectangle din secțiunea
anterioară, trebuie să adăugați doar o singură linie de cod.

clasa Dreptunghi: def


__init__ (self): self.width = 0

self.height = 0 def
set_size(self, size): self.width,
self.height = dimensiune def get_size(self):
return self.width, self.height size = property
(get_size, set_size)

În această nouă versiune de Rectangle, o proprietate este creată cu funcția de proprietate cu funcțiile accesorii ca
argumente ( întâi getter , apoi setter), iar dimensiunea numelui este apoi legată de această proprietate.
După aceasta, nu mai trebuie să vă faceți griji cu privire la modul în care sunt implementate lucrurile, dar puteți trata lățimea, înălțimea și
dimensiunea în același mod.

>>> r = Dreptunghi() >>>


r.lățime = 10
>>> r.inaltime = 5 >>>
r.dimensiune
(10, 5)
>>> r.size = 150, 100 >>>
r.width
150

După cum puteți vedea, atributul size este încă supus calculelor din get_size și set_size, dar arată exact ca un atribut normal.

Notă Dacă proprietă ile dumneavoastră se comportă ciudat, asigura i-vă că utiliza i o clasă cu stil nou (prin
subclasarea obiectului fie direct, fie indirect – sau setând metaclasa direct). Dacă nu sunteți, partea getter a proprietății
va funcționa în continuare, dar setter-ul s-ar putea să nu (în funcție de versiunea dvs. Python). Acest lucru poate fi puțin confuz.

De fapt, funcția de proprietate poate fi apelată și cu zero, unu, trei sau patru argumente. Dacă este apelată fără niciun
argument, proprietatea rezultată nu este nici citibilă, nici scrisă. Dacă este apelată cu un singur argument (o metodă getter),
proprietatea este doar lizibilă. Al treilea argument (opțional) este o metodă folosită pentru a șterge atributul (nu necesită
argumente). Al patrulea argument (opțional) este un șir documentar. Parametrii se numesc fget, fset, fdel și doc — îi puteți
folosi ca argumente de cuvinte cheie dacă doriți o proprietate care, de exemplu, poate fi scrisă doar și are un șir de documente.

Deși această secțiune a fost scurtă (o dovadă a simplității funcției de proprietate), este foarte
important. Morala este aceasta: cu clase de stil nou, ar trebui să folosiți proprietăți mai degrabă decât accesorii.

175
Machine Translated by Google

Capitolul 9 Metode magice, proprietă i i iteratoare

DAR CUM FUNCTIONEAZA?

În cazul în care sunteți curios despre modul în care proprietatea își face magia, vă voi oferi o explicație aici. Dacă
nu-ți pasă, sări înainte.

Faptul este că proprietatea nu este cu adevărat o funcție – este o clasă ale cărei instanțe au niște metode magice
care fac toată treaba. Metodele în cauză sunt __get__, __set__ și __delete__. Împreună, aceste trei metode definesc
așa-numitul protocol descriptor. Un obiect care implementează oricare dintre aceste metode este un descriptor.
Lucrul special despre descriptori este modul în care sunt accesați. De exemplu, atunci când citiți un atribut (în
special, când îl accesați într-o instanță, dar când atributul este definit în clasă), dacă atributul este legat de un
obiect care implementează __get__, obiectul nu va fi pur și simplu returnat; în schimb, metoda __get__ va fi
apelată, iar valoarea rezultată va fi returnată. Acesta este, de fapt, mecanismul care stă la baza proprietăților,
metodelor legate, metodelor statice și de clasă (consultați secțiunea următoare pentru mai multe informații) și
super.

Pentru mai multe despre descriptori, consultați Ghidul Descriptor HowTo (https://docs.python.org/3/howto/
descriptor.html ).

Metode statice și metode de clasă


Înainte de a discuta despre vechiul mod de implementare a proprietăților, să facem un ocol ușor și să ne uităm la
alte două caracteristici care sunt implementate într-o manieră similară cu proprietățile de stil nou. Metodele statice și
metodele de clasă sunt create prin împachetarea metodelor în obiecte din clasele staticmethod și, respectiv, classmethod.
Metodele statice sunt definite fără argumente proprii și pot fi apelate direct pe clasă în sine. Metodele de clasă sunt
definite cu un parametru asemănător, numit în mod normal cls. Puteți apela metode de clasă direct și pe obiectul clasei,
dar parametrul cls este legat automat de clasă. Iată un exemplu simplu:

clasa MyClass:

def smeth():
print('Aceasta este o metodă statică')
smeth = staticmethod(smeth)

def cmeth(cls):
print('Aceasta este o metodă de clasă a', cls) cmeth
= classmethod(cmeth)

Tehnica de împachetare și înlocuire manuală a metodelor astfel este puțin plictisitoare. În Python 2.4, a fost introdusă
o nouă sintaxă pentru metode de împachetare ca aceasta, numită decoratori. (De fapt funcționează cu orice obiect
apelabil ca învelișuri și pot fi utilizate atât pentru metode, cât și pentru funcții.) Specificați unul sau mai mulți decoratori
(care sunt aplicați în ordine inversă) listându-i deasupra metodei (sau funcției), folosind operatorul @ .

176
Machine Translated by Google

Capitolul 9 Metode magice, proprietă i i iteratoare

clasa MyClass:

@staticmethod
def smeth():
print('Aceasta este o metodă statică')

@classmethod
def cmeth(cls):
print('Aceasta este o metodă de clasă a', cls)

Odată ce ați definit aceste metode, ele pot fi folosite astfel (adică fără instanțierea clasei):

>>> MyClass.smeth()
Aceasta este o metodă statică
>>> MyClass.cmeth()
Aceasta este o metodă de clasă a <class '__main__.MyClass'>

Metodele statice și metodele de clasă nu au fost importante din punct de vedere istoric în Python, în principal pentru că
puteți utiliza întotdeauna funcții sau metode legate, într-un fel, dar și pentru că suportul nu a existat cu adevărat în versiunile
anterioare. Deci, chiar dacă este posibil să nu le vedeți foarte folosite în codul actual, ele au utilizările lor (cum ar fi funcțiile
din fabrică, dacă ați auzit de acestea) și s-ar putea să vă gândiți la unele noi
cele.

Notă Puteți utiliza de fapt sintaxa decoratorului și cu proprietăți. Consultați documentația privind funcția
de proprietate pentru detalii.

__getattr__, __setattr__ și Prieteni


Este posibil să interceptați fiecare acces la atribut pe un obiect. Printre altele, puteți folosi acest lucru pentru a
implementa proprietăți cu clase de stil vechi (unde proprietatea nu va funcționa neapărat așa cum ar trebui). Pentru a
executa codul atunci când este accesat un atribut, trebuie să utilizați câteva metode magice. Următoarele patru oferă toate
funcționalitățile de care aveți nevoie (în clasele în stil vechi, folosiți doar ultimele trei):

__getattribute__(self, name): apelat automat când este accesat numele atributului. (Acest lucru
funcționează corect numai pentru clasele de stil nou.)

__getattr__(self, name): Apelat automat atunci când numele atributului este accesat și
obiectul nu are un astfel de atribut.

__setattr__(self, name, value): Apelat automat atunci când se încearcă legarea numelui
atributului la valoare.

__delattr__(self, name): Apelat automat când se încearcă ștergerea numelui atributului.

Deși un pic mai dificil de utilizat (și în anumite privințe mai puțin eficiente) decât proprietatea, aceste metode magice sunt
destul de puternice, deoarece puteți scrie cod într-una dintre aceste metode care se ocupă de mai multe proprietăți.
(Totuși, dacă aveți de ales, rămâneți cu proprietatea.)

177
Machine Translated by Google

Capitolul 9 Metode magice, proprietă i i iteratoare

Iată din nou exemplul dreptunghiului, de data aceasta cu metode magice:

clasă Dreptunghi: def


__init__ (self): self.width = 0
self.height = 0 def
__setattr__(self, name,
value): if name == 'size': self.width, self.height =
valoare else: self. __dict__[nume] = valoare
def __getattr__(self, name): if name == 'size':
return self.width, self.height else: raise
AttributeError()

După cum puteți vedea, această versiune a clasei trebuie să aibă grijă de detalii administrative suplimentare. Când
luăm în considerare acest exemplu de cod, este important să rețineți următoarele:

•Metoda __setattr__ este apelată chiar dacă atributul în cauză nu este dimensiunea.
Prin urmare, metoda trebuie să ia în considerare ambele cazuri: dacă atributul este
dimensiunea, se efectuează aceeași operație ca și înainte; în caz contrar, se folosește atributul
magic __dict__. Conține un dicționar cu toate atributele instanței. Este folosit în locul atribuirii
obișnuite de atribute pentru a evita apelarea __setattr__ din nou (ceea ce ar face ca programul să
circule la nesfârșit).

•Metoda __getattr__ este apelată numai dacă nu este găsit un atribut normal, care
înseamnă că dacă numele dat nu este dimensiunea, atributul nu există, iar metoda generează un
AttributeError. Acest lucru este important dacă doriți ca clasa să funcționeze corect cu funcții
încorporate, cum ar fi hasattr și getattr. Dacă numele este dimensiune, se folosește expresia găsită
în implementarea anterioară.

Notă La fel cum există o capcană „buclă fără sfârșit” asociată cu __setattr__, există o capcană asociată cu
__getattribute__. Deoarece interceptează toate accesările la atribute (în clasele de stil nou), va intercepta și
accesele la __dict__ ! Singura modalitate sigură de a accesa atributele pe sine în interiorul __getattribute__ este
să utilizați metoda __getattribute__ a superclasei (folosind super).

Iteratori
Am menționat pe scurt iteratorii (și iterabilii) în capitolele precedente. În această secțiune, intru în mai multe detalii. Acopăr
o singură metodă magică, __iter__, care este baza protocolului iterator.

Protocolul Iterator
A itera înseamnă a repeta ceva de mai multe ori - ceea ce faci cu bucle. Până acum am iterat doar secvențe și dicționare în
bucle for, dar adevărul este că puteți itera și peste alte obiecte: obiecte care implementează metoda __iter__.

178
Machine Translated by Google

Capitolul 9 Metode magice, proprietă i i iteratoare

Metoda __iter__ returnează un iterator, care este orice obiect cu o metodă numită __next__, care poate
fi apelată fără niciun argument. Când apelați metoda __next__, iteratorul ar trebui să returneze „următoarea
valoare”. Dacă metoda este apelată și iteratorul nu mai are valori de returnat, ar trebui să ridice o excepție
StopIteration. Există o funcție de confort încorporată numită next pe care o puteți utiliza, unde next(it) este
echivalent cu aceasta.__next__().

Notă Protocolul iterator este puțin modificat în Python 3. În protocolul vechi, obiectele iteratoare ar trebui să
aibă o metodă numită next în loc de __next__.

Care-i rostul? De ce să nu folosești doar o listă? Pentru că deseori poate fi exagerat. Dacă aveți o funcție care poate
calcula valorile una câte una, este posibil să aveți nevoie de ele doar una câte una — nu toate odată, într-o listă, de exemplu.
Dacă numărul de valori este mare, lista poate ocupa prea multă memorie. Dar există și alte motive: utilizarea iteratoarelor
este mai generală, mai simplă și mai elegantă. Să aruncăm o privire la un exemplu pe care nu l-ai putea face cu o listă, pur
și simplu pentru că lista ar trebui să fie de lungime infinită!
„lista” noastră este succesiunea numerelor Fibonacci. Un iterator pentru acestea ar putea fi următorul:

clasa Fibs:
def __init__(self):
self.a = 0 self.b
= 1 def
__next__(self): self.a,
self.b = self.b, self.a + self.b return self.a def
__iter__ (self): intoarcerea sinelui

Rețineți că iteratorul implementează metoda __iter__, care, de fapt, va returna iteratorul însuși. În multe cazuri, ați pune
metoda __iter__ într- un alt obiect, pe care l-ați folosi în bucla for. Aceasta ar returna apoi iteratorul. Se recomandă ca
iteratorii să implementeze o metodă __iter__ proprie în plus (returning self, la fel cum am făcut aici), astfel încât ei înșiși
să poată fi utilizați direct în bucle for.

Notă Mai formal, un obiect care implementează metoda __iter__ este iterabil, iar obiectul care
implementează următorul este iteratorul.

Mai întâi, faceți un obiect Fibs.

>>> fibs = Fibs()

Apoi îl puteți folosi într-o buclă for - de exemplu, pentru a găsi cel mai mic număr Fibonacci care este mai mare de
1.000.

>>> pentru f în fibs:


... dacă f > 1000:
... print(f)
... break
...
1597

179
Machine Translated by Google

Capitolul 9 Metode magice, proprietă i i iteratoare

Aici, bucla se oprește deoarece emit o pauză în interiorul ei; dacă nu aș face-o, bucla for nu s-ar termina niciodată.

Sfat Funcția încorporată iter poate fi utilizată pentru a obține un iterator de la un obiect iterabil.

>>> it = iter([1, 2, 3]) >>>


next(it)
1

>>> urmatorul(asta)

Poate fi folosit și pentru a crea un iterabil dintr-o funcție sau alt obiect apelabil (consultați referința bibliotecii pentru
detalii).

Crearea de secvențe din iteratoare În plus față de

iterarea peste iteratoare și iterabile (care este ceea ce faceți în mod normal), le puteți converti în secvențe.
În majoritatea contextelor în care puteți utiliza o secvență (cu excepția operațiunilor precum indexarea sau
tăierea), puteți utiliza în schimb un iterator (sau un obiect iterabil). Un exemplu util în acest sens este conversia
explicită a unui iterator într-o listă folosind constructorul de listă.

>>> clasa TestIterator:


... valoare = 0
... def __next__(self):
... self.value += 1
... dacă self.value > 10: ridică StopIteration
... return self.value
... def __iter__(self):
... return self
...
>>> ti = TestIterator() >>>
list(ti) [1, 2, 3, 4, 5, 6, 7,
8, 9, 10]

Generatoare
Generatoarele (numite și generatoare simple din motive istorice) sunt relativ noi pentru Python și sunt (împreună
cu iteratoarele) poate una dintre cele mai puternice caracteristici care au apărut de ani de zile. Cu toate acestea,
conceptul de generator este destul de avansat și poate dura ceva timp până când „face clic” și veți vedea cum
funcționează sau cum v-ar fi util. Fii sigur că, în timp ce generatoarele te pot ajuta să scrii cod cu adevărat elegant,
cu siguranță poți scrie orice program dorești fără urmă de generatoare.

180
Machine Translated by Google

Capitolul 9 Metode magice, proprietă i i iteratoare

Un generator este un fel de iterator care este definit cu sintaxa funcției normale. Exact cum funcționează
generatoarele este cel mai bine arătat prin exemple. Să aruncăm mai întâi o privire la modul în care le faci și le
folosești, apoi aruncăm o privire sub capotă.

Realizarea unui generator

Realizarea unui generator este simplă; este ca și cum ai face o funcție. Sunt sigur că începi să te plictisești de vechea și
bună secvență Fibonacci până acum, așa că lasă-mă să fac altceva. Voi crea o funcție care aplatizează listele imbricate.
Argumentul este o listă care poate arăta cam așa:

imbricat = [[1, 2], [3, 4], [5]]

Cu alte cuvinte, este o listă de liste. Funcția mea ar trebui să-mi dea numerele în ordine. Iată o soluție:

def flatten(imbricat):
pentru sublistă în imbricat:
pentru element din sublistă:
element de randament

Majoritatea acestei funcții este destul de simplă. În primul rând, iterează peste toate sublistele din lista imbricată
furnizată; apoi repetă elementele fiecărei subliste în ordine. Dacă ultima linie ar fi fost print(element), de exemplu,
funcția ar fi fost ușor de înțeles, nu?
Deci, ceea ce este nou aici este declarația de randament. Orice funcție care conține o declarație de randament
se numește generator. Și nu este doar o chestiune de denumire; se va comporta destul de diferit față de funcțiile
obișnuite. Diferența este că, în loc să returnați o singură valoare, așa cum faceți cu returnarea, puteți obține mai
multe valori, una câte una. De fiecare dată când se obține o valoare (cu randament), funcția se blochează; adică își
oprește execuția exact în acel punct și așteaptă să fie trezit din nou. Când este, își reia execuția în punctul în care s-a oprit.
Pot folosi toate valorile iterând peste generator.

>>> imbricat = [[1, 2], [3, 4], [5]] >>> pentru


num în flatten(imbricat): print(num)
...
...
1
2
3
4
5

sau

>>> listă(aplatizare (imbricat))


[1, 2, 3, 4, 5]

181
Machine Translated by Google

Capitolul 9 Metode magice, proprietă i i iteratoare

GENERATOARE LOOPY

În Python 2.4, a fost introdusă o relativă a înțelegerii listelor (vezi Capitolul 5): înțelegerea
generatorului (sau expresii generatoare). Funcționează în același mod ca și înțelegerea listei, cu excepția faptului că
o listă nu este construită (și „corpul” nu este trecut imediat în buclă). În schimb, este returnat un generator,
permițându-vă să efectuați calculul pas cu pas.

>>> g = ((i + 2) ** 2 pentru i în intervalul (2, 27)) >>>


următorul (g)
16

După cum puteți vedea, aceasta diferă de înțelegerea listei în utilizarea parantezelor simple. Într-un caz simplu ca
acesta, aș fi putut la fel de bine să fi folosit o listă de înțelegere. Cu toate acestea, dacă doriți să „împachetați” un
obiect iterabil (posibil să producă un număr mare de valori), o înțelegere a listei ar anula avantajele iterării prin
instanțierea imediată a unei liste.

Un bonus bun este că, atunci când utilizați înțelegerea generatorului direct într-o pereche de paranteze existente,
cum ar fi într-un apel de funcție, nu este nevoie să adăugați o altă pereche. Cu alte cuvinte, puteți scrie un cod frumos
ca acesta:

sumă (i ** 2 pentru i în intervalul (10))

Un generator recursiv
Generatorul pe care l-am proiectat în secțiunea anterioară putea să se ocupe doar de liste imbricate cu două niveluri
adânc, iar pentru a face asta a folosit două bucle for. Ce se întâmplă dacă aveți un set de liste imbricate în mod arbitrar
profund? Poate le folosiți pentru a reprezenta o structură de arbore, de exemplu. (Puteți face asta și cu anumite clase de
arbori, dar strategia este aceeași.) Aveți nevoie de o buclă for pentru fiecare nivel de imbricare, dar pentru că nu știți câte
niveluri există, trebuie să vă schimbați soluția pentru a fi mai flexibil. Este timpul să apelăm la magia recursiunii.

def flatten(imbricat):
încercați: pentru
sublista în imbricat:
pentru elementul din flatten(sublist):
element de randament, cu excepția
TypeError: yield imbricat

Când se apelează aplatizare, aveți două posibilități (cum este întotdeauna cazul când se ocupă de recursivitate): cazul de
bază și cazul recursiv . În cazul de bază, funcției i se spune să aplatizeze un singur element (de exemplu, un număr), caz în
care bucla for ridică o Eroare de tip (pentru că încercați să repetați peste un număr), iar generatorul pur și simplu produce
element.
Dacă vi se spune să aplatizați o listă (sau orice iterabil), totuși, trebuie să lucrați. Treci prin toate sublistele (dintre
care unele pot să nu fie chiar liste) și apelezi la aplatizarea lor. Apoi obțineți toate elementele sublistelor aplatizate folosind
o altă buclă for. Poate părea ușor magic, dar funcționează.

>>> listă(platit([[[1], 2], 3, 4, [5, [6, 7]], 8])) [1, 2, 3, 4, 5, 6, 7, 8 ]

182
Machine Translated by Google

Capitolul 9 Metode magice, proprietă i i iteratoare

Cu toate acestea, există o problemă. Dacă imbricat este un șir sau un obiect asemănător șirului, este o secvență
și nu va genera TypeError, dar nu doriți să repetați peste el.

Notă Există două motive principale pentru care nu ar trebui să iterați asupra obiectelor de tip șir în funcția de

aplatizare . În primul rând, doriți să tratați obiectele asemănătoare șirurilor ca valori atomice, nu ca secvențe care ar trebui aplatizate.

În al doilea rând, repetarea peste ele ar duce de fapt la recursivitate infinită, deoarece primul element al unui șir este un

alt șir de lungime, iar primul element al acelui șir este șirul în sine!

Pentru a face față acestui lucru, trebuie să adăugați un test la începutul generatorului. Încercarea de a concatena obiectul
cu un șir și de a vedea dacă rezultă o Eroare TypeError este cea mai simplă și rapidă modalitate de a verifica dacă un obiect
este ca șir.1 Iată generatorul cu testul adăugat:

def flatten (imbricat):


încercați:
# Nu repetați peste obiecte de tip șir: încercați:
''
imbricat + cu excepția TypeError: pass else: ridicați
TypeError pentru sublistă în imbricat:

pentru elementul din flatten(sublist):


element de randament, cu excepția
TypeError: yield imbricat

''
După cum puteți vedea, dacă expresia imbricată + generează o Eroare de tip, aceasta este ignorată; totuși, dacă expresia
nu generează o TypeError, clauza else a instrucțiunii interioare try generează o TypeError proprie. Acest lucru face ca
obiectul asemănător șirului să fie prezentat așa cum este (în clauza exterioară except). Am în eles?
Iată un exemplu pentru a demonstra că această versiune funcționează și cu șiruri de caractere:

>>> listă(platten(['foo', ['bar', ['baz']]])) ['foo', 'bar', 'baz']

Rețineți că nu există nicio verificare de tip aici. Nu testez dacă imbricat este un șir, doar dacă se comportă ca
unul (adică poate fi concatenat cu un șir). O alternativă naturală la acest test ar fi utilizarea isinstance cu o
superclasă abstractă pentru șiruri și obiecte asemănătoare șirurilor, dar, din păcate, nu există o astfel de clasă
standard. Și verificarea tipului față de str nu ar funcționa nici măcar pentru UserString.

Generatoare în general

Dacă ai urmat exemplele până acum, știi să folosești generatoarele, mai mult sau mai puțin. Ați văzut că un
generator este o funcție care conține cuvântul cheie randament. Când este apelat, codul din corpul funcției nu este
executat. În schimb, este returnat un iterator. De fiecare dată când se solicită o valoare, codul din generator este executat
până când se întâlnește un randament sau un randament. Un randament înseamnă că ar trebui să se obțină o valoare.
Un return înseamnă că generatorul ar trebui să se oprească din execuție (fără a da mai nimic; return poate fi apelat fără
argumente numai atunci când este utilizat în interiorul unui generator).

1 Mulțumim lui Alex Martelli pentru că a subliniat acest mod și importanța folosirii lui aici.

183
Machine Translated by Google

Capitolul 9 Metode magice, proprietă i i iteratoare

Cu alte cuvinte, generatoarele constau din două componente separate: funcția- generator și iteratorul-generator.
Funcția-generator este ceea ce este definit de instrucțiunea def care conține un randament. Generatorul-iteratorul este ceea ce
returnează această funcție. În termeni mai puțin precisi, aceste două entități sunt adesea tratate ca una și denumite colectiv un
generator.

>>> def simple_generator(): randament


1
...
>>> simple_generator
<funcție simple_generator la 153b44> >>>
simple_generator() <obiect generator la 1510b0>

Iteratorul returnat de funcția-generator poate fi folosit la fel ca orice alt iterator.

Metode generatoare
Putem furniza generatoarelor valori după ce au început să funcționeze, utilizând un canal de comunicații între generator și „lumea
exterioară”, cu următoarele două puncte finale:

•Lumea exterioară are acces la o metodă pe generator numită send, care funcționează la fel ca următorul,
cu excepția faptului că este nevoie de un singur argument („mesajul” de trimis—un obiect arbitrar).

•În interiorul generatorului suspendat, randamentul poate fi acum folosit ca o expresie, mai degrabă
decât o declarație. Cu alte cuvinte, atunci când generatorul este reluat, yield returnează o valoare
- valoarea trimisă din exterior prin send. Dacă a fost folosit următorul, randamentul returnează Nimic.

Rețineți că utilizarea send (mai degrabă decât următoarea) are sens numai după ce generatorul a fost suspendat (adică după
ce a atins primul randament). Dacă trebuie să oferiți câteva informații generatorului înainte de aceasta, puteți utiliza pur și
simplu parametrii funcției generatorului.

Sfat Dacă doriți cu adevărat să utilizați trimiterea pe un generator nou pornit, îl puteți utiliza cu None ca parametru.

Iată un exemplu destul de prostesc care ilustrează mecanismul:

def repetitor(valoare): în
timp ce Adevărat: nou
= (valoare de randament)
dacă nou nu este Niciunul: valoare = nou

Iată un exemplu de utilizare a acestuia:

>>> r = repetitor(42) >>>


următorul(r)
42
>>> r.send(„Bună ziua, lume!”)
"Salut Lume!"

184
Machine Translated by Google

Capitolul 9 Metode magice, proprietă i i iteratoare

Observați utilizarea parantezelor în jurul expresiei randament. Deși nu este strict necesar în unele cazuri, probabil că este mai bine
să fii sigur decât să-ți pară rău și pur și simplu să includeți întotdeauna expresiile de randament între paranteze dacă utilizați valoarea
returnată într-un fel.
Generatoarele au și alte două metode.

•Metoda throw (numită cu un tip de excepție, o valoare opțională și un obiect de urmărire) este utilizată pentru
a ridica o excepție în interiorul generatorului (la expresia de randament).

•Metoda close (numită fără argumente) este folosită pentru a opri generatorul.

Metoda close (care este numită și de către colectorul de gunoi Python, atunci când este necesar) se bazează și pe excepții. Aceasta
ridică excepția GeneratorExit la punctul de randament, așa că dacă doriți să aveți un cod de curățare în generator, vă puteți încheia
randamentul într-o declarație try/finally. Dacă doriți, puteți prinde și excepția GeneratorExit, dar apoi trebuie să o ridicați din nou
(eventual după ce ați curățat puțin), să ridicați o altă excepție sau pur și simplu să reveniți. Încercarea de a obține o valoare de la un
generator după ce a fost apelată închiderea acestuia va avea ca rezultat o RuntimeError.

Sfat Pentru mai multe informații despre metodele generatoarelor și despre modul în care acestea transformă generatoarele

în corutine simple, consultați PEP 342 (www.python.org/dev/peps/pep-0342/).

Simularea generatoarelor
Dacă trebuie să utilizați o versiune mai veche de Python, generatoarele nu sunt disponibile. Ceea ce urmează este o rețetă simplă de
simulare a acestora cu funcții normale.
Începând cu codul pentru generator, începeți prin a introduce următoarea linie la începutul corpului funcției:

rezultat = []

Dacă codul folosește deja rezultatul numelui, ar trebui să veniți cu altul. (Folosirea unui nume mai descriptiv poate fi oricum o idee bună.)
Apoi înlocuiți toate rândurile acestui formular:

produce some_expression cu aceasta:


result.append(some_expression)

În cele din urmă, la sfârșitul funcției, adăugați această linie:

returnează rezultatul

Deși este posibil să nu funcționeze cu toate generatoarele, funcționează cu majoritatea. (De exemplu, eșuează cu generatoare
infinite, care, desigur, nu își pot introduce valorile într-o listă.)

185
Machine Translated by Google

Capitolul 9 Metode magice, proprietă i i iteratoare

Iată generatorul de aplatizare rescris ca o funcție simplă:

def flatten(imbricat):
rezultat = [] încercați:

# Nu repetați peste obiecte de tip șir: încercați:


''
imbricat + cu excepția TypeError: pass else: ridicați
TypeError pentru sublistă în imbricat:

pentru elementul din flatten(sublist):


result.append(element) cu excepția
TypeError: result.append(nested) returnează rezultat

Cele Opt Regine


Acum că ați învățat despre toată această magie, este timpul să o puneți la lucru. În această secțiune, vedeți cum să utilizați
generatoarele pentru a rezolva o problemă clasică de programare.

Generatoarele și generatoarele de backtracking

sunt ideale pentru algoritmi recursivi complecși care construiesc treptat un rezultat. Fără generatoare, acești
algoritmi necesită de obicei să treceți o soluție pe jumătate construită ca un parametru suplimentar, astfel încât
apelurile recursive să se poată construi pe ea. Cu generatoare, tot ce trebuie să facă apelurile recursive este să cedeze
partea lor. Asta am făcut cu versiunea recursivă anterioară a flatten și puteți folosi exact aceeași strategie pentru a
parcurge grafice și structuri arborescente.
În unele aplicații, însă, nu primiți răspunsul imediat; trebuie să încerci mai multe alternative,
și trebuie să faci asta la fiecare nivel al recursiunii tale. Pentru a face o paralelă cu viața reală, imaginați-vă că aveți o
întâlnire importantă la care să participați. Nu ești sigur unde se află, dar ai două uși în fața ta, iar sala de ședințe trebuie
să fie în spatele uneia dintre ele. Alegi stânga și treci prin. Acolo, te confrunți cu alte două uși. Alegi stânga, dar se
dovedește a fi greșit. Așa că dați înapoi și alegeți ușa potrivită, care se dovedește a fi și greșită (scuzați jocul de cuvinte).
Deci, dați înapoi din nou, până la punctul de unde ați început, gata să încercați ușa potrivită acolo.

GRAFICE ȘI ARBORI

Dacă nu ați auzit niciodată de grafice și arbori până acum, ar trebui să aflați despre ele cât mai curând
posibil, deoarece sunt concepte foarte importante în programare și informatică. Pentru a afla mai
multe, probabil că ar trebui să obțineți o carte despre informatică, matematică discretă, structuri de
date sau algoritmi. Pentru câteva definiții concise, puteți consulta următoarele pagini web:

• http://mathworld.wolfram.com/Graph.html

• http://mathworld.wolfram.com/Tree.html

• www.nist.gov/dads/HTML/tree.html

• www.nist.gov/dads/HTML/graph.html

186
Machine Translated by Google

Capitolul 9 Metode magice, proprietă i i iteratoare

O căutare rapidă pe web sau un pic de navigare în Wikipedia (http://wikipedia.org) va apărea mult material.

Această strategie de backtracking este utilă pentru rezolvarea problemelor care vă cer să încercați fiecare combinație
până când găsiți o soluție. Astfel de probleme sunt rezolvate astfel:

# Pseudocod
pentru fiecare posibilitate la nivelul 1:
pentru fiecare posibilitate la nivelul 2:
...
pentru fiecare posibilitate la nivelul n: este
viabilă?

Pentru a implementa acest lucru direct cu buclele for , trebuie să știți câte niveluri veți întâlni. Dacă acest lucru nu este
posibil, utilizați recursiunea.

Problema
Acesta este un puzzle de informatică foarte iubit: aveți o tablă de șah și opt piese regine pe care să le plasați.
Singura cerință este ca niciuna dintre regine să nu amenințe nici una dintre celelalte; adică trebuie să le așezi astfel încât să
nu se poată captura două regine una pe cealaltă. Cum faci acest lucru? Unde ar trebui să fie plasate reginele?
Aceasta este o problemă tipică de backtracking: încercați o poziție pentru prima regină (în primul rând), avansați la al
doilea și așa mai departe. Dacă descoperiți că nu puteți plasa o regină, vă întoarceți la cea anterioară și încercați o altă poziție.
În cele din urmă, fie epuizezi toate posibilitățile, fie găsești o soluție.
În problema, așa cum sa menționat, vi se oferă informații că vor fi doar opt regine, dar
să presupunem că poate exista orice număr de matci. (Acest lucru este mai asemănător cu problemele de
backtracking din lumea reală.) Cum rezolvi asta? Dacă vrei să încerci să rezolvi singur, ar trebui să nu mai citești
acum, pentru că sunt pe cale să-ți dau soluția.

Notă Puteți găsi soluții mult mai eficiente pentru această problemă. Dacă doriți mai multe detalii, o căutare pe web ar trebui

să arate o mulțime de informații.

Reprezentarea Statului
Pentru a reprezenta o posibilă soluție (sau o parte a acesteia), puteți utiliza pur și simplu un tuplu (sau o listă, de altfel).
Fiecare element al tuplului indică poziția (adică coloana) reginei rândului corespunzător. Deci, dacă stare[0] == 3, știți că
regina din rândul unu este poziționată în coloana patru (numărăm de la zero, vă amintiți?). Când lucrați la un nivel de
recursivitate (un anumit rând), știți doar ce poziții au matcile de mai sus, așa că este posibil să aveți un tuplu de stare a cărui
lungime este mai mică de opt (sau indiferent de numărul de matci).

Rețineți că aș fi putut folosi o listă în loc de un tuplu pentru a reprezenta starea. Este mai ales o chestiune de gust în acest

caz. În general, dacă secvența este mică și statică, tuplurile pot fi o alegere bună.

187
Machine Translated by Google

Capitolul 9 Metode magice, proprietă i i iteratoare

Găsirea conflictelor Să

începem prin a face o abstractizare simplă. Pentru a găsi o configurație în care nu există conflicte (unde nicio
regină nu poate captura alta), trebuie mai întâi să definiți ce este un conflict. Și de ce să nu o definiți ca o funcție
în timp ce sunteți la ea?
Funcția de conflict primește pozițiile reginelor de până acum (sub forma unui tuplu de stare) și determină
dacă o poziție pentru următoarea regină generează noi conflicte.

def conflict(state, nextX): nextY =


len(stare) pentru i în interval
(nextY): if abs(state[i] - nextX)
în (0, nextY - i):
returnează Adevărat

return False

Parametrul nextX este poziția orizontală sugerată (coordonată x sau coloană) a următoarei matci, iar nextY este
poziția verticală (coordonată y sau rând) a următoarei matci. Această funcție face o verificare simplă pentru fiecare
dintre matcile anterioare. Dacă următoarea regină are aceeași coordonată x sau este pe aceeași diagonală ca (nextX,
nextY), a apărut un conflict și este returnat True. Dacă nu apar astfel de conflicte, se returnează False.
Partea dificilă este următoarea expresie:

abs(state[i] - nextX) în (0, nextY - i)

Este adevărat dacă distanța orizontală dintre următoarea matcă și cea anterioară luată în considerare este fie
zero (aceeași coloană) fie egală cu distanța verticală (pe diagonală). Altfel, este fals.

Cazul de bază
Problema Eight Queens poate fi puțin dificil de implementat, dar cu generatoare nu este atât de rea. Dacă nu sunteți
obișnuit cu recursiunea, nu m-aș aștepta să veniți singur cu această soluție. De asemenea, rețineți că această soluție nu
este deosebit de eficientă, așa că, cu un număr foarte mare de matci, ar putea fi puțin lentă.
Să începem cu cazul de bază: ultima regină. Ce ai vrea să facă ea? Să presupunem că vrei să găsești
toate solutiile posibile. În acest caz, te-ai aștepta ca ea să producă (generare) toate pozițiile pe care le-ar putea ocupa
(eventual niciunul) având în vedere pozițiile celorlalți. Puteți schița acest lucru direct.

def queens(num, stat):


dacă len(starea) == num-1:
pentru pos în interval (num):
dacă nu conflict (stare, pos):
randament pos

În limbajul uman, aceasta înseamnă: „Dacă toate reginele, cu excepția uneia, au fost plasate, parcurgeți toate pozițiile
posibile pentru ultima și întoarceți pozițiile care nu dau naștere niciunui conflict”. Parametrul num este numărul de
matci în total, iar parametrul de stare este tuplul de poziții pentru matcile anterioare. De exemplu, să presupunem că
aveți patru matci și că primele trei au primit pozițiile 1, 3 și, respectiv, 0, așa cum se arată în Figura 9-1. (Nu acordați
atenție reginei albe în acest moment.)

188
Machine Translated by Google

Capitolul 9 Metode magice, proprietă i i iteratoare

Figura 9-1. Plasarea a patru matci pe o tabla de 4 × 4

După cum puteți vedea în figură, fiecare regină primește un rând (orizontal), iar pozițiile reginelor sunt numerotate în
partea de sus (începând cu zero, așa cum este normal în Python).

>>> listă(regine(4, (1, 3, 0))) [2]

Funcționează ca un farmec. Folosirea listei forțează pur și simplu generatorul să furnizeze toate valorile sale. În acest caz, o
singură poziție se califică. Matca albă a fost pusă în această poziție în Figura 9-1. (Rețineți că culoarea nu are o semnificație
specială și nu face parte din program.)

Cazul recursiv
Acum să trecem la partea recursivă a soluției. Când aveți cazul de bază acoperit, cazul recursiv poate presupune în mod corect
(prin inducție) că toate rezultatele de la niveluri inferioare (reginele cu numere mai mari) sunt corecte. Deci, ceea ce trebuie să
faceți este să adăugați o clauză else la declarația if în implementarea anterioară a funcției queens.

Ce rezultate vă așteptați de la apelul recursiv? Vrei pozițiile tuturor reginelor inferioare, nu?
Să presupunem că sunt returnate ca tuplu. În acest caz, probabil că trebuie să vă schimbați cazul de bază pentru a returna
și un tuplu (de lungimea unu) - dar ajung la asta mai târziu.

189
Machine Translated by Google

Capitolul 9 Metode magice, proprietă i i iteratoare

Așadar, vi se oferă un tuplu de poziții de la „sus” și pentru fiecare poziție legală a reginei actuale, vi
se oferă un tuplu de poziții de la „dedesubt”. Tot ce trebuie să faci pentru a menține lucrurile să curgă este
să obții următorul rezultat, cu propria ta poziție adăugată în față:

...
else:
pentru poziție în interval
(num): dacă nu conflict (stare,
pos): pentru rezultat în matci (num, stare + (poz,)):
randament (poz,) + rezultat

Părțile pentru poziție și, dacă nu, în conflict sunt identice cu ceea ce aveați înainte, așa că puteți rescrie puțin acest
lucru pentru a simplifica codul. Să adăugăm și câteva argumente implicite.

def queens(num=8, state=()):


pentru poziție în
interval(num): dacă nu
conflict(stare, pos): dacă
len(stare) == num-1:
randament (poz,) altfel:
pentru rezultat la matci(num, stare + (poz,)):
randament (poz,) + rezultat

Dacă vi se pare că codul este greu de înțeles, s-ar putea să vă fie util să formulați ceea ce face cu
propriile cuvinte. (Și vă amintiți că virgula în (pos,) este necesară pentru a face din el un tuplu, și nu doar
o valoare între paranteze, nu?)
Generatorul de matci vă oferă toate soluțiile (adică toate modalitățile legale de plasare a matcilor).

>>> lista(regine(3)) []
>>> lista(regine(4))
[(1, 3, 0, 2), (2, 0, 3, 1)]
>>> pentru solutie in matci
(8): ... soluție de imprimare

...
(0, 4, 7, 5, 2, 6, 1, 3) (0, 5, 7,
2, 6, 3, 1, 4)
...
(7, 2, 0, 5, 1, 4, 6, 3) (7, 3, 0,
2, 5, 1, 6, 4)
>>>

Dacă alergi matci cu opt matci, vezi o mulțime de soluții fulgerând. Să aflăm câți.

>>> len(lista(regine(8)))
92

190
Machine Translated by Google

Capitolul 9 Metode magice, proprietă i i iteratoare

Încheierea
Înainte de a părăsi reginele, să facem rezultatul puțin mai ușor de înțeles. Ieșirea clară este întotdeauna un lucru bun,
deoarece facilitează identificarea erorilor, printre altele.

def prettyprint(soluție):
def line(poz, lungime=len(soluție)):
return '. ' * (poz.) + 'X' + '. ' * (lungime-poz-1) pentru
poz în soluție: print(line(pos))

Rețineți că am creat o mică funcție de ajutor în interiorul prettyprint. L-am pus acolo pentru că am presupus că nu voi
avea nevoie de el nicăieri afară. În cele ce urmează, imprim o soluție aleatorie pentru a mă convinge că este corectă.

>>> import
aleator >>> prettyprint(random.choice(listă(regine(8))))
. . . . . X . .
. X . . . . . .
. . . . . . X .
X . . . . . . .
. . . X . . . .
. . . . . . . X
. . . . X . . .
. . X . . . . .

Acest „desen” corespunde diagramei din Figura 9-2.

191
Machine Translated by Google

Capitolul 9 Metode magice, proprietă i i iteratoare

Figura 9-2. Una dintre multele soluții posibile la problema celor opt regine

Un rezumat rapid
Ai văzut multă magie aici. Să facem un bilanț.

Clasele în stil nou versus stil vechi: modul în care funcționează clasele în Python se schimbă.
Versiunile recente de Python pre-3.0 au avut două feluri de clase, cele vechi ieșind rapid din
modă. Clasele de stil nou au fost introduse în versiunea 2.2 și oferă câteva caracteristici
suplimentare (de exemplu, funcționează cu super și proprietate, în timp ce clasele în stil
vechi nu). Pentru a crea o clasă cu stil nou, trebuie să subclasați obiectul, fie direct, fie
indirect, sau să setați proprietatea __metaclass__.

Metode magice: în Python există mai multe metode speciale (cu nume care încep și se
termină cu caractere de subliniere duble). Aceste metode diferă destul de mult în funcție
de funcție, dar cele mai multe dintre ele sunt apelate automat de Python în anumite
circumstanțe. (De exemplu, __init__ este apelat după crearea obiectului.)

Constructori: aceștia sunt comuni pentru multe limbaje orientate pe obiect și probabil
vei implementa unul pentru aproape fiecare clasă pe care o scrii. Constructorii sunt numiți
init și sunt apelați automat imediat după crearea unui obiect.

192
Machine Translated by Google

Capitolul 9 Metode magice, proprietă i i iteratoare

Suprascriere: O clasă poate suprascrie metode (sau orice alte atribute) definite
în superclasele sale pur și simplu prin implementarea metodelor. Dacă noua
metodă trebuie să apeleze versiunea suprascrisă, poate fie să apeleze versiunea
nelegată din superclasă direct (clase de stil vechi), fie să folosească funcția super (clase
de stil nou).

Secvențe și mapări: Crearea unei secvențe sau mapare proprie necesită


implementarea tuturor metodelor secvenței și a protocoalelor de mapare, inclusiv
metode magice precum getitem și __setitem__. Prin subclasarea listă (sau UserList) și
dict (sau UserDict), puteți economisi multă muncă.

Iteratoare: un iterator este pur și simplu un obiect care are o metodă __next__.
Iteratoarele pot fi utilizate pentru a itera peste un set de valori. Când nu mai există
valori, următoarea metodă ar trebui să ridice o excepție StopIteration. Obiectele
iterabile au o metodă __iter__, care returnează un iterator și pot fi folosite în bucle
for, la fel ca și secvențele. Adesea, un iterator este, de asemenea, iterabil; adică are o
metodă __iter__ care returnează iteratorul însuși.

Generatoare: o funcție-generator (sau metodă) este o funcție (sau metodă) care


conține cuvântul cheie randament. Când este apelată, funcția generator returnează
un generator, care este un tip special de iterator. Puteți interacționa cu un
generator activ din exterior utilizând metodele de trimitere, aruncare și închidere.

Eight Queens: Problema Eight Queens este bine cunoscută în informatică și se


pretează cu ușurință implementării cu generatoare. Scopul este de a poziționa opt
regine pe o tablă de șah, astfel încât niciuna dintre regine să nu fie într-o poziție din
care să poată ataca pe oricare dintre celelalte.

Funcții noi în acest capitol


Func ie Descriere
iter(obj) Extrage un iterator dintr-un obiect iterabil
next(it) Avansează un iterator și returnează următorul său element

property(fget, fset, fdel, doc) super(class, Returnează o proprietate; toate argumentele sunt optionale

obj) Returnează o instanță legată a superclasei clasei

Rețineți că iter și super pot fi apelați cu alți parametri decât cei descriși aici. Pentru mai multe informații,
consultați documentația standard Python.

Ce acum?
Acum cunoști majoritatea limbajului Python. Deci de ce au mai rămas atâtea capitole? Ei bine, mai sunt multe de
învățat, multe despre modul în care Python se poate conecta la lumea externă în diferite moduri. Și apoi avem
testarea, extinderea, ambalarea și proiectele, așa că nu am terminat încă, nu pe departe.

193
Machine Translated by Google

CAPITOLUL 10

Baterii incluse

Acum cunoașteți majoritatea limbajului de bază Python. În timp ce limbajul de bază este puternic în sine, Python vă oferă mai multe
instrumente cu care să vă jucați. O instalare standard include un set de module numit bibliotecă standard.
Pe unele le-ați văzut deja (matematică și cmath, de exemplu), dar sunt multe altele. Acest capitol vă arată câte ceva despre modul
în care funcționează modulele și despre cum să le explorați și să aflați ce au de oferit. Apoi, capitolul oferă o privire de ansamblu
asupra bibliotecii standard, concentrându-se pe câteva module utile selectate.

Module
Știți deja despre crearea propriilor programe (sau scripturi) și executarea lor. Ați văzut, de asemenea, cum puteți prelua funcții în
programele dvs. din module externe folosind import.

>>> import
matematică >>> math.sin(0)
0,0

Să aruncăm o privire la modul în care vă puteți scrie propriile module.

Modulele sunt programe


Orice program Python poate fi importat ca modul. Să presupunem că ați scris programul în Lista 10-1 și l-ați stocat într-un fișier
numit hello.py. Numele fișierului, cu excepția extensiei .py, devine numele modulului dvs.

Lista 10-1. Un modul simplu

# hello.py
print("Bună ziua, lume!")

Unde îl salvezi este, de asemenea, important; în secțiunea următoare veți afla mai multe despre asta, dar deocamdată să presupunem
că îl salvați în directorul C:\python (Windows) sau ~/python (UNIX/macOS).
Apoi, puteți spune interpretului unde să caute modulul executând următoarele (folosind directorul Windows):

>>> import sys >>>


sys.path.append('C:/python')

© Magnus Lie Hetland 2017 195


ML Hetland, Beginning Python, DOI 10.1007/978-1-4842-0028-5_10
Machine Translated by Google

Capitolul 10 Bateriile incluse

Sfat În UNIX, nu puteți adăuga pur și simplu șirul „~/python” la sys.path. Trebuie să utilizați calea completă (cum ar fi „/

home/yourusername/python”) sau, dacă doriți să o automatizați, utilizați sys.path.expanduser('~/python').

Acest lucru îi spune pur și simplu interpretului că ar trebui să caute module în directorul C:\python în plus față de locurile în care ar
arăta în mod normal. După ce ați făcut acest lucru, vă puteți importa modulul (care este stocat în fișierul C:\python\hello.py, vă
amintiți?).

>>> import Bună ziua,


lume!

Notă Când importați un modul, este posibil să observați apariția unui director nou, numit __pycache__, alături de fișierul sursă.

(În versiunile mai vechi, veți vedea în schimb fișiere cu sufixul .pyc .) Acest director conține fișiere cu fișiere procesate pe care

Python le poate gestiona mai eficient. Dacă importați același modul mai târziu, Python va importa aceste fișiere și apoi fișierul

dvs. .py , cu excepția cazului în care fișierul .py s-a modificat; în acest caz, este generat un nou fișier procesat. Ștergerea directorului

__pycache__ nu dăunează – se creează unul nou după cum este necesar.

După cum puteți vedea, codul din modul este executat atunci când îl importați. Cu toate acestea, dacă încercați să-l importați din nou,
nu se întâmplă nimic.

>>> import salut


>>>

De ce nu merge de data asta? Deoarece modulele nu sunt cu adevărat menite să facă lucruri (cum ar fi tipărirea textului) atunci când
sunt importate. Ele sunt menite în principal să definească lucruri, cum ar fi variabile, funcții, clase și așa mai departe.
Și pentru că trebuie să definiți lucrurile o singură dată, importarea unui modul de mai multe ori are același efect ca și importarea
lui o dată.

DE CE DOAR O dată?

Comportamentul de import-o singură dată este o optimizare substanțială în majoritatea cazurilor și poate fi foarte
important într-un caz special: dacă două module se importă reciproc.

În multe cazuri, puteți scrie două module care trebuie să acceseze funcții și clase unul de la celălalt pentru a funcționa
corect. De exemplu, este posibil să fi creat două module — clientdb și billing — care conțin cod pentru o bază de date
cu clienți și, respectiv, pentru un sistem de facturare. Baza dvs. de clienți poate conține apeluri către sistemul dvs. de
facturare (de exemplu, trimiterea automată a unei facturi către un client în fiecare lună), în timp ce sistemul de facturare
probabil trebuie să acceseze funcționalitățile din baza dvs. de clienți pentru a face facturarea corect.

Dacă fiecare modul ar putea fi importat de mai multe ori, veți avea o problemă aici. Modulul clientdb ar importa
facturarea, care din nou importă clientdb, care... obțineți imaginea. Sfârșiți cu o buclă nesfârșită de importuri (recursie
infinită, vă amintiți?). Cu toate acestea, deoarece nu se întâmplă nimic a doua oară când importați modulul, bucla este
întreruptă.

196
Machine Translated by Google

Capitolul 10 Bateriile incluse

Dacă insistați să vă reîncărcați modulul, puteți utiliza funcția de reîncărcare din modulul importlib .
Este nevoie de un singur argument (modulul pe care doriți să-l reîncărcați) și returnează modulul reîncărcat. Acest
lucru poate fi util dacă ați făcut modificări la modulul dvs. și doriți ca acele modificări să fie reflectate în programul
dvs. în timp ce acesta rulează. Pentru a reîncărca modulul simplu hello (conținând doar o declarație de tipărire ), aș
folosi următoarele:

>>> import importlib >>>


salut = importlib.reload(hello)
Salut Lume!

Aici, presupun că hello a fost deja importat (o dată). Atribuind rezultatul reîncărcării lui hello, am înlocuit
versiunea anterioară cu cea reîncărcată. După cum puteți vedea din salutul tipărit, chiar import modulul aici.

Dacă ați creat un obiect x prin instanțierea clasei Foo din bara de module și apoi reîncărcați bara, obiectul la care
se referă x nu va fi re-creat în niciun fel. x va fi în continuare o instanță a versiunii vechi de Foo (din versiunea veche
a barului). Dacă, în schimb, doriți ca x să se bazeze pe noul Foo din modulul reîncărcat, va trebui să îl creați din nou.

Modulele sunt folosite pentru a defini lucrurile


Deci modulele sunt executate prima dată când sunt importate în programul dumneavoastră. Pare oarecum util, dar
nu foarte. Ceea ce îi face să merite este că ei (la fel ca și cursurile) își păstrează domeniul de aplicare după aceea.
Aceasta înseamnă că toate clasele sau funcțiile pe care le definiți și orice variabile cărora le atribuiți o valoare
devin atribute ale modulului. Acest lucru poate părea complicat, dar în practică este foarte simplu.

Definirea unei funcții într-un modul


Să presupunem că ați scris un modul ca cel din Lista 10-2 și l-ați stocat într-un fișier numit hello2.py. De asemenea,
presupuneți că l-ați pus într-un loc în care interpretul Python îl poate găsi, fie utilizând trucul sys.path din secțiunea
anterioară, fie folosind metodele mai convenționale din secțiunea ulterioară „Facerea modulelor disponibile”.

Sfat Dacă faceți un program (care este menit să fie executat și nu chiar folosit ca modul) disponibil în același mod ca și

alte module, îl puteți executa efectiv folosind comutatorul -m la interpretul Python.

Rularea comenzii python -m progname args va rula programul progname cu argumentele liniei de comandă args, cu

condiția ca fișierul progname.py (rețineți sufixul) să fie instalat împreună cu celelalte module ale dvs. (adică, cu condiția să fi

importat programul) .

Lista 10-2. Un modul simplu care conține o funcție

# hello2.py
def hello():
print("Bună ziua, lume!")

197
Machine Translated by Google

Capitolul 10 Bateriile incluse

Apoi îl puteți importa astfel:

>>> import hello2

Modulul este apoi executat, ceea ce înseamnă că funcția hello este definită în domeniul de aplicare al modulului, astfel
încât să puteți accesa funcția astfel:

>>> hello2.hello()
Salut Lume!

Orice nume definit în domeniul global al modulului va fi disponibil în același mod. De ce ai vrea să faci asta? De ce
nu definiți totul în programul dvs. principal?
Motivul principal este reutilizarea codului. Dacă vă puneți codul într-un modul, îl puteți utiliza în mai multe
dintre programele dvs., ceea ce înseamnă că dacă scrieți o bază de date de clienți bună și o puneți într-un modul
numit clientdb, îl puteți folosi la facturare, la expediere. spam (deși sper că nu o veți face) și în orice program care are
nevoie de acces la datele clienților dvs. Dacă nu ați fi pus acest lucru într-un modul separat, ar trebui să rescrieți codul
în fiecare dintre aceste programe. Deci, amintiți-vă, pentru a vă face codul reutilizabil, faceți-l modular! (Și, da, acest
lucru este cu siguranță legat de abstractizare.)

Adăugarea codului de test într-un modul

Modulele sunt folosite pentru a defini lucruri precum funcții și clase, dar din când în când (destul de des, de
fapt), este util să adăugați un cod de testare care verifică dacă lucrurile funcționează așa cum ar trebui. De exemplu,
dacă doriți să vă asigurați că funcția hello a funcționat, puteți rescrie modulul hello2 într-unul nou, hello3, definit în
Lista 10-3.

Lista 10-3. Un modul simplu cu un cod de testare problematic

# hello3.py
def hello():
print("Bună ziua, lume!")

# Un test:
Buna ziua()

Acest lucru pare rezonabil - dacă rulați acest lucru ca un program normal, veți vedea că funcționează. Totuși,
dacă îl importați ca modul, pentru a utiliza funcția hello într-un alt program, se execută codul de test, ca în primul
modul hello din acest capitol.

>>> import hello3


Salut, lume! >>>
hello3.hello()
Salut Lume!

Nu asta vrei tu. Cheia pentru a evita acest comportament este verificarea dacă modulul este rulat ca program
pe cont propriu sau importat într-un alt program. Pentru a face asta, aveți nevoie de variabila __name__.

>>> __name__
'__main__' >>>
hello3.__name__ 'hello3'

198
Machine Translated by Google

Capitolul 10 Bateriile incluse

După cum puteți vedea, în „programul principal” (inclusiv promptul interactiv al interpretului), variabila __name__
are valoarea „__main__”. Într-un modul importat, acesta este setat la numele acelui modul. Prin urmare, puteți face
ca codul de testare al modulului să se comporte mai bine introducând o instrucțiune if, așa cum se arată în Lista 10-4.

Lista 10-4. Un modul cu cod de testare condiționată

# hello4.py

def hello():
print("Bună ziua, lume!")

def test():
salut()

if __name__ == '__main__': test()

Dacă rulați asta ca program, funcția hello este executată; dacă îl importați, se comportă ca un modul normal.

>>> import hello4 >>>


hello4.hello()
Salut Lume!

După cum puteți vedea, am împachetat codul de test într-o funcție numită test. Aș fi putut pune codul direct în
instrucțiunea if; cu toate acestea, punându-l într-o funcție de testare separată, puteți testa modulul chiar dacă l-ați
importat într-un alt program.

>>> hello4.test()
Salut Lume!

Notă Dacă scrieți un cod de testare mai amănunțit, probabil că este o idee bună să îl puneți într-un program separat. Vezi capitolul

16 pentru mai multe despre testele de scriere.

Disponibilitatea modulelor dvs. În exemplele

anterioare, am modificat sys.path, care conține o listă de directoare (sub formă de șiruri) în care interpretul ar trebui
să caute module. Cu toate acestea, nu doriți să faceți acest lucru în general. Cazul ideal ar fi ca sys.path să conțină
directorul corect (cel care conține modulul dvs.) pentru început. Există două moduri de a face acest lucru: puneți
modulul în locul potrivit sau spuneți interpretului unde să caute. Următoarele secțiuni discută aceste două soluții. Dacă
doriți să vă faceți modulul ușor disponibil pentru alții, aceasta este o altă chestiune. Ambalajul Python a trecut printr-o
fază de complexitate și diversitate crescândă; este acum reținut și simplificat de către Python Packaging Authority, dar
mai sunt multe de digerat.
În loc să mă scufund în acest subiect provocator, vă trimit la Ghidul utilizatorului Python Packaging, disponibil la
packaging.python.org.

199
Machine Translated by Google

Capitolul 10 Bateriile incluse

Așezarea modulului în locul potrivit Așezarea modulului în

locul potrivit – sau, mai degrabă, locul potrivit – este destul de ușoară. Este doar o chestiune de a afla unde caută
interpretul Python pentru module și apoi de a pune fișierul acolo. Dacă interpretul Python de pe mașina la care lucrați a
fost instalat de un administrator și nu aveți permisiuni de administrator, este posibil să nu vă puteți salva modulul în
niciunul dintre directoarele utilizate de Python. Va trebui apoi să utilizați soluția alternativă, descrisă în secțiunea
următoare: spuneți interpretului unde să caute.
După cum vă amintiți, lista de directoare (așa-numita cale de căutare) poate fi găsită în variabila cale din
modulul sys.

>>> import sys, pprint >>>


pprint.pprint(sys.path)
[„C:\\Python35\\Lib\\idlelib”, „C:\
\Python35”, „C:\\Python35\\DLLs”, „C:
\\Python35\\lib”, „C:\\ Python35\\lib\
\plat-win', 'C:\\Python35\\lib\\lib-tk',
'C:\\Python35\\lib\\site-packages']

Sfat Dacă aveți o structură de date prea mare pentru a se potrivi pe o singură linie, puteți utiliza funcția pprint din
modulul pprint în loc de instrucțiunea normală de imprimare . pprint este o funcție de imprimare destul de, care face o
imprimare mai inteligentă.

Este posibil să nu obțineți exact același rezultat, desigur. Ideea este că fiecare dintre aceste șiruri oferă un loc pentru
a pune module dacă doriți ca interpretul să le găsească. Chiar dacă toate acestea vor funcționa, directorul site-packages
este cea mai bună alegere, deoarece este conceput pentru acest gen de lucruri. Căutați prin sys.path și găsiți directorul
site-packages și salvați modulul din Lista 10-4 în el, dar dați-i un alt nume, cum ar fi another_hello.py. Apoi încercați
următoarele:

>>> import another_hello >>>


another_hello.hello()
Salut Lume!

Atâta timp cât modulul dvs. este localizat într-un loc precum pachetele de site, toate programele dvs. vor putea să-l importe.

Spune-i interpretului unde să caute


Punerea modulului în locul corect ar putea să nu fie soluția potrivită pentru dvs. din mai multe motive.

•Nu doriți să aglomerați directoarele interpretului Python cu propriile module.

•Nu aveți permisiunea de a salva fișiere în directoarele interpretului Python.

•Ați dori să vă păstrați modulele în altă parte.

Concluzia este că, dacă plasați modulele în altă parte, trebuie să spuneți interpretului unde să caute. După cum ați
văzut mai devreme, o modalitate de a face acest lucru este să modificați direct sys.path, dar acesta nu este o
modalitate obișnuită de a face acest lucru. Metoda standard este să includeți directorul (sau directoarele) modul în
variabila de mediu PYTHONPATH.
În funcție de sistemul de operare pe care îl utilizați, conținutul lui PYTHONPATH variază (vezi bara laterală „Variabile
de mediu”), dar practic este la fel ca sys.path—o listă de directoare.

200
Machine Translated by Google

Capitolul 10 Bateriile incluse

VARIABILE DE MEDIU

Variabilele de mediu nu fac parte din interpretul Python - fac parte din sistemul dvs. de operare.
Practic, sunt ca variabilele Python, dar sunt setate în afara interpretului Python. Să presupunem că utilizați shell-ul
bash , care este disponibil pe majoritatea sistemelor asemănătoare UNIX, macOS și versiunile recente de Windows.
Apoi puteți executa următoarea instrucțiune pentru a adăuga ~/python la variabila de mediu PYTHONPATH :

export PYTHONPATH=$PYTHONPATH:~/python

Dacă doriți ca instrucțiunea să fie executată pentru toate shell-urile pe care le începeți, o puteți adăuga la
fișierul .bashrc din directorul dvs. de acasă. Pentru instrucțiuni despre editarea variabilelor de mediu în alte moduri, ar
trebui să consultați documentația sistemului.

Pentru o alternativă la utilizarea variabilei de mediu PYTHONPATH, este posibil să doriți să luați în considerare așa-numitele
fișiere de configurare a căii. Acestea sunt fișiere cu extensia .pth, situate în anumite directoare și care conțin nume de
directoare care ar trebui adăugate la sys.path. Pentru detalii, vă rugăm să consultați documentația standard a bibliotecii
pentru modulul site.

Pachete Pentru

a vă structura modulele, le puteți grupa în pachete. Un pachet este practic doar un alt tip de modul. Lucrul interesant la
ele este că pot conține și alte module. În timp ce un modul este stocat într-un fișier (cu extensia de nume de fișier .py), un
pachet este un director. Pentru ca Python să-l trateze ca pe un pachet, trebuie să conțină un fișier numit __init__.py. Conținutul
acestui fișier va fi conținutul pachetului, dacă îl importați ca și cum ar fi un modul simplu. De exemplu, dacă ați avea un
pachet numit constante și fișierul constants/__init__.py conținea declarația PI = 3.14, ați putea face următoarele:

import constante
print(constants.PI)

Pentru a pune module într-un pachet, pur și simplu puneți fișierele module în directorul pachetului. De asemenea, puteți
imbrica pachete în alte pachete. De exemplu, dacă doriți un pachet numit desen, care conține un modul numit forme și unul
numit culori, veți avea nevoie de fișierele și directoarele (nume de căi UNIX) prezentate în Tabelul 10-1.

Tabelul 10-1. Un aspect simplu al pachetului

Fișier/Director Descriere

~/python/ ~/ Director în PYTHONPATH

python/drawing/ ~/ Directorul pachetelor (pachet de desene)

python/drawing/__init__.py ~/python/ Cod pachet (modul de desen)

drawing/colors.py ~/python/drawing/ modul de culori

shapes.py modul de forme

201
Machine Translated by Google

Capitolul 10 Bateriile incluse

Cu această configurație, următoarele declarații sunt toate legale:

desen # (2) Importă modulul culori import


# (1) Importă
desen.culori
pachetul
din de
desene
desene
import
import
forme
# (3) Importă modulul forme

După prima declarație, conținutul fișierului __init__.py din desen ar fi disponibil; modulele de forme și culori, însă, nu ar
fi. După a doua declarație, modulul de culori ar fi disponibil, dar numai sub numele complet, desen.culori. După a treia
declarație, modulul de forme ar fi disponibil, sub numele său scurt (adică, pur și simplu forme). Rețineți că aceste afirmații
sunt doar exemple. Nu este nevoie, de exemplu, să importați pachetul în sine înainte de a importa unul dintre modulele
sale, așa cum am făcut aici.
A doua declarație ar putea fi foarte bine executată de la sine, la fel ca și a treia.

Explorarea modulelor
Înainte de a descrie unele dintre modulele standard ale bibliotecii, vă voi arăta cum să explorați modulele pe cont
propriu. Aceasta este o abilitate valoroasă, deoarece veți întâlni multe module utile în cariera dvs. de programator
Python și nu le-aș putea acoperi pe toate aici. Biblioteca standard actuală este suficient de mare pentru a garanta cărți
de la sine (și astfel de cărți au fost scrise) - și este în creștere. Noi module sunt adăugate cu fiecare lansare și, adesea,
unele dintre module suferă mici modificări și îmbunătățiri. De asemenea, cu siguranță veți găsi mai multe module utile
pe Web, iar posibilitatea de a le înțelege rapid și ușor vă va face programarea mult mai plăcută.

Ce este într-un modul?

Cea mai directă modalitate de a sonda un modul este investigarea lui în interpretul Python. Primul lucru pe care
trebuie să-l faceți este să îl importați, desigur. Să presupunem că ați auzit zvonuri despre un modul standard numit copie.

>>> import copie

Nu se ridică excepții – așa că există. Dar ce face? Și ce conține?

Folosind dir
Pentru a afla ce conține un modul, puteți folosi funcția dir, care listează toate atributele unui obiect (și, prin
urmare, toate funcțiile, clasele, variabilele și așa mai departe, ale unui modul). Dacă imprimați dir(copy), veți obține
o listă lungă de nume. (Continuați, încercați.) Câteva dintre aceste nume încep cu un caracter de subliniere — un
indiciu (prin convenție) că nu sunt menite să fie folosite în afara modulului. Deci, haideți să le filtrăm cu puțină înțelegere
a listei (verificați secțiunea despre înțelegerea listei din capitolul 5 dacă nu vă amintiți cum funcționează asta).

>>> [n pentru n în dir(copiere) dacă nu n.startswith('_')]


[„Eroare”, „PyStringMap”, „copy”, „deepcopy”, „dispatch_table”, „eroare”, „name”, „t”, „weakref”]

Rezultatul constă din toate numele din dir(copy) care nu au un caracter de subliniere ca primă literă și ar trebui să fie
mai puțin confuze decât o listă completă.

202
Machine Translated by Google

Capitolul 10 Bateriile incluse

Variabila __toate__
Ceea ce am făcut cu mica înțelegere a listei din secțiunea anterioară a fost să fac o ghicire despre ceea ce trebuia să văd în modulul
de copiere. Cu toate acestea, puteți obține răspunsul corect direct din modul însuși.
În lista completă de dir(copie), este posibil să fi observat numele __all__. Aceasta este o variabilă care conține o listă similară cu cea pe
care am creat-o cu înțelegerea listei, cu excepția faptului că această listă a fost setată în modulul însuși. Sa vedem ce contine:

>>> copy.__all__
[„Eroare”, „copiere”, „copiere adâncă”]

Bănuiala mea nu a fost chiar atât de rea. Am primit doar câteva nume suplimentare care nu erau destinate utilizării mele. Dar de
unde a venit această listă __toate__ și de ce este cu adevărat acolo? La prima întrebare este ușor de răspuns. A fost setat în modulul
de copiere, astfel (copiat direct din copy.py):

__all__ = [„Eroare”, „copiere”, „copiere adâncă”]

Deci de ce este acolo? Acesta definește interfața publică a modulului. Mai precis, îi spune interpretului ce înseamnă importarea tuturor
numelor din acest modul. Deci, dacă folosești asta:

*
de la importul copiei

veți obține doar cele patru funcții listate în variabila __all__. Pentru a importa PyStringMap, de exemplu, ar trebui să fii explicit,
fie importând copy și utilizând copy.PyStringMap, fie utilizând din copie import PyStringMap.

Setarea __all__ astfel este o tehnică utilă și atunci când scrieți module. Deoarece este posibil să aveți o mulțime de variabile,
funcții și clase în modulul dvs. de care alte programe ar putea să nu le aibă nevoie sau să nu le dorească, este politicos să le filtrați.
Dacă nu setați __all__, numele exportate într-un import marcat cu stea sunt implicite pentru toate numele globale din modul care nu
încep cu un caracter de subliniere.

Obține ajutor cu ajutor Până acum,


v-ați folosit ingeniozitatea și cunoștințele despre diferite funcții Python și atribute speciale pentru
a explora modulul de copiere. Interpretul interactiv este un instrument puternic pentru acest tip
de explorare, deoarece stăpânirea limbii este singura limită a cât de profund puteți sonda un modul.
Cu toate acestea, există o funcție standard care vă oferă toate informațiile de care aveți nevoie în mod normal. Această funcție se
numește ajutor. Să încercăm cu funcția de copiere:

>>> ajutor(copy.copy)
Ajutor pentru copierea funcției în copierea modulului:

copie (x)
Operație de copiere superficială pe obiecte Python arbitrare.

Consultați șirul __doc__ al modulului pentru mai multe informații.

Aceasta vă spune că __copy__ primește un singur argument x și că este o „operație de copiere superficială”. Dar menționează și
șirul __doc__ al modulului. Ce e aia? Poate vă amintiți că am menționat docstrings în Capitolul 6. Un docstring este pur și simplu
un șir pe care îl scrieți la începutul unei funcții pentru a o documenta. Acest șir poate fi apoi menționat prin atributul funcției __doc__.
După cum puteți înțelege din textul de ajutor anterior, modulele pot avea și docstrings (sunt scrise la începutul modulului), la fel ca
și clasele (sunt scrise la începutul clasei).

203
Machine Translated by Google

Capitolul 10 Bateriile incluse

De fapt, textul de ajutor anterior a fost extras din șirul document al funcției de copiere:

>>> print(copy.copy.__doc__)
Operație de copiere superficială pe obiecte Python arbitrare.

Consultați șirul __doc__ al modulului pentru mai multe informații.

Avantajul utilizării ajutorului în comparație cu doar examinarea directă a șirului documentar astfel este că obțineți mai
multe informații, cum ar fi semnătura funcției (adică argumentele pe care le ia). Încercați să apelați ajutor pentru modulul în
sine și vedeți ce obțineți. Tipărește o mulțime de informații, inclusiv o discuție amănunțită despre diferența dintre copie și
deepcopy (în esență că deepcopy(x) face copii ale valorilor găsite în x ca atribute și așa mai departe, în timp ce copy(x) doar
copiază x, obligatoriu atributele copiei la aceleași valori ca cele ale lui x).

Documentație
O sursă naturală de informații despre un modul este, desigur, documentarea acestuia. Am amânat discuția despre
documentație, deoarece de multe ori este mult mai rapid să examinezi puțin modulul mai întâi.
De exemplu, vă puteți întreba: „Care au fost argumentele pentru a varia din nou?” În loc să căutați într-o carte Python sau în
documentația standard Python pentru o descriere a intervalului, puteți doar să o verificați direct.

>>> print(range.__doc__)
range(stop) -> range obiect range(start,
stop[, step]) -> range obiect

Returnează un obiect care produce o secvență de numere întregi de la început (inclusiv) până la oprire
(exclusiv) cu pas. range(i, j) produce i, i+1, i+2, ..., j-1. start este implicit 0 și stop este omis! intervalul (4)
produce 0, 1, 2, 3.
Aceștia sunt exact indicii validi pentru o listă de 4 elemente.
Când este dat pasul, acesta specifică creșterea (sau decrementarea).

Aveți acum o descriere precisă a funcției de gamă și, pentru că probabil că ați rulat interpretul Python deja (întrebarea
de funcții ca aceasta se întâmplă de obicei în timp ce programați), accesarea acestor informații a durat doar câteva
secunde.
Cu toate acestea, nu fiecare modul și fiecare funcție are un docstring bun (deși ar trebui) și uneori este
posibil să aveți nevoie de o descriere mai detaliată a modului în care funcționează lucrurile. Majoritatea modulelor
pe care le descărcați de pe Web au o documentație asociată. Unele dintre cele mai utile documentații pentru a
învăța să programați în Python este Python Library Reference, care descrie toate modulele din biblioteca standard.
Dacă vreau să caut ceva despre Python, de nouă ori din zece, îl găsesc acolo. Referința bibliotecii este disponibilă pentru
navigare online (la https://docs.python.org/library) sau pentru descărcare, la fel ca și alte documente standard (cum ar
fi Tutorialul Python și Referința limbajului Python). Toată documentația este disponibilă pe site-ul web Python la https://
docs.python.org.

Utilizați Sursa
Tehnicile de explorare pe care le-am discutat până acum vor fi probabil suficiente pentru majoritatea cazurilor. Dar aceia
dintre voi care doresc să înțeleagă cu adevărat limbajul Python ar putea dori să știe lucruri despre un modul la care nu se poate
răspunde fără a citi codul sursă. Citirea codului sursă este, de fapt, una dintre cele mai bune modalități de a învăța Python - pe
lângă codificarea dvs.

204
Machine Translated by Google

Capitolul 10 Bateriile incluse

Citirea efectivă nu ar trebui să fie o problemă, dar unde este sursa? Să zicem că ai vrut
pentru a citi codul sursă pentru copia modulului standard. Unde l-ai găsi? O soluție ar fi să examinați din nou sys.path și să îl căutați
singur, la fel cum o face interpretul. O modalitate mai rapidă este de a examina proprietatea __file__ a modulului.

>>> print(copy.__file__)
C:\Python35\lib\copy.py

Iata! Puteți deschide fișierul copy.py în editorul de cod (de exemplu, IDLE) și puteți începe să examinați cum funcționează. Dacă numele
fișierului se termină cu .pyc, trebuie doar să utilizați fișierul corespunzător al cărui nume se termină cu .py.

Aten ie Când deschide i un fi ier bibliotecă standard într-un editor de text, risca i să îl modifica i accidental.
Dacă faceți acest lucru, s-ar putea rupe, așa că atunci când închideți fișierul, asigurați-vă că nu salvați nicio modificare pe care ați putea-o fi făcut.

Rețineți că unele module nu au nicio sursă Python pe care o puteți citi. Acestea pot fi încorporate în interpret (cum ar fi modulul sys) sau
pot fi scrise în limbajul de programare C.1 (vezi capitolul 17 ). pentru mai multe informații despre extinderea Python folosind C.)

Biblioteca standard: Câteva favorite


Expresia „baterii incluse” cu referire la Python a fost inventată inițial de Frank Stajano și se referă la biblioteca standard copioasă
a lui Python. Când instalați Python, obțineți o mulțime de module utile („baterii”) gratuit. Deoarece există atât de multe modalități de
a obține mai multe informații despre aceste module (așa cum este explicat în prima parte a acestui capitol), nu voi include aici o referință
completă (care ar ocupa oricum mult prea mult spațiu); în schimb, vă voi descrie câteva dintre modulele mele standard preferate pentru
a vă stârni apetitul pentru explorare. Veți întâlni mai multe module standard în capitolele de proiect (Capitolele 20 prin 29). Descrierile
modulelor nu sunt complete, dar evidențiază unele dintre caracteristicile interesante ale fiecărui modul.

sys

Modulul sys vă oferă acces la variabile și funcții care sunt strâns legate de interpretul Python.
Unele dintre acestea sunt prezentate în Tabelul 10-2.

Tabelul 10-2. Câteva funcții și variabile importante din modulul sys

Funcție/Variabilă Descriere

argv Argumentele liniei de comandă, inclusiv numele scriptului

exit([arg]) Iese din programul curent, opțional cu o valoare de returnare dată sau un mesaj de eroare

module Un dicționar de mapare a numelor modulelor la modulele încărcate

O listă de nume de directoare în care pot fi găsite module

platforma de cale Un identificator de platformă, cum ar fi sunos5 sau win32

stdin Flux de intrare standard — un obiect asemănător fișierului

stdout Flux de ieșire standard — un obiect asemănător fișierului

stderr Flux de erori standard — un obiect asemănător fișierului

1 Dacă modulul a fost scris în C, codul sursă C ar trebui să fie disponibil.

205
Machine Translated by Google

Capitolul 10 Bateriile incluse

Variabila sys.argv conține argumentele transmise interpretului Python, inclusiv numele scriptului.
Funcția sys.exit iese din programul curent. (Dacă este apelat într-un bloc try/finally, discutat în Capitolul 8, clauza finally
este încă executată.) Puteți furniza un număr întreg pentru a indica dacă programul a reușit - o convenție UNIX. Probabil că veți
fi bine în majoritatea cazurilor dacă vă bazați pe implicit (care este zero, indicând succesul). Alternativ, puteți furniza un șir, care
este folosit ca mesaj de eroare și poate fi foarte util pentru un utilizator care încearcă să-și dea seama de ce sa oprit programul;
apoi, programul se închide cu acel mesaj de eroare și un cod care indică eșecul.

Maparea sys.modules mapează numele modulelor cu modulele reale. Se aplică numai modulelor importate în
prezent.
Variabila modulului sys.path a fost discutată mai devreme în acest capitol. Este o listă de șiruri, în care fiecare șir este
numele unui director în care interpretul va căuta module atunci când este executată o instrucțiune de import.

Variabila modulului sys.platform (un șir) este pur și simplu numele „platformei” pe care rulează interpretul. Acesta
poate fi un nume care indică un sistem de operare (cum ar fi sunos5 sau win32) sau poate indica un alt tip de platformă, cum
ar fi o mașină virtuală Java (de exemplu, java1.4.0) dacă rulați Jython.

Variabilele de modul sys.stdin, sys.stdout și sys.stderr sunt obiecte flux asemănătoare fișierelor. Ele
reprezintă conceptele standard UNIX de intrare standard, ieșire standard și eroare standard. Pentru a spune
simplu, sys.stdin este locul în care Python își primește intrarea (folosit în input, de exemplu), iar sys.stdout este locul
unde se imprimă. Aflați mai multe despre fișiere (și despre aceste trei fluxuri) în Capitolul 11.
Ca exemplu, luați în considerare problema utilizării argumentelor de tipărire în ordine inversă. Când apelați un script
Python din linia de comandă, puteți adăuga câteva argumente după el - așa-numitele argumente din linia de comandă.
Acestea vor fi apoi plasate în lista sys.argv, cu numele scriptului Python ca sys.argv[0].
Imprimarea acestora în ordine inversă este destul de simplă, așa cum puteți vedea în Lista 10-5.

Lista 10-5. Inversarea și imprimarea argumentelor din linia de comandă

# reverseargs.py
import sys args =
sys.argv[1:] args.reverse()
print(' '.join(args))

După cum puteți vedea, fac o copie a sys.argv. Puteți modifica originalul, dar, în general, este mai sigur să nu faceți,
deoarece alte părți ale programului se pot baza și pe sys.argv care conține argumentele originale. Observați, de asemenea,
că omit primul element al sys.argv — numele scriptului. Inversez lista cu args.reverse(), dar nu pot imprima rezultatul acelei
operațiuni. Este o modificare in loc care returnează Niciunul. O abordare alternativă ar fi următoarea:

print(' '.join(reversed(sys.argv[1:])))

În cele din urmă, pentru a face rezultatul mai frumos, folosesc metoda join string. Să încercăm rezultatul (presupunând că un alt
shell ar fi lovit).

$ python reverseargs.py acesta este un test de testare


a este acesta

206
Machine Translated by Google

Capitolul 10 Bateriile incluse

os
Modulul os vă oferă acces la mai multe servicii ale sistemului de operare. Modulul os este extins; doar câteva dintre
cele mai utile funcții și variabile sunt descrise în Tabelul 10-3. În plus față de acestea, os și submodulul său os.path conțin
mai multe funcții pentru a examina, construi și elimina directoare și fișiere, precum și funcții pentru manipularea căilor
(de exemplu, os.path.split și os.path.join vă permit ignora os.pathsep de cele mai multe ori). Pentru mai multe informații
despre această funcționalitate, consultați documentația standard a bibliotecii. Acolo puteți găsi, de asemenea, o
descriere a modulului pathlib, care oferă o interfață orientată pe obiect pentru manipularea căii.

Tabelul 10-3. Câteva funcții și variabile importante din modulul os

Funcție/Variabilă Descriere

mediu Cartografiere cu variabile de mediu

sistem (comandă) Execută o comandă a sistemului de operare într-un subshell

sep Separator folosit în căi

pathsep Separator pentru a separa căi

linesep Separator de linii („\n”, „\r” sau „\r\n”)

urandom(n) Returnează n octeți de date aleatoare criptografic puternice

Maparea os.environ conține variabilele de mediu descrise mai devreme în acest capitol. De exemplu, pentru a accesa
variabila de mediu PYTHONPATH, veți folosi expresia os.environ['PYTHONPATH'].
Această mapare poate fi folosită și pentru a modifica variabilele de mediu, deși nu toate platformele acceptă acest lucru.
Funcția os.system este folosită pentru a rula programe externe. Există și alte funcții pentru executarea
programelor externe, inclusiv execv, care iese din interpretul Python, dând control programului executat și popen,
care creează o conexiune asemănătoare unui fișier cu programul.
Pentru mai multe informații despre aceste funcții, consultați documentația standard a bibliotecii.

Sfat Verifica i modulul de subproces . Acesta colectează funcționalitatea funcțiilor os.system, execv
și popen .

Variabila de modul os.sep este un separator folosit în numele căilor. Separatorul standard în UNIX (și versiunea de linie
de comandă macOS a Python) este /. Standardul în Windows este \\ (sintaxa Python pentru o singură bară oblică
inversă), iar în vechiul macOS, era :. (Pe unele platforme, os.altsep conține un separator de cale alternativă, cum ar fi / în
Windows.)
Utilizați os.pathsep atunci când grupați mai multe căi, ca în PYTHONPATH. Pathsep-ul este folosit pentru a separa
căi: : în UNIX/macOS și ; în Windows.
Variabila de modul os.linesep este șirul separator de linii utilizat în fișierele text. În UNIX/OS X, acesta este un singur
caracterul de linie nouă (\n), iar în Windows, este o combinație de întoarcere la transport și o linie nouă (\r\n).
Funcția urandom folosește o sursă dependentă de sistem de „real” (sau, cel puțin, puternic criptografic)
aleatorietatea. Dacă platforma dvs. nu o acceptă, veți primi un NotImplementedError.

207
Machine Translated by Google

Capitolul 10 Bateriile incluse

Ca exemplu, luați în considerare problema pornirii unui browser web. Comanda de sistem poate fi folosită pentru
a executa orice program extern, ceea ce este foarte util în medii precum UNIX unde puteți executa programe (sau
comenzi) din linia de comandă pentru a lista conținutul unui director, a trimite e-mail și așa mai departe.
Dar poate fi util și pentru pornirea programelor cu interfețe grafice de utilizator, cum ar fi un browser web. În UNIX,
puteți face următoarele (cu condiția să aveți un browser la /usr/bin/firefox):

os.system('/usr/bin/firefox')

Iată o versiune de Windows (din nou, utilizați calea unui browser pe care l-ați instalat):

os.system(r'C:\"Fișiere de program (x86)"\"Mozilla Firefox"\firefox.exe')

Rețineți că am fost atent la includerea fișierelor de program și a Mozilla Firefox între ghilimele; în caz contrar,
învelișul subiacent se opune spațiului alb. (Acest lucru poate fi important și pentru directoarele din PYTHONPATH.) De
asemenea, rețineți că aici trebuie să utilizați barele oblice inverse deoarece shell-ul este confundat de barele oblice
înainte. Dacă rulați acest lucru, veți observa că browserul încearcă să deschidă un site web numit Files"\Mozilla...—
partea comenzii după spațiul alb. De asemenea, dacă încercați să rulați acest lucru din IDLE, apare o fereastră DOS, dar
browserul nu pornește până când nu închideți acea fereastră DOS.Per total, nu este un comportament tocmai ideal.
O altă funcție care se potrivește mai bine jobului este funcția specifică Windows os.startfile.

os.startfile(r'C:\Program Files (x86)\Mozilla Firefox\firefox.exe')

După cum puteți vedea, os.startfile acceptă o cale simplă, chiar dacă conține spații albe (adică nu includeți fișierele
de program între ghilimele, ca în exemplul os.system).
Rețineți că în Windows, programul dvs. Python continuă să ruleze după ce programul extern a fost pornit de
os.system (sau os.startfile); în UNIX, programul dumneavoastră Python așteaptă să se termine comanda os.system.

O SOLUȚIE MAI BUNĂ: WEBBROWSER

Funcția os.system este utilă pentru o mulțime de lucruri, dar pentru sarcina specifică de lansare a unui browser
web, există o soluție și mai bună: modulul browser web . Conține o funcție numită open, care vă permite să lansați
automat un browser web pentru a deschide adresa URL dată. De exemplu, dacă doriți ca programul dvs. să
deschidă site-ul web Python într-un browser web (fie pornind un browser nou, fie utilizând unul care rulează deja),
pur și simplu utilizați acest lucru:

import webbrowser
webbrowser.open('http://www.python.org')

Pagina ar trebui să apară.

fisier de intrare

Înveți multe despre citirea și scrierea în fișiere în Capitolul 11, dar aici este o previzualizare. Modulul de
introducere a fișierelor vă permite să repetați cu ușurință toate liniile dintr-o serie de fișiere text. Dacă vă apelați scriptul
astfel (presupunând o linie de comandă UNIX):

$ python some_script.py fișier1.txt fișier2.txt fișier3.txt

208
Machine Translated by Google

Capitolul 10 Bateriile incluse

veți putea să iterați pe rândul rândurilor fișierului1.txt până la fișierul3.txt. De asemenea, puteți itera peste liniile furnizate
intrării standard (sys.stdin, vă amintiți?), de exemplu, într-o conductă UNIX, folosind comanda standard UNIX cat.

$ cat fisier.txt | python some_script.py

Dacă utilizați intrarea fișierului, apelarea scriptului dvs. cu cat într-un canal UNIX funcționează la fel de bine ca și furnizarea
numelor de fișiere ca argumente de linie de comandă pentru scriptul dvs. Cele mai importante funcții ale modulului de introducere a
fișierelor sunt descrise în Tabelul 10-4.

Tabelul 10-4. Câteva funcții importante în modulul de intrare fișier

Func ie Descriere
input([fișiere[, inplace[, backup]]) nume fișier() Facilitează iterația peste linii în fluxuri de intrare multiple

lineno() filelineno() isfirstline() isstdin() nextfile() Returnează numele fișierului curent

close() Returnează numărul de linie curent (cumulativ).


Returnează numărul liniei din fișierul curent

Verifică dacă linia curentă este prima în fișier

Verifică dacă ultima linie a fost de la sys.stdin


Închide fișierul curent și trece la următorul

Închide secvența

fileinput.input este cea mai importantă dintre funcții. Returnează un obiect pe care îl puteți itera într-o buclă for. Dacă nu
doriți comportamentul implicit (în care intrarea fișierului află pe ce fișiere să iterați), puteți furniza unul sau mai multe nume
de fișiere acestei funcție (în secvență). De asemenea, puteți seta parametrul inplace la o valoare adevărată (inplace=True)
pentru a activa procesarea in-place. Pentru fiecare linie pe care o accesați, va trebui să imprimați un înlocuitor, care va fi
repus în fișierul de intrare curent. Argumentul opțional de rezervă oferă o extensie a numelui fișierului unui fișier de rezervă
creat din fișierul original atunci când efectuați procesarea la loc.

Funcția fileinput.filename returnează numele fișierului în care vă aflați în prezent (adică fișierul
care conține linia pe care o procesați în prezent).
Funcția fileinput.lineno returnează numărul liniei curente. Acest număr este cumulativ astfel încât
când ați terminat cu un fișier și începeți să procesați următorul, numărul de linie nu este resetat, ci începe cu unul mai mult
decât ultimul număr de linie din fișierul anterior.
Funcția fileinput.filelineno returnează numărul liniei curente din fișierul curent. De fiecare dată când ați terminat cu un
fișier și începeți să procesați următorul, numărul liniei fișierului este resetat și repornește la 1.
Funcția fileinput.isfirstline returnează o valoare adevărată dacă linia curentă este prima linie a
fila curenta; în caz contrar, returnează o valoare falsă.
Funcția fileinput.isstdin returnează o valoare adevărată dacă fișierul curent este sys.stdin; altfel, ea
returnează false.

Funcția fileinput.nextfile închide fișierul curent și trece la următorul. Rândurile pe care le săriți
nu contează împotriva numărului de linii. Acest lucru poate fi util dacă știți că ați terminat cu fișierul curent - de exemplu,
dacă fiecare fișier conține cuvinte în ordine sortată și căutați un anumit cuvânt. Dacă ați trecut de poziția cuvântului în
ordinea sortării, puteți sări în siguranță la următorul fișier.
Funcția fileinput.close închide întregul lanț de fișiere și termină iterația.

209
Machine Translated by Google

Capitolul 10 Bateriile incluse

Ca exemplu de utilizare a intrării fișierelor, să presupunem că ați scris un script Python și doriți să numerotați
liniile. Deoarece doriți ca programul să continue să funcționeze după ce ați făcut acest lucru, trebuie să adăugați numerele
rândului în comentarii în partea dreaptă a fiecărei rânduri. Pentru a le alinia, puteți utiliza formatarea șirurilor. Să permitem
fiecărei linii de program să obțină maximum 40 de caractere și să adăugăm comentariul după aceea. Programul din Lista
10-6 arată o modalitate simplă de a face acest lucru cu introducerea fișierului și parametrul inplace.

Lista 10-6. Adăugarea numerelor de linie la un script Python

# numberlines.py

import fișier de intrare

pentru linia din fileinput.input(inplace=True): line


= line.rstrip() num = fileinput.lineno()
print('{:<50} # {:2d}'.format(line, num))

Dacă rulați acest program pe sine, astfel:

$ python numberlines.py numberlines.py

ajungeți cu programul din Lista 10-7. Rețineți că programul în sine a fost modificat și că dacă îl rulați
astfel de mai multe ori, veți avea mai multe numere pe fiecare linie. Amintiți-vă că rstrip este o metodă de
șir care returnează o copie a unui șir, unde toate spațiile albe din dreapta au fost eliminate (consultați
secțiunea „Metode șiruri” din Capitolul 3 și Tabelul B-6 din Anexa B).

Lista 10-7. Programul de numerotare a liniilor cu numere de linie adăugate

# numberlines.py #1
#2
import fișier de intrare #3
#4
pentru linia din fileinput.input(inplace=True): line #5
= line.rstrip() num = fileinput.lineno() #6
print('{:<50} # {:2d}'.format(line, num)) #7
#8

Atenție Aveți grijă când utilizați parametrul inplace — este o modalitate ușoară de a distruge un fișier. Ar trebui să testați
programul cu atenție fără a seta în loc (acest lucru va imprima pur și simplu rezultatul), asigurându-vă că programul

funcționează înainte de a-l lăsa să vă modifice fișierele.

Pentru un alt exemplu de utilizare a introducerii fișierului, consultați secțiunea despre modulul aleatoriu, mai târziu în acest capitol.

Seturi, Heaps și Deques Există multe


structuri de date utile în jur, iar Python acceptă unele dintre cele mai comune. Unele dintre acestea, cum
ar fi dicționarele (sau tabelele hash) și listele (sau matricele dinamice), sunt parte integrantă a limbajului.
Altele, deși oarecum mai periferice, pot fi totuși utile uneori.

210
Machine Translated by Google

Capitolul 10 Bateriile incluse

Seturi

Cu mult timp în urmă, seturile au fost implementate de clasa Set în modulul seturi. Deși s-ar putea să întâlniți instanțe
Set în codul existent, există într-adevăr foarte puține motive să le utilizați singur, cu excepția cazului în care doriți să fiți
compatibil cu versiunea inversă. În versiunile recente, seturile sunt implementate de clasa de set încorporată. Aceasta
înseamnă că nu trebuie să importați modulul seturi - puteți doar să creați seturi direct.

>>> set(interval(10))
{0, 1, 2, 3, 4, 5, 6, 7, 8, 9}

Seturile sunt construite dintr-o secvență (sau alt obiect iterabil) sau specificate explicit cu acolade. Rețineți
că nu puteți specifica un set gol cu acolade, deoarece apoi ajungeți cu un dicționar gol.

>>> tip({})
<clasa 'dict'>

În schimb, trebuie să apelați set fără argumente. Utilizarea principală a seturilor este de a determina apartenența și, astfel,
duplicatele sunt ignorate:

>>> {0, 1, 2, 3, 0, 1, 2, 3, 4, 5} {0, 1, 2, 3, 4,


5}

La fel ca în cazul dicționarelor, ordonarea elementelor seturilor este destul de arbitrară și nu ar trebui să se bazeze pe ea.

>>> {'fee', 'fie', 'foe'} {'foe', 'fee',


'fie'}

În plus față de verificarea apartenenței, puteți efectua diverse operații standard de set (pe care poate le
cunoașteți din matematică), cum ar fi unirea și intersecția, fie folosind metode, fie utilizând aceleași operații ca
și pentru operațiile pe biți pe numere întregi (vezi Anexa B). De exemplu, puteți găsi unirea a două mulțimi
folosind fie metoda de unire a uneia dintre ele, fie operatorul pe biți sau , |.

>>> a = {1, 2, 3} >>>


b = {2, 3, 4} >>>
a.uniunea(b) {1, 2, 3,
4} >>> a | b {1, 2, 3,
4}

Iată câteva alte metode și operatorii corespunzători acestora; numele ar trebui să clarifice ceea ce ele
Rău:

>>> c = a & b
>>> c.issubset(a)
Adevărat

>>> c <= a
Adevărat

>>> c.issuperset(a)
Fals
>>> c >= a
Fals

211
Machine Translated by Google

Capitolul 10 Bateriile incluse

>>> a.intersecție(b) {2, 3}


>>> a & b {2, 3} >>>
a.diferență(b) {1} >>> a -
b {1} >>>
a .diferență_simetrică(b)
{1, 4} ^ b {1, 4} >>>
a.copy() {1, 2, 3} >>>
a.copy() este fals

>>> a

Există, de asemenea, diverse operații la locul lor, cu metode corespunzătoare, precum și metodele de bază de adăugare
și eliminare. Pentru mai multe informații, consultați secțiunea despre tipurile de set din Python Library Reference.

Sfat Dacă aveți nevoie de o funcție pentru a găsi, de exemplu, unirea a două seturi, puteți utiliza pur și simplu
versiunea nelegată a metodei unirii , din tipul setului . Acest lucru ar putea fi util, de exemplu, împreună cu reduce.

>>> my_sets = []
>>> pentru i în intervalul(10):
... my_sets.append(set(interval(i, i+5)))
...

>>> reduce(set.union, my_sets) {0,


1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13}

Seturile sunt mutabile și, prin urmare, nu pot fi utilizate, de exemplu, ca chei în dicționare. O altă problemă
este că seturile în sine pot conține numai valori imuabile (hashable) și, prin urmare, nu pot conține alte seturi.
Deoarece seturile de seturi apar adesea în practică, aceasta ar putea fi o problemă. Din fericire, există tipul
frozenset, care reprezintă seturi imuabile (și, prin urmare, hashabile).

>>> a = set()
>>> b = set()
>>> a.add(b)
Traceback (cel mai recent apel ultimul):
fișierul „<stdin>”, linia 1, în ?
TypeError: obiectele setate nu pot fi accesate
>>> a.add(frozenset(b))

Constructorul frozenset creează o copie a setului dat. Este util ori de câte ori doriți să utilizați un set fie ca
membru al altui set, fie ca cheie pentru un dicționar.

212
Machine Translated by Google

Capitolul 10 Bateriile incluse

Grămezi

O altă structură de date binecunoscută este heap-ul, un fel de coadă prioritară. O coadă de prioritate vă permite
să adăugați obiecte într-o ordine arbitrară și în orice moment (posibil între adăugare) să găsiți (și posibil să eliminați) cel
mai mic element. Funcționează mult mai eficient decât, să zicem, utilizarea minului pe o listă.
De fapt, nu există un tip de heap separat în Python - doar un modul cu unele funcții de manipulare a heap-
ului. Modulul se numește heapq (q înseamnă coadă) și conține șase funcții (vezi Tabelul 10-5), dintre care primele patru
sunt direct legate de manipularea heap-ului. Trebuie să utilizați o listă ca obiect heap în sine.

Tabelul 10-5. Câteva funcții importante în modulul de intrare fișier

Func ie Descriere

heappush(grămadă, Împinge x pe grămadă

x) heappop(grămadă) Scoate cel mai mic element din grămada

heapify(grămadă) Implementează proprietatea heap pe o listă arbitrară

heapreplace(grămadă, x) Scoate de pe cel mai mic element și împinge

x nlargest(n, iter) Returnează cele mai mari n elemente ale iter

nsmallest(n, iter) Returnează cele mai mici n elemente ale iter

Funcția heappush este folosită pentru a adăuga un articol la un heap. Rețineți că nu ar trebui să-l utilizați pe nicio listă
veche - doar una care a fost creată prin utilizarea diferitelor funcții heap. Motivul pentru aceasta este că ordinea
elementelor este importantă (chiar dacă poate părea un pic la întâmplare; elementele nu sunt exact sortate).

*
>>> din heapq import >>>
din random import shuffle >>> data =
list(range(10)) >>> shuffle(data) >>>
heap = [] >>> pentru n în date:
heappush(heap , n)

...
...
>>> heap
[0, 1, 3, 6, 2, 8, 4, 7, 9, 5] >>> heap(heap,
0.5) >>> heap [0, 0.5, 3, 6, 1, 8 , 4, 7, 9,
5, 2]

Ordinea elementelor nu este atât de arbitrară pe cât pare. Nu sunt în ordine strict sortată, dar există o garanție făcută:
elementul din poziția i este întotdeauna mai mare decât cel din poziția i // 2 (sau, dimpotrivă, este mai mic decât
elementele din pozițiile 2 * i și 2 * i + 1). Aceasta este baza pentru algoritmul heap de bază. Aceasta se numește
proprietatea heap.
Funcția heappop dezactivează cel mai mic element, care se găsește întotdeauna la indexul 0, și se asigură că cel
mai mic dintre elementele rămase preia această poziție (în timp ce se păstrează proprietatea heap).
Chiar dacă apariția primului element dintr-o listă nu este teribil de eficientă în general, nu este o problemă aici,
deoarece heappop se amestecă în spatele scenei.

213
Machine Translated by Google

Capitolul 10 Bateriile incluse

>>> heappop(heap) 0

>>> heappop(heap)
0,5 >>> heappop(heap)
1

>>>
grămada [2, 5, 3, 6, 9, 8, 4, 7]

Funcția heapify preia o listă arbitrară și o transformă într-un heap legal (adică impune proprietatea heap) prin cea mai
mică cantitate posibilă de amestecare. Dacă nu vă construiți heap-ul de la zero cu heapppush, aceasta este funcția pe
care trebuie să o utilizați înainte de a începe să utilizați heappush și heapppush.

>>> heap = [5, 8, 0, 3, 6, 7, 9, 1, 4, 2] >>> heapify(heap)


>>> heap [0, 1, 5, 3, 2, 7, 9, 8, 4, 6]

Funcția heapreplace nu este la fel de folosită ca celelalte. Scoate cel mai mic element din morman și apoi împinge
un nou element pe el. Acesta este puțin mai eficient decât un heappop urmat de un heapppush.

>>> heapreplace(heap, 0,5) 0

>>> heap
[0,5, 1, 5, 3, 2, 7, 9, 8, 4, 6] >>>
heappreplace(heap, 10) 0,5

>>>
grămada [1, 2, 5, 3, 6, 7, 9, 8, 4, 10]

Celelalte două funcții ale modulului heapq, nlargest(n, iter) și nsmallest(n, iter), sunt folosite pentru a găsi cele n
elemente mai mari sau, respectiv, cele mai mici ale oricărui iter de obiect iterabil. Puteți face acest lucru utilizând sortarea
(de exemplu, folosind funcția sortat) și tăierea, dar algoritmul heap este mai rapid și mai eficient din punct de vedere al
memoriei (și, ca să nu mai vorbim, mai ușor de utilizat).

Deques (și alte colecții)


Cozile duble, sau deques, pot fi utile atunci când trebuie să eliminați elemente în ordinea în care au fost adăugate. Tipul
deque, împreună cu alte câteva tipuri de colecții, se găsesc în modulul de colecții.
Un deque este creat dintr-un obiect iterabil (la fel ca seturile) și are mai multe metode utile.

>>> din colecții import deque >>> q =


deque(range(5)) >>> q.append(5) >>>
q.appendleft(6) >>> q deque([6, 0, 1, 2, 3, 4,
5]) >>> q.pop() 5 >>> q.popleft()

214
Machine Translated by Google

Capitolul 10 Bateriile incluse

6
>>> q.rotate(3) >>>
q deque([2, 3, 4, 0,
1]) >>> q.rotate(-1) >>> q
deque([3, 4, 0, 1, 2])

Deque-ul este util deoarece permite adăugarea și popping-ul eficient la început (în stânga), ceea ce nu poți face cu
liste. Ca efect secundar drăguț, puteți, de asemenea, să rotiți elementele (adică să le deplasați la dreapta sau la
stânga, înfășurându-le în jurul capetelor) eficient. Obiectele Deque au, de asemenea, metodele extend și extendleft,
extinde lucrând ca metoda listă corespunzătoare și extendleft lucrând analog cu appendleft. Rețineți că elementele
din obiectul iterabil folosit în extensileft vor apărea în deque în ordine inversă.

timp

Modulul de timp conține funcții pentru, printre altele, obținerea orei curente, manipularea orelor și a datelor,
citirea datelor din șiruri și formatarea datelor ca șiruri. Datele pot fi reprezentate fie ca un număr real (secundele
de la 0 ore, 1 ianuarie în „epocă”, un an dependent de platformă; pentru UNIX, este 1970) sau un tuplu care conține
nouă numere întregi. Aceste numere întregi sunt explicate în Tabelul 10-6. De exemplu, tuplul

(2008, 1, 21, 12, 2, 56, 0, 21, 0)

reprezintă 21 ianuarie 2008, la ora 12:02:56, care este o zi de luni și a douăzeci și unu a anului (fără ora de vară).

Tabelul 10-6. Câmpurile Python Date Tuples

Index Camp Valoare

0 An De exemplu, 2000, 2001 și așa mai departe


1 Lună În intervalul 1-12
2 Zi În intervalul 1-31
3 Ora În intervalul 0-23
4 Minut În intervalul 0-59
5 Al doilea În intervalul 0-61
6 Ziua săptămânii În intervalul 0-6, unde luni este 0
7 Ziua lui Iulian În intervalul 1–366
8 Economii de vară 0, 1 sau –1

Intervalul pentru secunde este de la 0 la 61 pentru a ține cont de secundele intersecte și secundele de două intersecții.
Numărul de vară este o valoare booleană (adevărat sau fals), dar dacă utilizați –1, mktime (o funcție care convertește un
astfel de tuplu într-un marcaj de timp măsurat în secunde de la epocă) probabil că va înțelege corect. Unele dintre cele mai
importante funcții din modulul de timp sunt descrise în Tabelul 10-7.

215
Machine Translated by Google

Capitolul 10 Bateriile incluse

Tabelul 10-7. Câteva funcții importante în modulul de timp

Func ie Descriere
asctime([tuplu]) oră Convertește un tuplu de timp într-un șir

locală([sec.]) Convertește secundele într-un tuplu de dată, ora locală

mktime(tuplu) somn(sec.) Convertește un tuplu de timp în ora locală

strptime(șir[, format]) Doarme (nu face nimic) secunde secunde

oră() Analizează un șir într-un tuplu de timp

Ora curentă (secunde de la epocă, UTC)

Funcția time.asctime formatează ora curentă ca șir, astfel:

>>> time.asctime()
„Luni, 18 iulie 14:06:07 2016”

De asemenea, puteți furniza un tuplu de dată (cum ar fi cele create de ora locală) dacă nu doriți ora curentă. (Pentru o
formatare mai elaborată, puteți utiliza funcția strftime, descrisă în documentația standard.)

Funcția time.localtime convertește un număr real (secunde de la epocă) într-un tuplu de dată, ora locală.
Dacă doriți ora universală, utilizați gmtime în schimb.
Funcția time.mktime convertește un tuplu de dată în timpul de la epoch în secunde; este inversul timpului local.

Funcția time.sleep face ca interpretul să aștepte un anumit număr de secunde.


Funcția time.strptime convertește un șir cu formatul returnat de asctime într-un tuplu de dată. (Argumentul opțional format
urmează aceleași reguli ca cele pentru strftime; consultați documentația standard.)
Funcția time.time returnează timpul curent (universal) ca secunde de la epocă. Chiar dacă epoca poate varia de la platformă la
platformă, puteți cronometra în mod fiabil ceva păstrând rezultatul timpului de înainte și după eveniment (cum ar fi un apel de funcție)
și apoi calculând diferența. Pentru un exemplu al acestor funcții, consultați secțiunea următoare, care acoperă modulul aleatoriu.

Funcțiile prezentate în Tabelul 10-7 sunt doar o selecție dintre cele disponibile din modulul de timp. Majoritatea funcțiilor din
acest modul îndeplinesc sarcini similare sau legate de cele descrise în această secțiune. Dacă aveți nevoie de ceva care nu este acoperit
de funcțiile descrise aici, aruncați o privire la secțiunea despre modulul de timp din Python Library Reference; sunt șanse să găsești
exact ceea ce cauți.
În plus, sunt disponibile două module mai recente legate de timp: datetime (care acceptă data și
aritmetica timpului) și timeit (care vă ajută să cronometrați bucăți din codul dvs.). Puteți găsi mai multe informații despre ambele în Python
Library Reference, iar ora este, de asemenea, discutată pe scurt în Capitolul 16.

Aleatoriu
Modulul aleatoriu conține funcții care returnează numere pseudoaleatoare, care pot fi utile pentru simulări sau orice
program care generează rezultate aleatoare. Rețineți că, deși numerele par complet aleatorii, există un sistem previzibil care
le stă la baza. Dacă aveți nevoie de aleatorie reală (pentru criptografie sau orice altceva legat de securitate, de exemplu), ar
trebui să verificați funcția urandom a modulului os. Clasa SystemRandom din modulul aleatoriu se bazează pe același tip de
funcționalitate și vă oferă date care sunt aproape ale aleatoriei reale.

Unele funcții importante din acest modul sunt prezentate în Tabelul 10-8.

216
Machine Translated by Google

Capitolul 10 Bateriile incluse

Tabelul 10-8. Câteva funcții importante în modulul aleatoriu

Func ie Descriere

random() Returnează un număr real aleator n astfel încât 0 n 1

getrandbits(n) Returnează n biți aleatori, sub forma unui număr întreg lung

uniform(a, b) Returnează un număr real aleator n astfel încât a n b

randrange([start], stop, [step]) choice(seq) Returnează un număr aleatoriu din interval (start, stop, step)

shuffle(seq[, aleator]) sample(seq, n) Returnează un element aleatoriu din secvența secv

Amestecă secvența secvenței în loc

Alege n elemente aleatorii, unice din secvența secv

Funcția random.random este una dintre cele mai de bază funcții aleatoare; pur și simplu returnează un număr pseudo-aleatoriu n
astfel încât 0 n 1. Dacă nu este exact ceea ce aveți nevoie, probabil ar trebui să utilizați una dintre celelalte funcții, care oferă
funcționalitate suplimentară. Funcția random.getrandbits returnează un număr dat de biți (cifre binare), sub forma unui număr
întreg.
Funcția random.uniform, atunci când este furnizată cu doi parametri numerici a și b, returnează un număr real
aleator (uniform distribuit) n astfel încât a n b. Deci, de exemplu, dacă doriți un unghi aleatoriu, puteți utiliza uniform(0, 360).

Funcția random.randrange este funcția standard pentru generarea unui număr întreg aleatoriu în intervalul pe care l-ați
obține apelând interval cu aceleași argumente. De exemplu, pentru a obține un număr aleatoriu în intervalul de la 1 la 10 (inclusiv),
ați folosi randrange(1, 11) (sau, alternativ, randrange(10) + 1), iar dacă doriți un număr întreg pozitiv impar aleatoriu mai mic de 20,
ați folosi randrange(1, 20, 2).
Funcția random.choice alege (uniform) un element aleatoriu dintr-o secvență dată.
Funcția random.shuffle amestecă aleatoriu elementele unei secvențe (mutabile), astfel încât fiecare
posibila comandă este la fel de probabilă.
Funcția random.sample alege (uniform) un număr dat de elemente dintr-o secvență dată,
asigurându-vă că toate sunt diferite.

Notă Pentru cei înclinați statistic, există și alte funcții similare cu uniform care returnează numere aleatoare
eșantionate în funcție de diverse alte distribuții, cum ar fi betavariate, exponențiale, Gaussian și multe altele.

Să ne uităm la câteva exemple folosind modulul aleatoriu. În aceste exemple, folosesc câteva dintre funcțiile din modulul de timp
descris anterior. În primul rând, să obținem numerele reale reprezentând limitele intervalului de timp (anul 2016). Faceți asta
exprimând data ca un tuplu de timp (folosind -1 pentru ziua săptămânii, ziua anului și ora de vară, făcându-l pe Python să calculeze
singur) și apelând mktime pe aceste tupluri:

*
de la import aleatoriu de
*
la ora import data1 =
(2016, 1, 1, 0, 0, 0, -1, -1, -1) time1 = mktime(data1) data2 =
(2017, 1, 1, 0, 0, 0 , -1, -1, -1) time2 = mktime(data2)

Apoi generați un număr aleatoriu uniform în acest interval (limita superioară exclusă):

>>> timp_aleatoriu = uniform(timp1, timp2)

217
Machine Translated by Google

Capitolul 10 Bateriile incluse

Apoi, pur și simplu convertiți acest număr înapoi la o dată lizibilă.

>>> print(asctime(localtime(random_time)))
Marți, 16 august 10:11:04 2016

Pentru exemplul următor, să întrebăm utilizatorul câte zaruri să arunce și câte fețe ar trebui să aibă fiecare.
Mecanismul de aruncare a zarurilor este implementat cu randrange și o buclă for.

din import aleatoriu randrange num


= int(input('Câte zaruri? ')) laturi = int(input('Câte
fețe pe zar? '))
suma = 0
pentru i în interval(num): sum += randrange(sides) + 1
print('Rezultatul este', sum)

Dacă puneți acest lucru într-un fișier script și îl rulați, obțineți o interacțiune de genul următor:

Câte zaruri? 3 Câte


fețe pe zar? 6 Rezultatul este 10

Acum presupuneți că ați creat un fișier text în care fiecare rând de text conține o avere. Apoi puteți utiliza modulul de
introducere a fișierelor descris mai devreme pentru a pune averile într-o listă și apoi selectați unul aleatoriu.

# fortune.py
import fileinput, random fortunes
= list(fileinput.input()) print
random.choice(fortunes)

În UNIX sau macOS, puteți testa acest lucru în fișierul standard de dicționar /usr/share/dict/words pentru a obține un
cuvânt aleatoriu.

$ python fortune.py /usr/share/dict/words dodge

Ca ultim exemplu, să presupunem că doriți ca programul dvs. să vă împartă cărțile, una câte una, de fiecare dată când
apăsați Enter de pe tastatură. De asemenea, vrei să te asiguri că nu primești același card de mai multe ori. În primul rând,
faci un „pachet de cărți” – o listă de șiruri.

>>> valori = list(range(1, 11)) + 'Jack Queen King'.split() >>> costume = 'diamonds
clubs hearts spades'.split() >>> pachet = ['{} din { }'.format(v, s) pentru v în valori
pentru s în costume]

Pachetul pe care tocmai l-am creat nu este foarte potrivit pentru un joc de cărți. Să aruncăm o privire la câteva dintre cărți:

>>> din pprint import pprint >>>


pprint(deck[:12]) ['1 de diamante', '1 de
crose', '1 de inimioare', '1 de pică',

218
Machine Translated by Google

Capitolul 10 Bateriile incluse

„2 de diamante”, „2
de crose”, „2 de
inimioare”, „2 de pică”,
„3 de diamante”, „3
de tresele”, „3 de
inimioare”, „3 de pică”]

Cam prea comandat, nu-i așa? Este ușor de reparat.

>>> din import aleatoriu shuffle >>>


shuffle(deck) >>> pprint(deck[:12]) ['3 of
spades', '2 of diamonds', '5 of diamonds',
'6 of spades', „8 de diamante”, „1 de
trefte”, „5 de inimi”,

„Regina diamantelor”,
„Regina inimilor”,
„Regele inimilor”,
„Jack of diamonds”,
„Regina cluburilor”]

Rețineți că tocmai am printat primele 12 cărți aici, pentru a economisi spațiu. Simțiți-vă liber să aruncați o privire la întregul
pachet.
În cele din urmă, pentru ca Python să vă împartă un card de fiecare dată când apăsați Enter pe tastatură, până când nu mai
sunt cărți, pur și simplu creați o buclă în timp. Presupunând că ați introdus codul necesar pentru a crea pachetul într-un fișier de
program, puteți adăuga pur și simplu următoarele la sfârșit:

while deck: input(deck.pop())

Rețineți că, dacă încercați această buclă while în interpretul interactiv, veți obține un șir gol tipărit de fiecare dată când
apăsați Enter. Acest lucru se datorează faptului că input returnează ceea ce scrieți (care nu este nimic) și care va fi tipărit. Într-
un program normal, această valoare returnată de la intrare este pur și simplu ignorată. Pentru ca acesta să fie „ignorat” în
mod interactiv, trebuie doar să atribuiți rezultatul intrării unei variabile pe care nu o veți privi din nou și numiți-o așa cum ar
fi ignora.

shelve și json În capitolul

următor, veți învăța cum să stocați date în fișiere, dar dacă doriți o soluție de stocare cu adevărat simplă, modulul shelve poate
face cea mai mare parte a muncii pentru dvs. Tot ce trebuie să faceți este să îi furnizați un nume de fișier. Singura funcție de
interes în raft este deschisă. Când este apelat (cu un nume de fișier), returnează un obiect Shelf, pe care îl puteți folosi pentru a
stoca lucruri. Tratați-l ca pe un dicționar normal (cu excepția faptului că cheile trebuie să fie șiruri de caractere), iar când ați
terminat (și doriți ca lucrurile să fie salvate pe disc), apelați metoda sa de închidere.

219
Machine Translated by Google

Capitolul 10 Bateriile incluse

O posibilă capcană

Este important să realizați că obiectul returnat de shelve.open nu este o mapare obișnuită, așa cum
demonstrează următorul exemplu:

>>> import shelve >>>


s = shelve.open('test.dat') >>> s['x'] = ['a',
'b', 'c'] >>> s['x '].append('d') >>> s['x']
['a', 'b', 'c']

Unde s-a dus „d”-ul?


Explicația este simplă: atunci când căutați un element dintr-un obiect de raft, obiectul este reconstruit
din versiunea sa stocată; iar când atribuiți un element unei taste, acesta este stocat. Ce s-a întâmplat în
exemplul precedent a fost următorul:

•Lista ['a', 'b', 'c'] a fost stocată în s sub tasta 'x'.

• Reprezentarea stocată a fost preluată, a fost construită o nouă listă din ea și „d” a fost adăugat
la copie. Această versiune modificată nu a fost stocată!
• În cele din urmă, originalul este preluat din nou - fără „d”.

Pentru a modifica corect un obiect care este stocat folosind modulul rafturi, trebuie să legați o variabilă temporară la
copia preluată și apoi să stocați copia din nou după ce a fost modificată2 :

>>> temp = s['x'] >>>


temp.append('d') >>> s['x']
= temp >>> s['x'] ['a', 'b' ,
„c”, „d”]

Există o altă modalitate de a ocoli această problemă: setați parametrul writeback al funcției deschise la true. Dacă faceți
acest lucru, toate structurile de date pe care le citiți sau le atribuiți raftului vor fi păstrate în memorie (în cache) și scrise
înapoi pe disc numai când închideți raftul. Dacă nu lucrați cu o cantitate imensă de date și nu doriți să vă faceți griji în
legătură cu aceste lucruri, setarea scrierii înapoi la true poate fi o idee bună. Apoi, trebuie să vă asigurați că închideți
raftul când ați terminat; o modalitate de a face acest lucru este utilizarea raftului ca manager de context, la fel ca în cazul
unui fișier deschis, așa cum este explicat în capitolul următor.

Un exemplu simplu de bază de date

Lista 10-8 prezintă o aplicație simplă de bază de date care utilizează modulul shelve.

Lista 10-8. O aplicație simplă de bază de date

# database.py
import sys, shelf

def store_person(db):

2 Mulțumesc lui Luther Blissett pentru că a subliniat acest lucru.

220
Machine Translated by Google

Capitolul 10 Bateriile incluse

"""

Interogați utilizatorul pentru date și stocați-le în obiectul de raft


"""

pid = input('Introduceți numărul unic de identificare:


') persoană = {} persoană['nume'] = input('Introduceți
numele: ') persoană['vârstă'] = input('Introduceți
vârsta: ') persoană['telefon '] = input('Introduceți
numărul de telefon: ') db[pid] = persoană

def lookup_person(db):
"""

Interogați utilizatorul pentru ID și câmpul dorit și obțineți datele corespunzătoare de la obiectul raft
"""

pid = input('Introduceți numărul ID: ')


field = input('Ce doriți să știți? (nume, vârstă, telefon) ') field = field.strip().lower()

print(field.capitalize() + ':', db[pid][field])

def print_help():
print('Comenzile disponibile sunt:') print('store:
Stochează informații despre o persoană') print('lookup: Caută o
persoană din numărul ID') print('quit: Salvați modificările și ieșiți
') imprimare('?
: Imprimă acest mesaj')

def enter_command():
cmd = input('Introduceți comanda (? pentru ajutor): ')
cmd = cmd.strip().lower() return cmd

def main():
database = shelve.open('C:\\database.dat') # Poate doriți să schimbați acest nume încercați: în
timp ce este adevărat:

cmd = enter_command() if
cmd == 'magazin':
store_person(bază de date)
elif cmd == 'lookup':
lookup_person(bază de date)
elif cmd == '?': print_help() elif cmd
== 'quit': return

în sfârșit:
database.close()

if name == '__main__': main()

221
Machine Translated by Google

Capitolul 10 Bateriile incluse

Programul prezentat în Lista 10-8 are câteva caracteristici interesante:

•Totul este învelit în funcții pentru a face programul mai structurat. (O posibilă îmbunătățire
este gruparea acelor funcții ca metode ale unei clase.)

•Programul principal se află în funcția principală, care este apelată numai dacă __name__ ==
'__principal__'. Aceasta înseamnă că puteți importa acest lucru ca modul și apoi puteți apela
funcția principală dintr-un alt program.

• Deschid o bază de date (raft) în funcția principală și apoi o transmit ca parametru celorlalte funcții
care au nevoie de ea. Aș fi putut folosi și o variabilă globală, pentru că acest program este atât
de mic, dar este mai bine să eviți variabilele globale în majoritatea cazurilor, cu excepția cazului în
care ai un motiv să le folosești.

•După ce citesc unele valori, fac o versiune modificată apelând strip și cobor pe ele, deoarece dacă o
cheie furnizată trebuie să se potrivească cu cea stocată în baza de date, cele două trebuie să fie
exact la fel. Dacă folosiți întotdeauna strip și mai jos pe ceea ce introduc utilizatorii, le puteți permite
să fie neglijenți cu privire la utilizarea literelor mari sau mici și a spațiilor albe suplimentare. De
asemenea, rețineți că am folosit majuscule atunci când am tipărit numele câmpului.

Am folosit try și în sfârșit pentru a mă asigura că baza de date este închisă corect. Nu știi niciodată când ceva
ar putea merge prost (și primești o excepție), iar dacă programul se termină fără a închide baza de date în mod
corespunzător, s-ar putea să ajungi cu un fișier de bază de date corupt care este în esență inutil. Folosind try și, în
sfârșit, eviți asta. Aș fi putut folosi raftul și ca manager de context, așa cum este explicat în capitolul 11.

Deci, să scoatem această bază de date pentru o învârtire. Iată un exemplu de interacțiune:

Introdu comanda (? pentru ajutor): ?


Comenzile disponibile sunt: stocare:
Stochează informații despre o persoană căutare: Caută o
persoană din numărul ID ieșire: Salvați modificările și
ieșiți?
: Imprimă acest mesaj
Introduceți comanda (? pentru ajutor):
magazin Introduceți un număr ID unic: 001
Introduceți numele: Dl. Gumby Introduceți
vârsta: 42 Introduceți numărul de telefon:
555-1234 Introduceți comanda (? pentru
ajutor): căutare Introduceți numărul ID: 001 Ce
ai vrea sa stii? (nume, vârstă, telefon) telefon
Telefon: 555-1234 Introdu comanda (? pentru ajutor): ieși

Această interacțiune nu este îngrozitor de interesantă. Aș fi putut face exact același lucru cu un dicționar obișnuit în
loc de obiectul de raft. Dar acum că am părăsit programul, să vedem ce se întâmplă când îl repornesc — poate a doua zi?

Introduceți comanda (? pentru ajutor): căutare


Introduceți numărul ID: 001 Ce ați dori să știți?
(nume, vârstă, telefon) nume Nume: domnul Gumby Introdu comanda
(? pentru ajutor): ieși

222
Machine Translated by Google

Capitolul 10 Bateriile incluse

După cum puteți vedea, programul citește în fișierul pe care l-am creat prima dată, iar domnul Gumby este încă acolo!
Simțiți-vă liber să experimentați cu acest program și să vedeți dacă îi puteți extinde funcționalitatea și îl puteți îmbunătăți
ușurința în utilizare. Poate te poți gândi la o versiune pe care o folosești pentru tine?

Sfat Dacă doriți să salvați datele într-o formă care poate fi citită cu ușurință de programele scrise în alte
limbi, este posibil să doriți să căutați formatul JSON. Biblioteca standard Python oferă modulul json pentru a
lucra cu șiruri JSON, conversia între ele și valorile Python.

re

Unii oameni, când se confruntă cu o problemă, gândesc: „Știu, voi folosi expresii obișnuite”.
Acum au două probleme.

— Jamie Zawinski

Modulul re conține suport pentru expresii regulate. Dacă ați auzit despre expresiile regulate, probabil că știți cât de puternice
sunt acestea; dacă nu ai, pregătește-te să fii uimit.
Ar trebui să rețineți, totuși, că stăpânirea expresiilor regulate poate fi puțin dificilă la început. Cheia este să învățați despre
ele puțin la un moment dat - doar căutați părțile de care aveți nevoie pentru o anumită sarcină. Nu are rost să memorezi totul din
față. Această secțiune descrie principalele caracteristici ale modulului re și ale expresiilor regulate și vă permite să începeți.

Sfat În plus față de documentația standard, „Regular Expression HOWTO” al lui Andrew Kuchling (https:// docs.python.org/3/howto/regex.html)

este o sursă utilă de informații despre expresiile regulate în Python.

Ce este o expresie regulată?


O expresie regulată (numită și regex sau regexp) este un model care se poate potrivi cu o bucată de text. Cea mai simplă formă
de expresie regulată este doar un șir simplu, care se potrivește singur. Cu alte cuvinte, expresia regulată „python” se potrivește cu
șirul „python”. Puteți utiliza acest comportament de potrivire pentru lucruri precum căutarea de modele în text, înlocuirea anumitor
modele cu unele valori calculate sau împărțirea textului în bucăți.

Wildcardul
O expresie regulată poate potrivi mai mult de un șir și creați un astfel de model folosind câteva caractere speciale. De exemplu,
caracterul punct (punct) se potrivește cu orice caracter (cu excepția unei linii noi), astfel încât expresia regulată „.ython” se potrivește
atât cu șirul „python”, cât și cu șirul „jython”. De asemenea, ar potrivi șiruri precum „qython”, „+ython” sau „ython” (în care prima
literă este un singur spațiu), dar nu și șiruri precum
două,
„cpython”
nici zero.
sau „ython”, deoarece punctul se potrivește cu un singur literă, și nici

Deoarece se potrivește cu „orice” (orice caracter, cu excepția unei linii noi), punctul se numește wildcard.

223
Machine Translated by Google

Capitolul 10 Bateriile incluse

Evadarea personajelor speciale


Personajele obișnuite se potrivesc cu ele însele și nimic altceva. Personajele speciale, însă, sunt o poveste diferită.
De exemplu, imaginați-vă că doriți să potriviți șirul „python.org”. Folosiți pur și simplu modelul „python.org”?
Ai putea, dar s-ar potrivi și cu „pythonzorg”, de exemplu, ceea ce probabil nu ți-ai dori. (Punctul se potrivește cu orice caracter, cu excepția
unei linii noi, vă amintiți?) Pentru a face ca un caracter special să se comporte ca unul normal, îl scapi , așa cum am demonstrat cum să scapi
de ghilimele din șiruri în Capitolul 1. Îi plasezi o bară oblică inversă în fața lui. . Astfel, în acest exemplu, ați folosi „python\\.org”, care s-ar potrivi
cu „python.org” și nimic altceva.

Rețineți că pentru a obține o singură bară oblică inversă, care este necesară aici de modulul re, trebuie să scrieți două bare oblice
inverse în șir - pentru a o scăpa din interpret. Astfel, aveți două niveluri de evadare aici: (1) din interpret și (2) din modulul re. (De fapt, în
unele cazuri, puteți scăpa folosind o singură bară oblică inversă și ca interpretul să o scape automat, dar nu vă bazați pe ea.) Dacă v-ați
săturat să dubleți barele oblice inverse, utilizați un șir brut, cum ar fi r'python\.org'.

Seturi de caractere

Potrivirea oricărui personaj poate fi utilă, dar uneori vrei mai mult control. Puteți crea un așa-numit set de caractere prin includerea unui
subșir între paranteze. Un astfel de set de caractere se va potrivi cu oricare dintre caracterele pe care le conține. De exemplu, „[pj]ython” s-
ar potrivi atât cu „python” cât și cu „jython”, dar nimic altceva. De asemenea, puteți utiliza intervale, cum ar fi „[az]” pentru a potrivi orice
caracter de la a la z (în ordine alfabetică) și puteți combina astfel de intervale punând unul după altul, cum ar fi „[a-zA-Z0-9]” pentru a potrivi
literele mari și mici și cifrele. (Rețineți că setul de caractere se va potrivi doar cu un astfel de caracter, totuși.)

Pentru a inversa setul de caractere, puneți mai întâi caracterul ^, ca în „[^abc]”, pentru a se potrivi cu orice caracter, cu excepția a, b sau c.

PERSONAJE SPECIALE ÎN SETURILE DE CARACTERE

În general, caracterele speciale, cum ar fi punctele, asteriscurile și semnele de întrebare trebuie să fie
eliminate cu o bară oblică inversă dacă doriți să apară ca caractere literale în model, mai degrabă decât să
funcționeze ca operatori de expresie regulată. În seturile de caractere, evadarea acestor caractere nu este în
general necesară (deși perfect legală). Cu toate acestea, ar trebui să țineți cont de următoarele reguli:

• Trebuie să scăpați de semnul caret (^) dacă apare la începutul setului de caractere, cu excepția
cazului în care doriți să funcționeze ca un operator de negație. (Cu alte cuvinte, nu-l pune la
început decât dacă vrei să spui serios.)

• În mod similar, paranteza dreaptă (]) și liniuța (-) trebuie puse fie la începutul setului de caractere,
fie evadate cu o bară oblică inversă. (De fapt, liniuța poate fi pusă și la sfârșit, dacă doriți.)

Alternative și submodele
Seturile de caractere sunt frumoase atunci când lași fiecare literă să varieze independent, dar ce se întâmplă dacă vrei să potriviți doar
șirurile „python” și „perl”? Nu puteți specifica un astfel de model specific cu seturi de caractere sau metacaractere.
În schimb, utilizați caracterul special pentru alternative: caracterul pipe (|). Deci, modelul tău ar fi „python|perl”.

Cu toate acestea, uneori nu doriți să utilizați operatorul de alegere pe întregul model - doar o parte a acestuia.
Pentru a face acest lucru, includeți partea sau submodelul între paranteze. Exemplul anterior ar putea fi rescris ca „p(ython|erl)”. (Rețineți că
termenul submodel se poate aplica și unui singur caracter.)

224
Machine Translated by Google

Capitolul 10 Bateriile incluse

Submodele opționale și repetate


Adăugând un semn de întrebare după un submodel, îl faceți opțional. Poate apărea în șirul potrivit, dar nu este strict
obligatoriu. Deci, de exemplu, acest model (puțin ilizibil):

r'(http://)?(www\.)?python\.org'

s-ar potrivi cu toate următoarele șiruri (și nimic altceva):

„http://www.python.org” „http://

python.org” „www.python.org” „python.org”

Aceste lucruri merită remarcate aici:


• Am scăpat de puncte, pentru a le împiedica să funcționeze ca metacaractere.
• Am folosit un șir brut pentru a reduce numărul de bare oblice inverse necesare.

•Fiecare submodel opțional este inclus în paranteze.

•Submodelele opționale pot apărea sau nu, independent unele de altele.

Semnul întrebării înseamnă că submodelul poate apărea o dată sau deloc. Câțiva alți operatori vă permit să repetați
un submodel de mai multe ori.

•(model)*: modelul se repetă de zero sau de mai multe ori.

•(pattern)+: modelul se repetă o dată sau de mai multe ori.

•(model){m,n}: modelul se repetă de la m la n ori.

Deci, de exemplu, r'w*\.python\.org' se potrivește cu 'www.python.org' dar și „.python.org”, „ww.python. org' și


'wwwwww.python.org'. În mod similar, r'w+\.python\.org' se potrivește cu 'w.python.org', dar nu cu '.python.org',
iar r'w{3,4}\.python\.org' se potrivește doar cu 'www. python.org” și „www.python.org”.

Notă Termenul de potrivire este folosit aici pentru a însemna că modelul se potrivește întregului șir. Funcția de
potrivire (vezi Tabelul 10-9) necesită doar ca modelul să se potrivească cu începutul șirului.

Începutul și sfârșitul unui șir


Până acum, v-ați uitat doar la un model care se potrivește cu un șir întreg, dar puteți încerca și să găsiți un
subșir care să se potrivească cu modelul, cum ar fi subșirul „www” al șirului „www.python.org” potrivirea
modelului „w+”. Când căutați subșiruri ca acesta, uneori poate fi util să ancorați acest subșir fie la începutul, fie la
sfârșitul întregului șir. De exemplu, ați putea dori să potriviți „ht+p” la începutul unui șir, dar nu în altă parte. Apoi
folosiți o liniuță ('^') pentru a marca începutul. De exemplu, „^ht+p” s-ar potrivi cu „http://python.org” (și „htttttp://
python.org”, de altfel), dar nu „www.http.org”. În mod similar, sfârșitul unui șir poate fi indicat prin semnul dolarului
($).

Notă Pentru o listă completă a operatorilor de expresii regulate, consultați secțiunea „Sintaxa expresiilor regulate”
din Biblioteca Python.

225
Machine Translated by Google

Capitolul 10 Bateriile incluse

Conținutul re Modulului
Să știi să scrii expresii regulate nu este prea bine dacă nu le poți folosi pentru nimic. Modulul re conține mai
multe funcții utile pentru lucrul cu expresii regulate. Unele dintre cele mai importante sunt descrise în Tabelul 10-9.

Tabelul 10-9. Câteva funcții importante din modulul re

Func ie Descriere

compile(pattern[, flags]) Creează un obiect model dintr-un șir cu o expresie regulată

search(pattern, string[, flags]) Caută model în șir

match(pattern, string[, flags]) split(pattern, Se potrivește cu modelul de la începutul șirului

string[, maxsplit=0]) Împarte un șir după aparițiile modelului findall(pattern , șir) sub(pat, repl,

șir[, numără=0]) escape(șir) Returnează o listă cu toate aparițiile modelului în șir

Înlocuiește aparițiile pat în șir cu repl

Escape toate caracterele speciale ale expresiilor regulate din șir

Funcția re.compile transformă o expresie regulată (scrisă ca șir) într-un obiect model, care poate fi folosit pentru
o potrivire mai eficientă. Dacă utilizați expresii regulate reprezentate ca șiruri de caractere atunci când apelați
funcții precum căutare sau potrivire, acestea trebuie oricum transformate în obiecte expresii regulate în interior.
Făcând acest lucru o dată, cu funcția de compilare, acest pas nu mai este necesar de fiecare dată când utilizați
modelul. Obiectele model au funcțiile de căutare/potrivire ca metode, deci re.search(pat, șir) (unde pat este o expresie
regulată scrisă ca șir) este echivalent cu pat.search(șir) (unde pat este un obiect model creat cu compilare). Obiectele
de expresie regulată compilate pot fi, de asemenea, utilizate în funcțiile re normale.

Funcția re.search caută un șir dat pentru a găsi primul subșir, dacă există, care se potrivește cu expresia
regulată dată. Dacă se găsește unul, este returnat un MatchObject (evaluat la adevărat); în caz contrar, se
returnează None (evaluarea la fals). Datorită naturii valorilor returnate, funcția poate fi utilizată în instrucțiuni
condiționate, astfel:

if re.search(pat, string): print('Am


găsit!')

Cu toate acestea, dacă aveți nevoie de mai multe informații despre subșirul potrivit, puteți examina
MatchObject returnat. (Veți afla mai multe despre MatchObject în secțiunea următoare.)
Funcția re.match încearcă să se potrivească cu o expresie regulată la începutul unui șir dat. Deci re.
match('p', 'python') returnează true (un obiect potrivire), în timp ce re.match('p', 'www.python.org') returnează
false (Niciuna).

Notă Funcția de potrivire va raporta o potrivire dacă modelul se potrivește cu începutul unui șir; modelul nu
este necesar să se potrivească cu întregul șir. Dacă doriți să faceți asta, trebuie să adăugați un semn dolar la
sfârșitul modelului dvs. Semnul dolarului se va potrivi cu capătul șirului și astfel „întinde” meciul.

226
Machine Translated by Google

Capitolul 10 Bateriile incluse

Funcția re.split împarte un șir în funcție de aparițiile unui model. Aceasta este similară cu metoda împărțirii șirurilor,
cu excepția faptului că permiteți expresii regulate complete în loc de doar un șir de separare fix. De exemplu, cu
metoda string split, puteți împărți un șir după aparițiile șirului ', ' dar cu re. împărțire puteți împărți pede
orice
caractere
secvență
de spațiu și virgule.

>>> some_text = 'alpha, beta,,,,gamma >>> delta'


re.split('[, ]+', some_text) ['alpha', 'beta', 'gamma',
'delta']

Notă Dacă modelul conține paranteze, grupurile între paranteze sunt intercalate între
subșirurile divizate. De exemplu, re.split('o(o)', 'foobar') ar produce ['f', 'o', 'bar'].

După cum puteți vedea din acest exemplu, valoarea returnată este o listă de subșiruri. Argumentul maxsplit indică
numărul maxim de împărțiri permise.

>>> re.split('[, ]+', some_text, maxsplit=2) ['alpha', 'beta',


'gamma delta'] >>> re.split('[, ]+', some_text,
['alfa', 'beta,,,,gamma
maxsplit=1)
delta']

Funcția re.findall returnează o listă cu toate aparițiile modelului dat. De exemplu, pentru a găsi toate cuvintele dintr-un
șir, puteți face următoarele:

>>> pat = '[a-zA-Z]+' >>>


text = '"Hm... Err -- esti sigur?" spuse el, părând nesigur. >>> re.findall(pat, text)

[„Hm”, „Err”, „sunt”, „tu”, „sigur”, „el”, „a spus”, „sună”, „nesigur”]

Sau puteți găsi semnele de punctuație:

>>> pat = r'[.?\-",]+' >>>


re.findall(pat, text) ['"', '...', '--',
'?"', ' ,', '.']

Rețineți că liniuța (-) a fost eliminată, astfel încât Python nu o va interpreta ca parte a unui interval de caractere (cum
ar fi az).
Funcția re.sub este folosită pentru a înlocui aparițiile cele mai din stânga, nesuprapuse, ale unui model cu a
dat înlocuitor. Luați în considerare următorul exemplu:

>>> pat = '{nume}' >>>


text = 'Dragă {nume}...' >>>
re.sub(pat, 'Mr. Gumby', text)
„Stimate domnule Gumby...”

Consultați secțiunea „Numere de grup și funcții în substituții” de mai jos în acest capitol pentru informații despre cum
să utilizați mai eficient această funcție.

227
Machine Translated by Google

Capitolul 10 Bateriile incluse

Funcția re.escape este o funcție de utilitate folosită pentru a evada toate caracterele dintr-un șir care ar putea fi
interpretat ca un operator de expresie regulată. Utilizați acest lucru dacă aveți un șir lung cu multe dintre aceste
caractere speciale și doriți să evitați să tastați multe bare oblice inverse sau dacă obțineți un șir de la un utilizator (de
exemplu, prin funcția de introducere) și doriți să îl utilizați ca un parte a unei expresii regulate. Iată un exemplu despre
cum funcționează:

>>> re.escape('www.python.org') 'www\


\.python\\.org' >>> re.escape('Dar unde
este ambiguitatea?')
„Dar\\ unde\\ este\\ ambiguitatea\\?'

Notă În Tabelul 10-9, ve i observa că unele dintre func ii au un parametru op ional numit flags. Acest parametru
poate fi folosit pentru a modifica modul în care sunt interpretate expresiile regulate. Pentru mai multe informații despre
acest lucru, consultați secțiunea despre modulul re din Python Library Reference.

Potriviți obiecte și grupuri


Funcțiile re care încearcă să potrivească un model cu o secțiune a unui șir returnează obiecte MatchObject atunci când este
găsită o potrivire. Aceste obiecte conțin informații despre subșirul care se potrivește cu modelul. Ele conțin, de asemenea,
informații despre ce părți ale modelului se potrivesc cu ce părți ale subșirului. Aceste părți se numesc grupuri.

Un grup este pur și simplu un submodel care a fost inclus între paranteze. Grupurile sunt numerotate după
paranteza din stânga. Grupul zero este întregul model. Deci, în acest model:

„A existat (a fost un (put) (cooper)) care (locuia în Fyfe)”

grupurile sunt urmatoarele:

0 A fost un tonagar mic care locuia în Fyfe 1 era un


tonagar mic 2 mic

3 Cooper
4 locuiau în Fyfe

În mod obișnuit, grupurile conțin caractere speciale, cum ar fi metacaractere sau operatori de repetiție și, prin urmare, ați
putea fi interesat să știți cu ce se potrivește un anumit grup. De exemplu, în acest model:

r'www\.(.+)\.com$'

grupul 0 ar conține întregul șir, iar grupul 1 ar conține totul între „www.” și „.com”. Creând astfel de modele, puteți extrage
părțile unui șir care vă interesează.
Unele dintre cele mai importante metode de potrivire a obiectelor sunt descrise în Tabelul 10-10.

228
Machine Translated by Google

Capitolul 10 Bateriile incluse

Tabelul 10-10. Câteva metode importante de potrivire a obiectelor

Metodă Descriere

grup([grup1, …]) Preia aparițiile submodelelor (grupurilor) date

început([grup]) Returnează poziția de pornire a apariției unui grup dat

sfârșit([grup]) Returnează poziția finală (o limită exclusivă, ca în felii) a apariției unui grup dat

span([grup]) Returnează pozițiile de început și de sfârșit ale unui grup

Grupul de metode returnează (sub)șirul care a fost potrivit de un grup dat în model. Dacă nu este dat un număr de
grup, se presupune grupul 0. Dacă este dat doar un singur număr de grup (sau utilizați doar valoarea implicită, 0), este
returnat un singur șir. În caz contrar, se returnează un tuplu de șiruri corespunzătoare numerelor de grup date.

Notă În plus față de întregul meci (grupa 0), puteți avea doar 99 de grupuri, cu numere în intervalul 1–99.

Metoda start returnează indexul de pornire al apariției grupului dat (care este implicit 0, întregul model).

Metoda end este similară cu start, dar returnează indexul final plus unu.
Metoda span returnează tuplu (start, end) cu indici de început și de sfârșit ai unui grup dat (care este implicit 0,
întregul model).
Următorul exemplu demonstrează cum funcționează aceste metode:

>>> m = re.match(r'www\.(.*)\..{3}', 'www.python.org') >>> m.group(1)


'python' >>> m .start(1) 4

>>> m.end(1) 10

>>> m.span(1)
(4, 10)

Grupați numere și funcții în substituții


În primul exemplu folosind re.sub, am înlocuit pur și simplu un subșir cu altul - ceva ce aș fi putut face cu ușurință cu
metoda înlocuire șir (descrisă în secțiunea „Metode șiruri” din Capitolul 3). Desigur, expresiile regulate sunt utile
deoarece vă permit să căutați într-un mod mai flexibil, dar vă permit și să efectuați substituții mai puternice.

Cel mai simplu mod de a valorifica puterea re.sub este să folosiți numerele de grup în șirul de substituție.
Orice secvențe de escape de forma „\\n” din șirul de înlocuire sunt înlocuite cu șirul potrivit de grupul n din model. De
exemplu, să presupunem că doriți să înlocuiți cuvintele de forma „*ceva*” cu „<em>ceva</em>”, unde primul este o
modalitate normală de a exprima accentul în documente cu text simplu (cum ar fi e-mailul) , iar acesta din urmă este
codul HTML corespunzător (așa cum este utilizat în paginile web). Să construim mai întâi expresia regulată.

>>> accent_pattern = r'\*([^\*]+)\*'

229
Machine Translated by Google

Capitolul 10 Bateriile incluse

Rețineți că expresiile regulate pot deveni greu de citit, așa că folosirea numelor de variabile semnificative (și,
eventual, un comentariu sau două) este importantă dacă cineva (inclusiv dvs.!) va vedea codul la un moment
dat.

Sfat O modalitate de a face expresiile obișnuite mai lizibile este să utilizați steag -ul VERBOSE în funcțiile re . Acest

lucru vă permite să adăugați spații albe (caractere de spațiu, tab-uri, linii noi și așa mai departe) la modelul dvs., care va fi

ignorat de re—cu excepția cazului în care îl puneți într-o clasă de caractere sau îl scăpați cu o bară oblică inversă. De asemenea,

puteți pune comentarii în astfel de expresii regulate verbose. Următorul este un obiect model care este echivalent cu modelul de

accent, dar care utilizează steag -ul VERBOSE :

>>> emphasis_pattern = re.compile(r''' ... \*


# Începutul etichetei de accentuare -- un asterisc #
... ( ... Începeți grupul pentru capturarea frazei # Capturați
[^\*]+ ... ) orice, cu excepția asteriscurilor # Încheierea grupului
# Începutul etichetei de accentuare „’’, re.VERBOSE)
... \*
...

...

Acum că am modelul meu, pot folosi re.sub pentru a-mi înlocui.

>>> re.sub(accent_model, r'<em>\1</em>', „Bună ziua, *lume*!')


„Bună ziua, <em>lume</em>!”

După cum puteți vedea, am tradus cu succes textul din text simplu în HTML.
Dar vă puteți face înlocuirile și mai puternice folosind o funcție ca înlocuitor. Această funcție va fi
furnizată cu MatchObject ca singur parametru, iar șirul pe care îl returnează va fi folosit ca înlocuitor. Cu
alte cuvinte, puteți face orice doriți subșirului potrivit și puteți face o procesare elaborată pentru a genera
înlocuirea acestuia. Ce folos ai putea avea pentru o asemenea putere, te întrebi? Odată ce începeți să
experimentați cu expresii regulate, veți găsi cu siguranță nenumărate utilizări pentru acest mecanism.
Pentru o aplicație, consultați secțiunea „Un exemplu de sistem de șabloane” puțin mai târziu în capitol.

MODELE LACOMI I NEGREEDY

Operatorii de repetiție sunt în mod implicit lacomi, ceea ce înseamnă că se vor potrivi cât mai mult posibil.
De exemplu, să presupunem că am rescris programul de accent pentru a folosi următorul model:

>>> accent_pattern = r'\*(.+)\*'

Aceasta se potrivește cu un asterisc, urmat de unul sau mai multe caractere și apoi de un alt asterisc. Sună
perfect, nu-i așa? Dar nu este.

>>> re.sub(emphasis_pattern, r'<em>\1</em>', '*Acesta* este *este*!')


'<em>Acesta* este *este</em>!'

230
Machine Translated by Google

Capitolul 10 Bateriile incluse

După cum puteți vedea, modelul se potrivea cu totul, de la primul asterisc la ultimul, inclusiv cele două
asteriscuri dintre! Asta înseamnă să fii lacom: ia tot ce poți.

În acest caz, în mod clar nu doriți acest comportament prea lacom. Soluția prezentată în textul precedent
(folosind un set de caractere care se potrivește cu orice, cu excepția unui asterisc) este bună când știi că o
anumită literă este ilegală. Dar să luăm în considerare un alt scenariu. Ce se întâmplă dacă ai folosi forma
„**ceva**” pentru a semnifica accentul? Acum nu ar trebui să fie o problemă să includeți asteriscuri unice în fraza
accentuată. Dar cum eviți să fii prea lacom?

De fapt, este destul de ușor - folosești doar o versiune nelacomă a operatorului de repetiție. Toți operatorii de
repetiție pot fi făcuți non-lacomi punând un semn de întrebare după ei.

>>> emphasis_pattern = r'\*\*(.+?)\*\*' >>>


re.sub(emphasis_pattern, r'<em>\1</em>', '**Acesta** este **ea**!') „<em>Acesta</
em> este <em>asta</em>!'

Aici am folosit operatorul +? în loc de +, ceea ce înseamnă că modelul se va potrivi cu una sau mai multe
apariții ale wildcardului, ca înainte. Cu toate acestea, se va potrivi cât mai puține, pentru că acum este
nelacom. Deci, se va potrivi doar cu minimul necesar pentru a ajunge la următoarea apariție a lui „\*\*”, care este
sfârșitul modelului. După cum puteți vedea, funcționează frumos.

Găsirea expeditorului unui e-mail


Ați salvat vreodată un e-mail ca fișier text? Dacă ați făcut-o, este posibil să fi văzut că conține o mulțime de text în
esență ilizibil în partea de sus, similar cu cel afișat în Lista 10-9.

Lista 10-9. Un set de anteturi de e-mail (fictive).

De la foo@bar.baz Joi 20 dec 01:22:50 2008 Cale de


întoarcere: <foo@bar.baz> Primit: de la
xyzzy42.bar.com (xyzzy.bar.baz [123.456.789.42]) de frozz.bozz .floop (8.9.3/8.9.3)
cu ID ESMTP BAA25436 pentru <magnus@bozz.floop>; Joi, 20 Dec 2004
01:22:50 +0100 (MET)
Primit: din [43.253.124.23] de bar.baz
(InterMail vM.4.01.03.27 201-229-121-127-20010626) cu ID ESMTP
<20041220002242.ADASD123.bar.baz@[43.253.124.23]>; Joi, 20 Dec 2004 00:22:42 +0000 User-Agent:
Microsoft-Outlook-Express-Macintosh-Edition/5.02.2022 Data: Miercuri, 19 Dec 2008 17:22:42 -0700 Subiect: Re: Spam
De la: Foo Fie <foo@bar.baz> Către: Magnus Lie Hetland <magnus@bozz.floop> CC: <Mr.Gumby@bar.baz> ID-ul
mesajului: <B8467D62.84F%foo@baz.com> În răspuns -Către: <20041219013308.A2655@bozz.floop> Versiune Mime:
1.0 Tip de conținut: text/plain; charset="US-ASCII" Codare de transfer de conținut: 7 biți Stare: RO

Lungimea conținutului: 55
Rânduri: 6

231
Machine Translated by Google

Capitolul 10 Bateriile incluse

Atât de mult și mulțumesc pentru tot spamul!

A ta,
Foo Fie

Să încercăm să aflăm de la cine este acest e-mail. Dacă examinați textul, sunt sigur că vă puteți da seama în
acest caz (mai ales dacă vă uitați la semnătura din partea de jos a mesajului în sine, desigur). Dar poți vedea
un model general? Cum extragi numele expeditorului, fără adresa de e-mail? Sau cum puteți enumera toate
adresele de e-mail menționate în anteturi? Să ne ocupăm mai întâi de sarcina anterioară.
Rândul care conține expeditorul începe cu șirul „De la:” și se termină cu o adresă de e-mail inclusă între
paranteze unghiulare (< și >). Vrei să găsești textul între paranteze. Dacă utilizați modulul de introducere a
fișierelor, aceasta ar trebui să fie o sarcină ușoară. Un program care rezolvă problema este prezentat în Lista 10-10.

Notă Puteți rezolva această problemă fără a utiliza expresii regulate dacă doriți. De asemenea, puteți utiliza
modulul de e -mail.

Lista 10-10. Un program pentru găsirea expeditorului unui e-mail

# find_sender.py
import fileinput, re pat
= re.compile('From: (.*) <.*?>$') for line in
fileinput.input(): m = pat.match(line) if m:
print (grup m.(1))

Apoi puteți rula programul astfel (presupunând că mesajul de e-mail este în fișierul text message.eml):

$ python find_sender.py message.eml Foo


Fie

Ar trebui să rețineți următoarele despre acest program:


• Compilez expresia regulată pentru a face procesarea mai eficientă.
• Includ submodelul pe care vreau să-l extrag în paranteze, făcându-l un grup.
• Folosesc un model non-lacom, astfel încât adresa de e-mail să se potrivească doar cu ultima pereche de
paranteze unghiulare (doar în cazul în care numele conține unele paranteze).

• Folosesc semnul dolarului pentru a indica faptul că vreau ca modelul să se potrivească cu întreaga
linie, până la sfârșit.

• Folosesc o declarație if pentru a mă asigura că de fapt am potrivit ceva înainte de a


încerca să extrag potrivirea unui anumit grup.

Pentru a enumera toate adresele de e-mail menționate în anteturi, trebuie să construiți o expresie regulată care
să se potrivească cu o adresă de e-mail, dar nimic altceva. Puteți utiliza apoi metoda findall pentru a găsi toate aparițiile
din fiecare linie. Pentru a evita duplicarea, păstrați adresele într-un set (descris mai devreme în acest capitol). În cele din
urmă, extrageți cheile, le sortați și le imprimați.

232
Machine Translated by Google

Capitolul 10 Bateriile incluse

import fileinput, re pat =


re.compile(r'[az\-\.]+@[az\-\.]+', re.IGNORECASE) adrese = set()

pentru linia în fileinput.input(): pentru


adresa în pat.findall(linia):
adrese.add(adresa) pentru adresa
în sortat(adresele): adresa tipărită

Rezultatul rezultat la rularea acestui program (cu mesajul de e-mail din Lista 10-9 ca intrare) este după cum
urmează:

Mr.Gumby@bar.baz
foo@bar.baz
foo@baz.com
magnus@bozz.floop

Rețineți că, atunci când sortați, literele mari sunt înaintea literelor mici.

Rețineți că nu am respectat strict specificația problemei de aici. Problema a fost găsirea adreselor în
antet, dar în acest caz programul găsește toate adresele din întregul fișier. Pentru a evita acest lucru, puteți
apela fileinput.close() dacă găsiți o linie goală, deoarece antetul nu poate conține linii goale. Alternativ, puteți
utiliza fileinput.nextfile() pentru a începe procesarea următorului fișier, dacă există mai multe.

Un exemplu de sistem de șabloane

Un șablon este un fișier în care puteți pune anumite valori pentru a obține un text finit de un fel. De exemplu, este
posibil să aveți un șablon de e-mail care necesită doar inserarea unui nume de destinatar. Python are deja un
mecanism avansat de șablon: formatarea șirurilor. Cu toate acestea, cu expresii regulate, puteți face sistemul și mai
avansat. Să presupunem că doriți să înlocuiți toate aparițiile lui „[ceva]” („câmpurile”) cu rezultatul evaluării a ceva
ca expresie în Python. Astfel, acest șir:

„Suma dintre 7 și 9 este [7 + 9].'

ar trebui tradus astfel:

„Suma dintre 7 și 9 este 16”.

De asemenea, doriți să puteți efectua sarcini în aceste câmpuri, astfel încât acest șir:

„[name="Mr. Gumby"]Bună ziua, [nume]”

ar trebui tradus astfel:

„Bună ziua, domnule Gumby”

233
Machine Translated by Google

Capitolul 10 Bateriile incluse

Poate părea o sarcină complexă, dar haideți să revizuim instrumentele disponibile.

•Puteți folosi o expresie regulată pentru a potrivi câmpurile și a extrage conținutul acestora.

•Puteți evalua șirurile de expresie cu eval, furnizând dicționarul


care conțin domeniul de aplicare. Faceți acest lucru într-o declarație try/except. Dacă
apare o SyntaxError, probabil că aveți o instrucțiune (cum ar fi o atribuire) pe mâini și ar
trebui să utilizați exec în schimb.

•Puteți executa șirurile de atribuire (și alte instrucțiuni) cu exec, stocând fișierul
domeniul de aplicare al șablonului într-un dicționar.

•Puteți folosi re.sub pentru a înlocui rezultatul evaluării în șirul care este procesat. Dintr-
o dată, nu pare atât de intimidant, nu-i așa?

Sfat Dacă o sarcină pare descurajantă, aproape întotdeauna ajută să o descompuneți în bucăți mai mici. De asemenea, faceți un bilanț al

instrumentelor pe care le aveți la dispoziție pentru idei despre cum să vă rezolvați problema.

Consultați Lista 10-11 pentru un exemplu de implementare.

Lista 10-11. Un sistem de șabloane

# templates.py

import fileinput, re

# Se potrivește câmpurile cuprinse între paranteze


drepte: field_pat = re.compile(r'\[(.+?)\]')

# Vom colecta variabile în aceasta: scope


= {}

# Acesta este folosit în re.sub:


def replacement(match):
code = match.group(1)
try: # Dacă câmpul poate
fi evaluat, returnați-l: return str(eval(code, scope)) cu
excepția SyntaxError: # În caz contrar, executați
atribuirea în același domeniu ... exec code în scope și
returnează un șir gol: # return
...
''

# Obțineți tot textul ca un singur șir:

# (Există și alte moduri de a face acest lucru; vezi capitolul 11) lines
= [] for line in fileinput.input(): lines.append(line) text = ''.join(lines)

# Înlocuiți toate aparițiile modelului câmpului:


print(field_pat.sub(înlocuire, text))

234
Machine Translated by Google

Capitolul 10 Bateriile incluse

Mai simplu spus, acest program face următoarele:

•Definește un model pentru câmpurile de potrivire.

•Creează un dicționar care să acționeze ca scop pentru șablon.

•Definește o funcție de înlocuire care face următoarele:

•Ia grupul 1 din meci și îl pune în cod.

•Încearcă să evalueze codul cu dicționarul de domeniu ca spațiu de nume, convertește


rezultat într-un șir și îl returnează. Dacă acest lucru reușește, câmpul a fost o expresie și
totul este în regulă. În caz contrar (adică, apare o SyntaxError), treceți la pasul următor.

•Execută câmpul în același spațiu de nume (dicționarul de domeniu) folosit


pentru evaluarea expresiilor și apoi returnează un șir gol (deoarece atribuirea nu
evaluează nimic).

•Folosește introducerea fișierului pentru a citi toate liniile disponibile, a le pune într-o listă și a le uni
un șir mare.

•Înlocuiește toate aparițiile field_pat folosind funcția de înlocuire din re.sub și tipărește rezultatul.

Notă În versiunile anterioare de Python, era mult mai eficient să puneți liniile într-o listă și apoi să le uniți la
sfârșit decât să faceți ceva de genul acesta:
''
text =

pentru linia din fileinput.input(): text +=


line

Deși acest lucru pare elegant, fiecare sarcină trebuie să creeze un șir nou, care este șirul vechi cu cel nou atașat, ceea
ce poate duce la o risipă de resurse și poate face programul să încetinească. În versiunile mai vechi de Python, diferența
dintre aceasta și utilizarea join poate fi uriașă. În versiunile mai recente, utilizarea operatorului += poate fi, de fapt, mai
rapidă. Dacă performanța este importantă pentru dvs., puteți încerca ambele soluții. Și dacă doriți un mod mai elegant
de a citi în tot textul unui fișier, aruncați o privire la capitolul 11.

Așadar, tocmai am creat un sistem de șabloane cu adevărat puternic în doar 15 linii de cod (fără a număra spațiile și
comentariile). Sper că începi să vezi cât de puternic devine Python atunci când folosești bibliotecile standard. Să încheiem
acest exemplu testând sistemul de șabloane. Încercați să îl rulați pe fișierul simplu afișat în Lista 10-12.

Lista 10-12. Un exemplu de șablon simplu

[x = 2] [y
= 3]
Suma lui [x] și [y] este [x + y].

Ar trebui să vezi asta:

Suma 2 și 3 este 5.

235
Machine Translated by Google

Capitolul 10 Bateriile incluse

Dar stai, devine mai bine! Deoarece am folosit introducerea fișierelor, pot procesa mai multe fișiere pe rând. Asta
înseamnă că pot folosi un fișier pentru a defini valorile pentru unele variabile și apoi un alt fișier ca șablon în care sunt
inserate aceste valori. De exemplu, aș putea avea un fișier cu definiții ca în Lista 10-13, numit magnus.txt, și un fișier
șablon ca în Lista 10-14, numit template.txt.

Lista 10-13. Câteva definiții de șablon

[name = 'Magnus Lie Hetland' ] [email =


'magnus@foo.bar' ] [language = 'python' ]

Lista 10-14. Un șablon

[timp de import]
Dragă [nume],

As dori sa invat sa programez. Am auzit că folosești foarte mult


limba [limbă] -- ar trebui să iau în considerare acest lucru?

Și, apropo, [email] este adresa ta de e-mail corectă?

Fooville, [time.asctime()]

Oscar Frozzbozz

Instrucțiunea de timp de import nu este o atribuire (care este tipul de instrucțiune pe care mi-am propus să mă
ocup), dar pentru că nu sunt pretențios și folosesc doar o instrucțiune simplă try/except, programul meu acceptă
orice instrucțiune sau expresie care funcționează cu eval sau exec. Puteți rula programul astfel (presupunând o linie de
comandă UNIX):

$ python templates.py magnus.txt template.txt

Ar trebui să obțineți o ieșire similară cu următoarea:

Dragă Magnus Lie Hetland,

As dori sa invat sa programez. Am auzit că folosești foarte mult limbajul python -- ar trebui să iau în considerare acest
lucru?

Și, apropo, magnus@foo.bar este adresa ta de e-mail corectă?

Fooville, Luni 18 Iul 15:24:10 2016

Oscar Frozzbozz

Chiar dacă acest sistem de șablon este capabil de unele substituții destul de puternice, are totuși unele defecte. De
exemplu, ar fi bine dacă ați putea scrie fișierul de definiție într-un mod mai flexibil. Dacă ar fi executat cu execfile, ați
putea utiliza pur și simplu sintaxa Python normală. Acest lucru ar rezolva și problema de a obține toate acele linii goale în
partea de sus a ieșirii.
Vă puteți gândi la alte modalități de îmbunătățire a programului? Vă puteți gândi la alte utilizări pentru conceptele folosite
în acest program? Cel mai bun mod de a deveni cu adevărat competenți în orice limbaj de programare este să te joci cu el — să-i
testezi limitele și să-i descoperi punctele forte. Vedeți dacă puteți rescrie acest program, astfel încât să funcționeze mai bine și să se
potrivească nevoilor dvs.

236
Machine Translated by Google

Capitolul 10 Bateriile incluse

Alte module standard interesante


Chiar dacă acest capitol a acoperit o mulțime de materiale, abia am zgâriat suprafața bibliotecilor standard. Pentru a vă
tenta să vă scufundați, voi menționa rapid câteva biblioteci interesante.

argparse: În UNIX, programele de linie de comandă sunt adesea executate cu diferite opțiuni
sau comutatoare. (Interpretul Python este un exemplu tipic.) Toate acestea vor fi găsite în
sys.argv, dar gestionarea corectă a acestora este departe de a fi ușor. Modulul argparse
facilitează furnizarea unei interfețe de linie de comandă cu drepturi depline.

cmd: Acest modul vă permite să scrieți un interpret de linie de comandă, oarecum ca


interpretul interactiv Python. Puteți defini propriile comenzi pe care utilizatorul le poate
executa la prompt. Poate ați putea folosi acest lucru ca interfață cu utilizatorul pentru unul
dintre programele dvs.?

csv: CSV este prescurtare pentru valori separate prin virgulă, un format simplu utilizat de
multe aplicații (de exemplu, multe foi de calcul și programe de baze de date) pentru a stoca
date tabulare. Este folosit în principal la schimbul de date între diferite programe. Modulul
csv vă permite să citiți și să scrieți cu ușurință fișiere CSV și gestionează unele dintre părțile mai
complicate ale formatului destul de transparent.

datetime: dacă modulul de timp nu este suficient pentru nevoile dvs. de urmărire a timpului,
este foarte posibil ca datatime să fie. Are suport pentru obiecte speciale de dată și oră și vă
permite să le construiți și să le combinați în diferite moduri. Interfața este în multe privințe
puțin mai intuitivă decât cea a modulului de timp.

difflib: Această bibliotecă vă permite să calculați cât de similare sunt două secvențe. De
asemenea, vă permite să găsiți secvențele (dintr-o listă de posibilități) care sunt „cel mai
asemănătoare” cu o secvență originală pe care o furnizați. difflib ar putea fi folosit pentru a
crea un program simplu de căutare, de exemplu.

enum: un tip de enumerare este un tip cu un număr fix, mic de valori posibile. Multe
limbi au astfel de tipuri încorporate, dar dacă aveți nevoie de una în Python, modulul enum
este prietenul dumneavoastră.

functools: Aici, puteți găsi funcționalitate care vă permite să utilizați o funcție cu doar unii
dintre parametrii săi (evaluare parțială), completându-i pe cei rămași ulterior. În Python 3.0,
aici veți găsi filter și reduce.hashlib. Cu acest modul, puteți calcula mici „semnături” (numere)
din șiruri. Și dacă calculați semnăturile pentru două șiruri diferite, puteți fi aproape sigur că
cele două semnături vor fi diferite. Puteți utiliza acest lucru pentru fișiere text mari. Aceste
module au mai multe utilizări în criptografie și securitate.3

itertools: aici aveți o mulțime de instrumente pentru crearea și combinarea iteratoarelor (sau
a altor obiecte iterabile). Există funcții pentru înlănțuirea iterabilelor, pentru crearea
iteratoarelor care returnează pentru totdeauna numere întregi consecutive (similar cu
intervalul, dar fără o limită superioară), pentru a parcurge un iterabil în mod repetat și alte lucruri utile.

3 Vezi și modulele md5 și sha .

237
Machine Translated by Google

Capitolul 10 Bateriile incluse

logare: pur și simplu folosirea instrucțiunilor print pentru a afla ce se întâmplă în


programul dvs. poate fi utilă. Dacă doriți să urmăriți lucrurile chiar și fără a avea o mulțime
de rezultate de depanare, puteți scrie aceste informații într-un fișier jurnal. Acest modul
vă oferă un set standard de instrumente pentru gestionarea unuia sau mai multor jurnalele
centrale, cu mai multe niveluri de prioritate pentru mesajele dvs. de jurnal, printre altele.

statistici: Calcularea mediei unui set de numere nu este chiar atât de dificil, dar
obținerea corectă a mediei, chiar și pentru un număr pare de elemente, și pentru a
implementa diferențele dintre populație și abaterile standard ale eșantionului, de exemplu,
necesită o putin mai multa grija. În loc să faci asta singur, folosește modulul de statistici!

timeit, profil și urmărire: modulul timeit (cu scriptul de linie de comandă care îl
însoțește) este un instrument pentru măsurarea timpului de rulare al unei bucăți de cod.
Are câteva trucuri în mânecă și probabil că ar trebui să îl utilizați mai degrabă decât modulul
de timp pentru măsurarea performanței. Modulul de profil (împreună cu modulul său
însoțitor, pstats) poate fi folosit pentru o analiză mai cuprinzătoare a eficienței unei bucăți
de cod. Modulul de urmărire (și programul) vă poate oferi o analiză de acoperire (adică ce
părți ale codului sunt executate și care nu). Acest lucru poate fi util atunci când scrieți codul de
testare, de exemplu.

Un rezumat rapid
În acest capitol, ați învățat despre module: cum să le creați, cum să le explorați și cum să utilizați unele dintre cele incluse în
bibliotecile standard Python.

Module: Un modul este practic un subprogram a cărui funcție principală este de a defini
lucruri, cum ar fi funcții, clase și variabile. Dacă un modul conține vreun cod de testare,
acesta ar trebui plasat într-o instrucțiune if care verifică dacă name == '__ main__'. Modulele
pot fi importate dacă sunt în PYTHONPATH. Importați un modul stocat în fișierul foo.py cu
instrucțiunea import foo.

Pachete: un pachet este doar un modul care conține alte module. Pachetele sunt implementate
ca directoare care conțin un fișier numit __init__.py.

Explorarea modulelor: după ce ați importat un modul în interpretul interactiv, îl puteți


explora în mai multe moduri. Printre acestea se numără utilizarea dir, examinarea
variabilei __all__ și utilizarea funcției de ajutor. Documentația și codul sursă pot fi, de
asemenea, surse excelente de informații și perspectivă.

Biblioteca standard: Python vine cu mai multe module incluse, numite colectiv biblioteca
standard. Unele dintre acestea au fost analizate în acest capitol:

•sys: Un modul care vă oferă acces la mai multe variabile și funcții care sunt strâns legate
cu interpretul Python.

•os: un modul care vă oferă acces la mai multe variabile și funcții care sunt strâns legate
de sistemul de operare.

•fileinput: un modul care facilitează repetarea pe liniile mai multor fișiere sau fluxuri.

•seturi, heapq și deque: trei module care oferă trei structuri de date utile. Seturile
sunt disponibile și sub forma unui set de tip încorporat.

238
Machine Translated by Google

Capitolul 10 Bateriile incluse

•time: Un modul pentru obținerea orei curente și pentru manipularea și formatarea


orelor și datelor.

•random: Un modul cu funcții pentru generarea de numere aleatoare, alegerea elementelor


aleatorii dintr-o secvență și amestecarea elementelor unei liste.

•shelve: Un modul pentru crearea unei mapări persistente, care o stochează


conținutul unei baze de date cu un nume de fișier dat.

•re: Un modul cu suport pentru expresii regulate.

Dacă sunteți curios să aflați mai multe despre module, vă îndemn din nou să răsfoiți Bibliotecii de referință Python.
Este cu adevărat o lectură interesantă.

Funcții noi în acest capitol


Func ie Descriere

dir(obj) Returnează o listă alfabetică a numelor de atribute

ajutor([obj]) Oferă ajutor interactiv sau ajutor pentru un anumit obiect

imp.reload(modul) Returnează o versiune reîncărcată a unui modul care a fost deja importat

Ce acum?
Dacă ați înțeles cel puțin câteva dintre conceptele din acest capitol, priceperea dvs. Python a făcut probabil un mare salt
înainte. Cu bibliotecile standard la îndemână, Python trece de la puternic la extrem de puternic. Cu ceea ce ați învățat până acum,
puteți scrie programe pentru a rezolva o gamă largă de probleme. În capitolul următor, veți afla mai multe despre utilizarea
Python pentru a interacționa cu lumea exterioară a fișierelor și a rețelelor și, prin urmare, să abordați probleme de o amploare
mai mare.

239
Machine Translated by Google

CAPITOLUL 11

Fișiere și chestii

Până acum, am lucrat în principal cu structuri de date care se află în interpretul însuși. Puțină interacțiune pe care
programele noastre le-au avut cu lumea exterioară a fost prin introducere și tipărire. În acest capitol, facem un pas
mai departe și lăsăm programele noastre să arunce o privire asupra unei lumi mai mari: lumea fișierelor și a fluxurilor.
Funcțiile și obiectele descrise în acest capitol vă vor permite să stocați date între invocări de programe și să procesați date
din alte programe.

Deschiderea fișierelor

Puteți deschide fișiere cu funcția de deschidere, care se află în modulul io, dar este importată automat pentru dvs. Acesta
ia un nume de fișier ca singur argument obligatoriu și returnează un obiect fișier. Presupunând că aveți un fișier text (creat
cu editorul dvs. de text, poate) numit somefile.txt stocat în directorul curent, îl puteți deschide astfel:

>>> f = open('somefile.txt')

De asemenea, puteți specifica calea completă către fișier, dacă acesta se află în altă parte. Dacă nu există, totuși, veți vedea
o urmărire a excepției ca aceasta:

Traceback (cel mai recent apel ultimul):


Fișierul „<stdin>”, linia 1, în <modul>
FileNotFoundError: [Errno 2] Nu există un astfel de fișier sau director: „somefile.txt”

Dacă doriți să creați fișierul scriind text în el, acest lucru nu este complet satisfăcător. Soluția se găsește în al doilea argument
de deschidere.

Moduri de fișiere

Dacă utilizați open doar cu un nume de fișier ca parametru, obțineți un obiect fișier din care puteți citi. Dacă doriți să scrieți
în fișier, trebuie să precizați acest lucru în mod explicit, furnizând un mod. Argumentul mode pentru funcția deschisă poate
avea mai multe valori, așa cum este rezumat în Tabelul 11-1.

© Magnus Lie Hetland 2017 241


ML Hetland, Beginning Python, DOI 10.1007/978-1-4842-0028-5_11
Machine Translated by Google

Capitolul 11 Fi iere i chestii

Tabelul 11-1. Cele mai comune valori pentru argumentul de mod al funcției deschise

Valoare Descriere
'r' Modul de citire (implicit)
'w' Modul de scriere

'X' Mod de scriere exclusiv

'A' Modul Adăugare


'b' Modul binar (adăugat la alt mod)
't' Modul text (implicit, adăugat la alt mod)

'+' Modul citire/scriere (adăugat la alt mod)

Specificarea explicită a modului de citire are același efect ca și a nu furniza deloc un șir de mod. Modul de scriere vă
permite să scrieți în fișier și va crea fișierul dacă acesta nu există. Modul exclusiv de scriere merge mai departe și
generează o FileExistsError dacă fișierul există deja. Dacă deschideți un fișier existent în modul de scriere, conținutul
existent va fi șters sau trunchiat, iar scrierea începe din nou de la începutul fișierului; dacă preferați să continuați să scrieți la
sfârșitul fișierului existent, utilizați modul adăugare.
„+” poate fi adăugat la oricare dintre celelalte moduri pentru a indica faptul că atât citirea, cât și scrierea sunt permise.
Deci, de exemplu, „r+” poate fi folosit la deschiderea unui fișier text pentru citire și scriere. (Pentru ca acest lucru să fie util,
probabil că veți dori să utilizați și căutare; vedeți bara laterală „Acces aleatoriu” mai târziu în acest capitol.) Rețineți că există
o diferență importantă între „r+” și „w+”: acesta din urmă se va trunchia dosarul, în timp ce primul nu va.
Modul implicit este „rt”, ceea ce înseamnă că fișierul este tratat ca text Unicode codificat. Decodarea și codificarea
sunt apoi efectuate automat, cu UTF-8 ca codificare implicită. Alte codificări și strategii Unicode de gestionare a erorilor
pot fi setate folosind argumentele cuvintelor cheie codificare și erori. (Vezi capitolul 1 pentru mai multe despre
Unicode.) Există, de asemenea, o traducere automată a caracterelor newline. În mod implicit, liniile se termină cu „\n”. Alte
terminații de rând („\r” sau „\r\n”) sunt înlocuite automat la citire. La scriere, „\n” este înlocuit cu sfârșitul de linie implicit al
sistemului (os.linesep).
În mod normal, Python folosește ceea ce se numește modul de linie nouă universală, unde orice linie nouă validă
('\n', '\r' sau '\r\n') este recunoscută, de exemplu, prin metoda readlines, discutată mai târziu. Dacă doriți să păstrați acest
mod, dar doriți să împiedicați traducerea automată către și de la „\n”, puteți furniza un șir gol argumentului cuvânt cheie
newline, ca în open(name, newline=''). Dacă doriți să specificați că numai „\r” sau „\r\n” trebuie tratat ca o sfârșit de linie
validă, furnizați în schimb sfârșitul de linie preferat. În acest caz, sfârșitul de linie nu este tradus la citire, dar „\n” va fi
înlocuit cu sfârșitul de linie adecvat la scriere.
Dacă fișierul dvs. conține date binare nontextuale, cum ar fi un clip audio sau o imagine, cu siguranță nu ați dori ca
niciuna dintre aceste transformări automate să fie efectuată. În acest caz, pur și simplu utilizați modul binar (de exemplu,
„rb”) pentru a dezactiva orice funcționalitate specifică textului.
Există și alte câteva argumente opționale mai puțin avansate, de asemenea, pentru a controla tamponarea și a
lucra mai direct cu descriptorii de fișiere. Consultați documentația Python sau rulați ajutor (deschidere) în interpretul
interactiv, pentru a afla mai multe.

Metodele de bază ale fișierului


Acum știi cum să deschizi fișierele. Următorul pas este să faci ceva util cu ei. În această secțiune, învățați despre câteva
metode de bază ale obiectelor fișier și despre alte obiecte asemănătoare fișierelor , uneori numite fluxuri. Un obiect
asemănător fișierului este pur și simplu unul care acceptă câteva dintre aceleași metode ca un fișier, în special fie de citire,
fie de scriere sau ambele. Obiectele returnate de urlopen (vezi capitolul 14) sunt un bun exemplu în acest sens. Aceștia
acceptă metode precum read și readline, dar nu metode precum write și isatty, de exemplu.

242
Machine Translated by Google

Capitolul 11 Fi iere i chestii

TREI FLUIRURI STANDARD

În capitolul 10, în secțiunea despre modulul sys , am menționat trei fluxuri standard. Acestea sunt obiecte
asemănătoare fișierelor și le puteți aplica majoritatea a ceea ce ați învățat despre fișiere.

O sursă standard de intrare a datelor este sys.stdin. Când un program citește de la intrarea standard, puteți
furniza text tastându-l sau îl puteți conecta cu ieșirea standard a altui program, folosind o conductă, așa cum este
demonstrat în secțiunea „Ieșire conductă”.

Textul pe care îl dați pentru a tipări apare în sys.stdout. Solicitările pentru introducere merg, de asemenea, acolo.
Datele scrise în sys.stdout apar de obicei pe ecran, dar pot fi redirecționate către intrarea standard a unui alt
program cu o conductă, așa cum sa menționat.

Mesajele de eroare (cum ar fi urmele stivei) sunt scrise în sys.stderr, care este similar cu sys.stdout , dar poate fi
redirecționat separat.

Citire si scriere
Cele mai importante capabilități ale fișierelor sunt furnizarea și primirea de date. Dacă aveți un obiect asemănător
fișierului numit f, puteți scrie date cu f.write și puteți citi date cu f.read. Ca și în cazul majorității funcționalităților Python,
există o oarecare flexibilitate în ceea ce utilizați ca date, dar clasele de bază utilizate sunt str și bytes, pentru modul text
și, respectiv, binar.
De fiecare dată când apelați f.write(șir), șirul pe care îl furnizați este scris în fișier după cele pe care le-ați scris
anterior.

>>> f = open('somefile.txt', 'w') >>>


f.write('Bună ziua, ') 7

>>> f.write('Lumea!') 6

>>> f.close()

Observați că apelez metoda de închidere când am terminat cu fișierul. Veți afla mai multe despre el în secțiune
„Închiderea fișierelor” mai târziu în acest capitol. Cititul este la fel de simplu. Nu uitați să spuneți fluxului câți
caractere (sau octeți, în modul binar) doriți să citiți. Iată un exemplu (continuând de unde am rămas):

>>> f = open('somefile.txt', 'r') >>> f.read(4)

'Iad'
>>> f.read() 'o,
lume!'

Mai întâi precizez câte caractere să citesc (4), apoi citesc pur și simplu restul fișierului (fără a furniza un număr). Rețineți
că aș fi putut renunța la specificația modului de la apel pentru deschidere, deoarece „r” este implicit.

243
Machine Translated by Google

Capitolul 11 Fi iere i chestii

Ieșire de conducte
Într-un shell precum bash, puteți scrie mai multe comenzi una după alta, legate între ele cu conducte, ca în acest exemplu:

$ cat somefile.txt | python somescript.py | fel

Această conductă constă din trei comenzi.

•cat somefile.txt: Această comandă scrie pur și simplu conținutul fișierului somefile.txt la
ieșirea standard (sys.stdout).

•python somescript.py: Această comandă execută scriptul Python somescript. Se


presupune că scriptul citește din intrarea sa standard și scrie rezultatul în ieșirea
standard.

•sort: Această comandă citește tot textul de la intrarea standard (sys.stdin), sortează
liniile alfabetic și scrie rezultatul în ieșirea standard.

Dar ce rost au aceste caractere pipe (|) și ce face somescript.py? Țevile conectează ieșirea standard a
unei comenzi cu intrarea standard a următoarei. Deștept, nu? Deci, puteți ghici în siguranță că
somescript.py citește date din sys.stdin (care este ceea ce scrie cat somefile.txt) și scrie un rezultat în
sys.stdout (care este locul în care sort își primește datele).
Un script simplu (somescript.py) care utilizează sys.stdin este prezentat în Lista 11-1. Conținutul
fișierului somefile.txt este afișat în Lista 11-2.

Lista 11-1. Script simplu care numără cuvintele din sys.stdin

# somescript.py
import sys text =
sys.stdin.read() words =
text.split() wordcount =
len(words) print('Wordcount:',
wordcount)

Lista 11-2. Un fișier care conține un text fără sens

Mama ta era un hamster, iar tatăl tău


mirosea a fructe de soc.

Iată rezultatele pentru cat somefile.txt | python somescript.py:

Număr de cuvinte: 11

244
Machine Translated by Google

Capitolul 11 Fi iere i chestii

ACCES ALEATORII

În acest capitol, tratez fișierele doar ca fluxuri — puteți citi datele numai de la început până la sfârșit, strict în ordine.
De fapt, puteți, de asemenea, să vă deplasați într-un fișier, accesând doar părțile care vă interesează (numit acces
aleatoriu) utilizând cele două metode fișier-obiect seek and tell.

Metoda seek(offset[, wherece]) mută poziția curentă (unde se efectuează citirea sau scrierea) în poziția
descrisă de offset și unde. offset -ul este un număr de octeți (caractere). unde implicit este io.SEEK_SET sau
0, ceea ce înseamnă că offset-ul este de la începutul fișierului (offset-ul trebuie să fie nenegativ). unde poate
fi, de asemenea, setat la io.SEEK_CUR sau 1 (deplasare relativ la poziția curentă; offset-ul poate fi negativ) sau
io.SEEK_END sau 2 (mutare relativ la sfârșitul fișierului). Luați în considerare acest exemplu:

>>> f = deschis(r'C:\text\somefile.txt', 'w') >>>


f.write('01234567890123456789')
20
>>> f.seek(5) 5

>>> f.write('Bună, lume!') 13

>>> f.close() >>>


f = open(r'C:\text\somefile.txt') >>> f.read()

'01234Bună ziua, lume!89'

Metoda tell() returnează poziția curentă a fișierului, ca în exemplul următor:

>>> f = open(r'C:\text\somefile.txt') >>> f.read(3)


'012'

>>> f.read(2)
'34' >>> f.tell() 5

Rânduri de citire și scriere


De fapt, ceea ce am făcut până acum este puțin practic. Aș putea la fel de bine să citesc în rândurile unui flux ca
și să citesc scrisoare cu scrisoare. Puteți citi o singură linie (text de unde ați ajuns până acum, până la primul
separator de rând pe care îl întâlniți inclusiv) cu metoda readline. Puteți utiliza această metodă fie fără niciun
argument (caz în care o linie este pur și simplu citită și returnată) sau cu un întreg nenegativ, care este atunci
numărul maxim de caractere pe care readline este permis să le citească. Deci, dacă some_file. readline() returnează
„Hello, World!\n”, apoi some_file.readline(5) returnează „Hello”. Pentru a citi toate liniile unui fișier și a le returna ca o
listă, utilizați metoda readlines.
Metoda writelines este opusul readlines: dați-i o listă (sau, de fapt, orice secvență sau iterabil
obiect) de șiruri și scrie toate șirurile în fișier (sau flux). Rețineți că liniile noi nu sunt adăugate; trebuie să le
adăugați singur. De asemenea, nu există o metodă de scriere, deoarece puteți utiliza doar scrierea.

245
Machine Translated by Google

Capitolul 11 Fi iere i chestii

Închiderea fișierelor

Ar trebui să vă amintiți să vă închideți fișierele apelând metoda lor de închidere. De obicei, un obiect fișier este
închis automat atunci când părăsiți programul (și posibil înainte de asta), și nu închiderea fișierelor din care ați citit nu
este chiar atât de important. Cu toate acestea, închiderea acelor fișiere nu poate răni și ar putea ajuta la evitarea
menținerii fișierului „blocat” inutil împotriva modificărilor în unele sisteme de operare și setări. De asemenea, evită
utilizarea oricăror cote pentru fișierele deschise pe care le-ar putea avea sistemul tău.
Ar trebui să închideți întotdeauna un fișier în care ați scris , deoarece Python poate salva (păstra temporar undeva,
din motive de eficiență) datele pe care le-ați scris și, dacă programul dvs. se blochează dintr-un motiv oarecare, este posibil
ca datele să nu fie scrise deloc în fișier. . Lucrul sigur este să vă închideți fișierele după ce ați terminat cu ele. Dacă doriți
să resetați memoria tampon și să faceți modificările vizibile în fișierul propriu-zis de pe disc, dar încă nu doriți să închideți
fișierul, puteți utiliza metoda de golire. Rețineți, totuși, că spălarea ar putea să nu permită altor programe care rulează în
același timp să acceseze fișierul din cauza unor considerente de blocare care depind de sistemul de operare și de setări. Ori
de câte ori puteți închide fișierul, este de preferat.
Dacă doriți să fiți sigur că fișierul dvs. este închis, puteți utiliza o declarație try/finally cu apelul la
închide în clauza finally.

# Deschideți fișierul aici


încercați: # Scrieți datele în
fișierul dvs. în sfârșit: file.close()

Există, de fapt, o declarație concepută special pentru acest tip de situație - declarația cu.

cu open("somefile.txt") ca somefile:
do_something(somefile)

Instrucțiunea with vă permite să deschideți un fișier și să-l atribuiți unui nume de variabilă (în acest caz, somefile).
Apoi scrieți date în fișierul dvs. (și, poate, faceți alte lucruri) în corpul declarației, iar fișierul este închis automat când
se ajunge la sfârșitul declarației, chiar dacă aceasta este cauzată de o excepție.

MANAGERI DE CONTEXT

Declarația with este de fapt o construcție destul de generală, care vă permite să utilizați așa-numiții
manageri de context. Un manager de context este un obiect care acceptă două metode: __enter__ și __exit__.

Metoda __enter__ nu acceptă argumente. Este apelată la introducerea instrucțiunii with , iar valoarea returnată
este legată de variabila după cuvântul cheie as .

Metoda __exit__ ia trei argumente: un tip de excepție, un obiect excepție și o urmărire a excepției. Se apelează la
părăsirea metodei (cu orice excepție ridicată furnizată prin parametri). Dacă __exit__ returnează false, orice excepție
este suprimată.

Fișierele pot fi folosite ca manageri de context. Metodele lor __enter__ returnează ele însele obiectele fișier, în timp
ce metodele lor __exit__ închid fișierele. Pentru mai multe informații despre această funcție puternică, dar destul
de avansată, consultați descrierea managerilor de context din Manualul de referință Python. Consultați, de asemenea,
secțiunile despre tipurile de manager de context și despre contextlib din Bibliotecii de referință Python.

246

S-ar putea să vă placă și