apeescape2.com
  • Glavni
  • Spletno Mesto
  • Vodenje Projektov
  • Trendi
  • Ux Oblikovanje
Tehnologija

Koda Buggy Python: 10 najpogostejših napak, ki jih naredijo razvijalci Pythona

O Pythonu

Python je interpretiran objektno usmerjen programski jezik na visoki ravni z dinamično semantiko. Zaradi vgrajenih podatkovnih struktur na visoki ravni v kombinaciji z dinamičnim tipkanjem in dinamičnim povezovanjem je zelo privlačna za Hiter razvoj aplikacij , kot tudi za uporabo kot skriptni ali lepilni jezik za povezovanje obstoječih komponent ali storitev. Python podpira module in pakete, s čimer spodbuja modularnost programa in ponovno uporabo kode.

O tem članku

Pythonova enostavna, enostavna sintaksa lahko zavede Razvijalci Pythona - še posebej tisti, ki so v jeziku novejši - ker so zamudili nekatere njegove tankočutnosti in podcenili moč jezika raznolik jezik Python .



S tem v mislih ta članek predstavlja seznam 10 najbolj subtilnih, težje ulovljivih napak, ki lahko ugriznejo še nekaj več napredni razvijalci Pythona zadaj.



(Opomba: Ta članek je namenjen naprednejšemu občinstvu kot Pogoste napake programerjev Python , ki je bolj namenjena tistim, ki so novejši od jezika.)

Pogosta napaka # 1: Zloraba izrazov kot privzetih vrednosti za argumente funkcije

Python vam omogoča, da določite, da je argument funkcije neobvezno z zagotavljanjem a privzeta vrednost za to. Čeprav je to odlična lastnost jezika, lahko privzeta vrednost povzroči nekaj zmede spremenljiv . Na primer, razmislite o tej definiciji funkcije Python:



>>> def foo(bar=[]): # bar is optional and defaults to [] if not specified ... bar.append('baz') # but this line could be problematic, as we'll see... ... return bar

Pogosta napaka je misel, da bo neobvezni argument nastavljen na določen privzeti izraz vsakič funkcija se pokliče brez navedbe vrednosti za neobvezni argument. Na primer v zgornji kodi lahko pričakujemo, da bo klic foo() večkrat (tj. brez navedbe bar argumenta) vedno vrne 'baz', saj bi bila predpostavka, da vsakič foo() se pokliče (brez navedenega bar argumenta) bar je nastavljeno na [] (tj. nov prazen seznam).

Poglejmo pa, kaj se dejansko zgodi, ko to storite:

>>> foo() ['baz'] >>> foo() ['baz', 'baz'] >>> foo() ['baz', 'baz', 'baz']

Kaj? Zakaj je še naprej dodajal privzeto vrednost 'baz' do an obstoječe seznam vsakič foo() je bil poklican, namesto da bi ustvaril novo vsakič seznam?



Naprednejši programski odgovor na Python je tak privzeta vrednost za argument funkcije se oceni samo enkrat, in sicer v času, ko je funkcija definirana. Tako je bar argument se inicializira na privzeti (tj. prazen seznam) samo, kadar foo() je najprej definiran, nato pa pokliče na foo() (tj. brez navedenega bar argumenta) bo še naprej uporabljal isti seznam, na katerega bar je bil prvotno inicializiran.

FYI, pogosta rešitev za to je naslednja:

>>> def foo(bar=None): ... if bar is None: # or if not bar: ... bar = [] ... bar.append('baz') ... return bar ... >>> foo() ['baz'] >>> foo() ['baz'] >>> foo() ['baz']

Pogosta napaka # 2: napačna uporaba spremenljivk razreda

Upoštevajte naslednji primer:

>>> class A(object): ... x = 1 ... >>> class B(A): ... pass ... >>> class C(A): ... pass ... >>> print A.x, B.x, C.x 1 1 1

Smiselno.

>>> B.x = 2 >>> print A.x, B.x, C.x 1 2 1

Ja, spet po pričakovanjih.

kako ustvariti api v javascriptu
>>> A.x = 3 >>> print A.x, B.x, C.x 3 2 3

Kaj za $% #! & ?? Spremenili smo samo A.x. Zakaj C.x spremeniti tudi?

V Pythonu so spremenljivke razredov interno obdelane kot slovarji in sledijo temu, kar se pogosto imenuje Nalog za reševanje metode (MRO) . Torej v zgornji kodi, saj je atribut x ni mogoče najti v razredu C, iskali ga bodo v njegovih osnovnih razredih (samo A v zgornjem primeru, čeprav Python podpira več dedovanja). Z drugimi besedami, C nima svojega x lastnina, neodvisna od A. Torej, sklici na C.x so dejansko sklici na A.x. To povzroča težave s Pythonom, razen če se z njimi pravilno ravna. Več o tem atributi razreda v Pythonu .

Pogosta napaka # 3: Napačno določanje parametrov za blok izjem

Recimo, da imate naslednjo kodo:

>>> try: ... l = ['a', 'b'] ... int(l[2]) ... except ValueError, IndexError: # To catch both exceptions, right? ... pass ... Traceback (most recent call last): File '', line 3, in IndexError: list index out of range

Tukaj je težava v tem, da except izjava ne ne vzemite seznam izjem, določenih na ta način. Namesto tega je v Pythonu 2.x sintaksa except Exception, e se uporablja za vezavo izjeme na neobvezno drugi navedeni parameter (v tem primeru e), da bo na voljo za nadaljnji pregled. Posledično je v zgornji kodi IndexError izjema je ne ujeti except izjava; Namesto tega je izjema namesto tega vezana na parameter z imenom IndexError.

Pravilen način za ulov več izjem v except stavek poda prvi parameter kot a tuple vsebuje vse izjeme, ki jih je treba ujeti. Za največjo prenosljivost uporabite tudi as ključno besedo, saj to sintakso podpirata tako Python 2 kot Python 3:

>>> try: ... l = ['a', 'b'] ... int(l[2]) ... except (ValueError, IndexError) as e: ... pass ... >>>

Pogosta napaka # 4: Napačno razumevanje pravil obsega Python

Ločljivost obsega Python temelji na tem, kar je znano kot LEGB pravilo, ki je okrajšava za L okalno, JE zapiranje, G lobalni, B vgrajen. Zdi se dovolj preprosto, kajne? No, pravzaprav obstaja nekaj razlik v načinu delovanja v Pythonu, kar nas pripelje do pogostejše naprednejše težave s programiranjem Pythona spodaj. Upoštevajte naslednje:

>>> x = 10 >>> def foo(): ... x += 1 ... print x ... >>> foo() Traceback (most recent call last): File '', line 1, in File '', line 2, in foo UnboundLocalError: local variable 'x' referenced before assignment

V čem je problem?

Zgornja napaka se zgodi, ker ko naredite dodelitev na spremenljivko v obsegu, Python samodejno šteje, da je ta spremenljivka lokalna za ta obseg in zasenči katero koli podobno imenovano spremenljivko v katerem koli zunanjem obsegu.

Mnogi so s tem presenečeni, ko dobijo UnboundLocalError v prej delujoči kodi, ko je spremenjena z dodajanjem stavka o dodelitvi nekje v telesu funkcije. (Več o tem lahko preberete tukaj .)

Posebej pogosto je, da pri uporabi spotakne razvijalce seznami . Upoštevajte naslednji primer:

>>> lst = [1, 2, 3] >>> def foo1(): ... lst.append(5) # This works ok... ... >>> foo1() >>> lst [1, 2, 3, 5] >>> lst = [1, 2, 3] >>> def foo2(): ... lst += [5] # ... but this bombs! ... >>> foo2() Traceback (most recent call last): File '', line 1, in File '', line 2, in foo UnboundLocalError: local variable 'lst' referenced before assignment

Kaj? Zakaj foo2 bomba, medtem ko foo1 tekel v redu?

Odgovor je enak kot v prejšnjem primeru primera, vendar je res bolj subtilen. foo1 ne ustvarja dodelitev do lst, medtem ko foo2 je. Spomin na to lst += [5] je v resnici samo okrajšava za lst = lst + [5], vidimo, da poskušamo dodeliti vrednost do lst (zato Python domneva, da je v lokalnem obsegu). Vendar vrednost, ki jo želimo dodeliti lst temelji na lst sama (spet zdaj domnevno lokalno), ki še ni opredeljena. Bum.

Pogosta napaka # 5: Spreminjanje seznama med ponovitvijo po njem

Težava z naslednjo kodo bi morala biti dokaj očitna:

>>> odd = lambda x : bool(x % 2) >>> numbers = [n for n in range(10)] >>> for i in range(len(numbers)): ... if odd(numbers[i]): ... del numbers[i] # BAD: Deleting item from a list while iterating over it ... Traceback (most recent call last): File '', line 2, in IndexError: list index out of range

Brisanje predmeta s seznama ali polja med iteracijo nad njim je težava s Pythonom, ki jo dobro pozna vsak izkušen razvijalec programske opreme. Čeprav je zgornji primer morda precej očiten, lahko to celo nenamerno ugrizne v kodi, ki je veliko bolj zapletena.

Na srečo Python vključuje številne elegantne programske paradigme, ki lahko ob pravilni uporabi znatno poenostavijo in poenostavijo kodo. Stranska prednost tega je, da je manj verjetno, da bo naključno brisanje elementa s seznama, medtem ko se bo iteriralo nad njim, ugriznilo preprostejšo kodo. Ena takih paradigem je seznami . Poleg tega so razumevanja seznamov še posebej koristna za izogibanje tej specifični težavi, kot kaže ta nadomestna izvedba zgornje kode, ki deluje popolnoma:

>>> odd = lambda x : bool(x % 2) >>> numbers = [n for n in range(10)] >>> numbers[:] = [n for n in numbers if not odd(n)] # ahh, the beauty of it all >>> numbers [0, 2, 4, 6, 8]

Pogosta napaka # 6: Zmedeno, kako Python veže spremenljivke v zaporah

Upoštevajoč naslednji primer:

>>> def create_multipliers(): ... return [lambda x : i * x for i in range(5)] >>> for multiplier in create_multipliers(): ... print multiplier(2) ...

Pričakujete lahko naslednje rezultate:

0 2 4 6 8

Toda dejansko dobite:

8 8 8 8 8

Presenečenje!

To se zgodi zaradi Pythona pozna vezava vedenje, ki pravi, da se vrednosti spremenljivk, uporabljenih pri zapiranju, poiščejo v času, ko je poklicana notranja funkcija. Torej, v zgornji kodi, kadar koli se pokliče katera od vrnjenih funkcij, vrednost i je pogledal gor v okolici v času, ko se imenuje (in do takrat je zanka končana, zato je bila i že dodeljena končna vrednost 4).

Rešitev te pogoste težave s Pythonom je malce kramp:

>>> def create_multipliers(): ... return [lambda x, i=i : i * x for i in range(5)] ... >>> for multiplier in create_multipliers(): ... print multiplier(2) ... 0 2 4 6 8

Voilà! Privzete argumente tukaj izkoriščamo za ustvarjanje anonimnih funkcij, da dosežemo želeno vedenje. Nekateri bi temu rekli elegantno. Nekateri bi temu rekli subtilno. Nekateri to sovražijo. Če pa ste razvijalec Pythona, je to v vsakem primeru pomembno razumeti.

Pogosta napaka # 7: Ustvarjanje krožnih odvisnosti modula

Recimo, da imate dve datoteki, a.py in b.py, od katerih vsak uvozi drugega, in sicer:

V a.py:

import b def f(): return b.x print f()

In v b.py:

import a x = 1 def g(): print a.f()

Najprej poskusimo uvoziti a.py:

>>> import a 1

Odlično delal. Morda vas to preseneti. Navsezadnje imamo tukaj krožni uvoz, kar bi verjetno moral predstavljati težavo, kajne?

kupna moč bo večja, ko

Odgovor je, da zgolj prisotnost krožnega uvoza sam po sebi ni težava v Pythonu. Če je bil modul že uvožen, je Python dovolj pameten, da ga ne poskuša znova uvoziti. Vendar pa lahko resnično naletite na težave, odvisno od točke, v kateri vsak modul poskuša dostopati do funkcij ali spremenljivk, opredeljenih v drugem.

Če se torej vrnemo k našemu primeru, ko smo uvozili a.py, ni bilo težav pri uvozu b.py, saj b.py ne zahteva ničesar od a.py opredeliti v času uvoza . Edina referenca v b.py do a je klic a.f(). Toda ta klic je v g() in nič v a.py ali b.py prikliče g(). Življenje je torej dobro.

Kaj pa se zgodi, če poskusimo uvoziti b.py (brez predhodnega uvoza a.py, to je):

>>> import b Traceback (most recent call last): File '', line 1, in File 'b.py', line 1, in import a File 'a.py', line 6, in print f() File 'a.py', line 4, in f return b.x AttributeError: 'module' object has no attribute 'x'

Ojoj. To ni dobro! Tukaj je težava v tem, da med uvozom b.py poskuša uvoziti a.py, ki nato pokliče f(), ki poskuša dostopati do b.x. Toda b.x še ni opredeljena. Zato AttributeError izjema.

Vsaj ena rešitev tega je precej trivialna. Preprosto spremenite b.py za uvoz a.py znotraj g():

x = 1 def g(): import a # This will be evaluated only when g() is called print a.f()

Ne, ko ga uvozimo, je vse v redu:

>>> import b >>> b.g() 1 # Printed a first time since module 'a' calls 'print f()' at the end 1 # Printed a second time, this one is our call to 'g'

Pogosta napaka # 8: Ime se spopada z moduli standardne knjižnice Python

Ena od lepot Pythona je bogastvo knjižničnih modulov, ki jih dobite 'iz škatle'. Če pa se temu zavestno ne izognete, v standardni knjižnici, ki je priložena Pythonu, ni tako težko naleteti na imenski spopad med imenom enega od vaših modulov in modulom z istim imenom. , morda imate v svoji kodi modul z imenom email.py, ki bi bil v nasprotju s standardnim modulom z istim imenom).

To lahko privede do težav, na primer pri uvozu druge knjižnice, ki poskuša uvoziti različico modula Python Standard Library, toda ker imate modul z istim imenom, drugi paket pomotoma uvozi vašo različico namesto tiste znotraj Python Standard Library. Tu se dogajajo slabe napake Pythona.

Zato je treba paziti, da ne uporabljamo enakih imen kot v modulih standardne knjižnice Python. Preprosteje spremenite ime modula v paketu, kot pa datoteko Predlog za izboljšanje Pythona (PEP) zahtevati spremembo imena gorvodno in poskusiti to odobriti.

Pogosta napaka # 9: neuspeh pri odpravljanju razlik med Python 2 in Python 3

Upoštevajte naslednjo datoteko foo.py:

import sys def bar(i): if i == 1: raise KeyError(1) if i == 2: raise ValueError(2) def bad(): e = None try: bar(int(sys.argv[1])) except KeyError as e: print('key error') except ValueError as e: print('value error') print(e) bad()

V Pythonu 2 to deluje dobro:

$ python foo.py 1 key error 1 $ python foo.py 2 value error 2

Zdaj pa se zavrtimo na Pythonu 3:

$ python3 foo.py 1 key error Traceback (most recent call last): File 'foo.py', line 19, in bad() File 'foo.py', line 17, in bad print(e) UnboundLocalError: local variable 'e' referenced before assignment

Kaj se je pravkar zgodilo tukaj? 'Težava' je v tem, da v Pythonu 3 objekt izjeme ni dostopen preko obsega except blok. (Razlog za to je, da bi sicer ohranil referenčni cikel z ogrodjem sklada v pomnilniku, dokler zbiralnik smeti ne zažene in izbriše reference iz pomnilnika. Na voljo je več tehničnih podrobnosti o tem tukaj ).

Eden od načinov, kako se temu težavi izogniti, je ohranjanje sklicevanja na objekt izjeme zunaj obseg except blok, tako da ostane dostopen. Tu je različica prejšnjega primera, ki uporablja to tehniko, s čimer dobimo kodo, ki je prijazna za Python 2 in Python 3:

import sys def bar(i): if i == 1: raise KeyError(1) if i == 2: raise ValueError(2) def good(): exception = None try: bar(int(sys.argv[1])) except KeyError as e: exception = e print('key error') except ValueError as e: exception = e print('value error') print(exception) good()

Zagon tega na Py3k:

$ python3 foo.py 1 key error 1 $ python3 foo.py 2 value error 2

Joj!

(Mimogrede, naša Vodnik za najem Pythona razpravlja o številnih drugih pomembnih razlikah, ki se jih je treba zavedati pri selitvi kode iz Pythona 2 v Python 3.)

Pogosta napaka # 10: Zloraba __del__ metoda

Recimo, da ste to imeli v datoteki z imenom mod.py:

import foo class Bar(object): ... def __del__(self): foo.cleanup(self.myhandle)

In potem ste poskusili to narediti iz another_mod.py:

import mod mybar = mod.Bar()

Dobili bi grdo AttributeError izjema.

Zakaj? Ker, kot poročajo tukaj , ko se tolmač izklopi, so globalne spremenljivke modula nastavljene na None Kot rezultat, v zgornjem primeru, na točki, da __del__ se prikliče ime foo je že nastavljeno na None.

Rešitev tega nekoliko naprednejšega programskega problema Pythona bi bila uporaba atexit.register() namesto tega. Na ta način, ko se vaš program konča z izvajanjem (ko se normalno izstopi, to je), se začnejo registrirani upravljavci prej tolmač je izključen.

S tem razumevanjem je popravek za zgornje mod.py koda bi potem lahko izgledala nekako takole:

import foo import atexit def cleanup(handle): foo.cleanup(handle) class Bar(object): def __init__(self): ... atexit.register(cleanup, self.myhandle)

Ta izvedba zagotavlja čist in zanesljiv način klica vse potrebne funkcije čiščenja ob običajnem zaključku programa. Očitno je, da je do foo.cleanup da se odločite, kaj storiti s predmetom, vezanim na ime self.myhandle, vendar dobite idejo.

Zaviti

Python je zmogljiv in prilagodljiv jezik s številnimi mehanizmi in paradigmami, ki lahko močno izboljšajo produktivnost. Kot pri katerem koli programskem orodju ali jeziku je tudi v tem primeru omejeno razumevanje ali spoštovanje njegovih zmožnosti lahko včasih večja ovira kot korist, zato človek v pregovornem stanju »ve dovolj, da je lahko nevaren«.

neprekinjena dostava z uporabo pakirnice in teraforme

Seznanitev s ključnimi odtenki Pythona, kot so (vendar nikakor ne omejene na) zmerno napredne programske težave, predstavljene v tem članku, bo pomagalo optimizirati uporabo jezika in se izogniti nekaterim pogostejšim napakam.

Morda boste želeli preveriti tudi našo Insider's Guide to Python Interviewing za predloge glede vprašanj za razgovore, ki lahko pomagajo prepoznati strokovnjake za Python.

Upamo, da so vam kazalci v tem članku v pomoč in pozdravljamo vaše povratne informacije.

Ne borite se s prihodki zaposlenih. Sprejmi, prilagodi in izkoristi.

Prihodnost Dela

Ne borite se s prihodki zaposlenih. Sprejmi, prilagodi in izkoristi.
Povečanje uvajanja programske opreme - Vadnica za Docker Swarm

Povečanje uvajanja programske opreme - Vadnica za Docker Swarm

Back-End

Priljubljene Objave
Nasveti za razvoj aplikacije za Android: Moja naučena lekcija
Nasveti za razvoj aplikacije za Android: Moja naučena lekcija
Uvod v programski jezik Elm
Uvod v programski jezik Elm
Kako izvesti teste uporabnosti v šestih korakih
Kako izvesti teste uporabnosti v šestih korakih
Če ne uporabljate podatkov UX, to ni zasnova UX
Če ne uporabljate podatkov UX, to ni zasnova UX
Vodje izdelkov v primerjavi s projektnimi vodji II. Del: Situacijska analiza
Vodje izdelkov v primerjavi s projektnimi vodji II. Del: Situacijska analiza
 
Street Guide o tem, kako najeti honorarnega finančnega direktorja
Street Guide o tem, kako najeti honorarnega finančnega direktorja
Zgradite elegantne komponente tirnic z navadnimi starimi predmeti iz rubina
Zgradite elegantne komponente tirnic z navadnimi starimi predmeti iz rubina
Ustvarjanje aplikacij Vue.js, upodobljenih na strežniku, z uporabo Nuxt.js
Ustvarjanje aplikacij Vue.js, upodobljenih na strežniku, z uporabo Nuxt.js
Kako samostojni svetovalci za zasebni kapital odklenejo vrednost delničarjev
Kako samostojni svetovalci za zasebni kapital odklenejo vrednost delničarjev
Zakaj sem iz AngularJS prešel na React
Zakaj sem iz AngularJS prešel na React
Priljubljene Objave
  • postopek identifikacije projektov, ki bodo ustvarili pozitivne denarne tokove, se imenuje:
  • kje se učiti c ++
  • kako ustvariti koncept
  • kako narediti žeton
  • kako zgraditi ogrodje
Kategorije
Prihodki In Rast Prihodnost Dela Trendi Orodja In Vaje Back-End Ui Design Spletno Mesto Življenjski Cikel Izdelka Donosnost In Učinkovitost Mobilno Oblikovanje

© 2021 | Vse Pravice Pridržane

apeescape2.com