socialgekon.com
  • Tärkein
  • Varastointi
  • Ihmiset Ja Joukkueet
  • Ongelmien karttoittaminen
  • Sijoittajat Ja Rahoitus
Teknologia

Buginen Python-koodi: 10 yleisintä virhettä, joita Python-kehittäjät tekevät

Tietoja Pythonista

Python on korkean tason olio- ja tulkittu ohjelmointikieli, jolla on dynaaminen semantiikka. Sen korkea integroitu tietorakenne yhdistettynä dynaamiseen kirjoittamiseen ja sitomiseen tekevät siitä erittäin houkuttelevan nopea sovelluskehitys , sekä käytettäväksi komentosarjakielenä tai liimana olemassa olevien komponenttien tai palveluiden yhdistämiseen. Python toimii moduulien ja pakettien kanssa, mikä edistää ohjelmien modulaarisuutta ja koodien uudelleenkäyttöä.

Tietoja tästä artikkelista

Yksinkertainen ja helppo oppia Python-syntakse, voit lähettää python-kehittäjät väärään suuntaan - etenkin ne, jotka oppivat kieltä - menettävät osan sen hienovaraisuuksista matkan varrella ja aliarvioivat kielen voiman Pythonin monipuolinen kieli .

Tässä mielessä tämä artikkeli esittelee 'top 10' -luettelon hienovaraisista, vaikeasti ymmärrettävistä virheistä, jotka voivat saada kiinni jopa joistakin edistyneimmistä Python-kehittäjistä.



( Huomautus: Tämä artikkeli on tarkoitettu edistyneemmälle yleisölle kuin Common Python Programmer Errors, joka on suunnattu enemmän niille, joilla kieli on uusi. )

Yleinen virhe # 1: Lausekkeiden väärinkäyttö oletusarvoina toimintoargumenteille

Python antaa sinun määrittää, että funktion argumentti on valinnainen antamalla sille oletusarvo. Vaikka tämä on kielen hieno ominaisuus, se voi aiheuttaa sekaannusta oletusasetuksena vaihteleva . Harkitse esimerkiksi tätä Python-funktion määritelmää:

>>> 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

Yleinen väärinkäsitys on, että valinnainen argumentti asetetaan tietylle oletuslausekkeelle joka kerta, kun funktiota kutsutaan ilman tarvetta antaa arvoa valinnaiselle argumentille. Esimerkiksi yllä olevassa koodissa saatat odottaa soittavan foo() useita kertoja (ts. määrittelemättä palkkiargumenttia) palauttaisi aina baz, koska hypoteesi olisi, että joka kerta foo() kutsutaan (ilman määritettyä pylväargumenttia) pylväsasetus on [] (eli uusi tyhjä luettelo).

Mutta katsotaanpa, mitä todella tapahtuu, kun tämä tehdään:

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

Hei? Miksi oletusarvo oli baz olemassa olevaan luetteloon joka kerta että foo() kutsuttiin sen sijaan, että loisit uuden listan joka tilaisuudessa? Edistynein Python-ohjelmointivastaus on, funktion argumentin oletusarvo arvioidaan vain kerran, kun funktio määritetään. Siksi palkkiargumentti alustetaan oletusarvoonsa (eli tyhjään luetteloon) vain, kun foo() on määritelty ensin, mutta sitten kutsuu foo() (eli ilman määriteltyä bar argumenttia) he käyttävät edelleen samaa luetteloa kuin bar alun perin alustettiin.

Muuten, yleinen ratkaisu tähän on seuraava:

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

Yleinen virhe # 2: Luokkimuuttujien väärä käyttö

Tarkastellaan seuraavaa esimerkkiä:

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

Käydä järkeen.

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

Kyllä, jälleen odotetusti.

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

Mikä tämä on? Muutamme vain A.x Miksi C.x muuttunut myös?

Pythonissa luokan muuttujia käsitellään sisäisesti kuten sanakirjoja, ja ne seuraavat sitä, mihin usein viitataan Menetelmän tarkkuusjärjestys (MRO) . Joten yllä olevassa koodissa, koska attribuuttia x ei löydy luokasta C, sitä etsitään sen perusluokista (yllä olevassa esimerkissä vain A, vaikka Python tukee useita perintöjä). Toisin sanoen C: llä ei ole A: sta riippumatonta omaa ominaisuutta x. Siksi viitteet C.x: ään ovat itse asiassa viittauksia A.x: ään. Tämä aiheuttaa Python-ongelman, ellei sitä käsitellä oikein. Lisätietoja luokan määritteet Pythonissa .

Yleinen virhe # 3: Parametrien määrittäminen virheellisesti poikkeuslohkolle

Oletetaan, että sinulla on seuraava koodi:

>>> 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

Ongelmana on, että raportti except se ei ota luetteloa tällä tavalla määritetyistä poikkeuksista. Sen sijaan Python 2.x -syntaksia except Exception, e käytetään sitomaan poikkeus toiseen parametriin valinnainen määritelty (tässä tapauksessa e), jotta se olisi käytettävissä lisätarkastuksia varten. Tämän seurauksena yllä olevassa koodissa poikkeus IndexError ei ole raportin siepattu except; pikemminkin poikkeus lopulta sidotaan parametriin nimeltä IndexError.

Oikea tapa saada useita poikkeuksia raporttiin except on määrittää ensimmäinen parametri a: ksi tupla A sisältää kaikki pyydettävät poikkeukset. Käytä myös maksimaalista siirrettävyyttä käyttämällä as avainsanaa, koska Python 2 ja Python 3 tukevat syntaksia:

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

Yleinen virhe # 4: Pythonin soveltamisalan sääntöjen ymmärtämättä jättäminen

Pythonin laajuusresoluutio perustuu niin sanottuun sääntöön LEGB , joka on lyhenne sanoista L okaali, ON n sulkeminen, G lobal, B ylöspäin. Se näyttää melko suoraviivaiselta, eikö? No, oikeastaan ​​on olemassa muutamia hienovaraisuuksia tämän toiminnassa Pythonissa, mikä johtaa meidät alla olevaan yleiseen, edistyneempään Python-ohjelmointiongelmaan.

Harkitse seuraavaa:

>>> 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

Mikä on ongelma?

Yllä oleva virhe johtuu siitä, että kun a tehtävä laajuuden muuttujalle, Python pitää muuttujaa automaattisesti paikallisena siinä laajuudessa ja seuraa mitä tahansa samankaltaista muuttujaa missä tahansa ulommassa laajuudessa.

Monet heistä ovat siis yllättyneitä saadessaan UnboundLocalError edellisessä työkoodissa, kun sitä muokataan lisäämällä raporttilauseke jonnekin funktion runkoon. (Voit lukea lisää tästä tässä .)

On erityisen yleistä, että tämä hämmentää kehittäjiä käytön aikana luettelot . Harkitse seuraavaa esimerkkiä:

>>> 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

Hei? Miksi foo2 epäonnistui, kun taas foo1 toimi hyvin?

Vastaus on sama kuin edellisen esimerkin ongelma, mutta se on varmasti hienovaraisempi. foo1 Ei tee a tehtävä a lst, kun taas foo2 jos se on. Muista, että lst += [5] on oikeastaan ​​lyhenne sanoista lst = lst + [5], näemme, että yritämme määrittää arvon lst: lle (joten Python olettaa, että olet paikallisella alueella). Lst: lle yrittämämme arvo perustuu kuitenkin samaan lst (jälleen oletetaan olevan paikallinen), joka on vielä määriteltävä. Puomi.

Yleinen virhe # 5: Muokkaa luetteloa iteroimalla sen yli

Seuraavan koodin ongelman pitäisi olla melko ilmeinen:

>>> 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

Kohteen poistaminen luettelosta tai taulukosta, samalla kun iteroidaan sen yli, on Python-ongelma, jonka kaikki kokeneet ohjelmistokehittäjät tuntevat. Vaikka yllä oleva esimerkki saattaa olla melko ilmeinen, edes edistyneet kehittäjät voivat vahingossa jäädä tämän paljon monimutkaisemman koodin ulkopuolelle.

Onneksi Python sisältää useita tyylikkäitä ohjelmointimalleja, jotka oikein käytettynä voivat johtaa merkittävästi yksinkertaistettuun ja virtaviivaiseen koodiin. Tämän toissijainen etu on, että yksinkertaisempi koodi jää vähemmän todennäköiseksi luettelon kohteen vahingossa tapahtuvan poistamisen aikana, kun iteroidaan-over-it-virhe. Yksi tällainen paradigma on [luettelon ymmärtäminen] ((https://docs.python.org/2/tutorial/datastructures.html#tut-listcomps). Toisaalta luettelon ymmärtäminen on erityisen hyödyllistä tämän ongelman välttämiseksi, kuten näkyy tässä yllä esitetyn koodin vaihtoehtoisessa toteutuksessa, joka toimii täydellisesti:

>>> 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]

Yleinen virhe # 6: sekaannus siitä, kuinka Python sitoo muuttujia lukkoihin

Ottaen huomioon seuraavan esimerkin:

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

Sinun pitäisi odottaa seuraavaa tulosta:

0 2 4 6 8

Mutta saat todella:

8 8 8 8 8

Yllätys!

Tämä tapahtuu käyttäytymisen vuoksi myöhäinen linkki Python, joka sanoo, että sulkemisissa käytettyjen muuttujien arvot haetaan silloin, kun sisäinen toiminto kutsutaan. Joten yllä olevassa koodissa, kun jotain palautetuista funktioista kutsutaan, arvo i halutaan sisään sen ympärillä oleva alue silloin, kun sitä kutsutaan (ja siinä vaiheessa ympyrä on valmis, joten i: lle on jo annettu lopullinen arvo 4).

Ratkaisu tähän yleiseen Python-ongelmaan on vähän hakkerointi:

>>> 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à! Hyödynnämme oletusargumentteja anonyymien toimintojen luomiseksi halutun toiminnan saavuttamiseksi. Jotkut kutsuvat tätä tyylikkääksi. Jotkut kutsuisivat sitä hienovaraiseksi. Jotkut vihaavat sitä. Mutta jos olet Python-kehittäjä, tämä on tärkeää ymmärtää.

Yleinen virhe # 7: Luo pyöreän moduulin riippuvuudet

Oletetaan, että sinulla on kaksi tiedostoa, a.py ja b.py, joista kukin tuo toisen, seuraavasti:

sisään a.py:

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

Ja b.py

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

Yritetään ensin tuoda a.py:

>>> import a 1

Se toimi hyvin. Ehkä olit yllättynyt. Loppujen lopuksi meillä on täällä kiertotalous, jonka oletettavasti pitäisi olla ongelma, eikö?

Vastaus on, että pelkkä läsnäolo pyöreä tuonti ei sinänsä ole ongelma Pythonissa. Jos moduuli on jo tuotu, Python on tarpeeksi älykäs, jotta se ei yritä tuoda sitä uudelleen. Riippuen siitä pisteestä, jossa kukin moduuli yrittää käyttää toisessa määriteltyjä toimintoja tai muuttujia, saatat kuitenkin törmätä ongelmiin.

Joten palataan esimerkkiin, kun tuot a.py, minulla ei ollut ongelmia tuoda b.py, koska b.py ei vaadi mitään b.py määriteltävä tuontihetkellä. Ainoa viite b.py -sivulla a a, on kutsu a.f() Mutta puhelu on g() eikä mitään kohdassa a.py tai b.py kutsuu g(). Elämä on siis kaunista.

Mutta mitä tapahtuu, jos yrität tuoda b.py (tietysti ilman aikaisempaa maahantuontia a.py):

>>> 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'

Voi ei. Tuo ei ole hyvä! Ongelmana on, että tuontiprosessissa b.py se yrittää tuoda a.py, mikä sen seurauksena kutsuu f(), joka puolestaan ​​yrittää käyttää b.x. Mutta b.x sitä ei ole vielä määritelty. Tästä syystä poikkeus AttributeError

Ainakin yksi ratkaisu tähän on melko triviaali. Muokkaa vain b.py tuoda a.py sisällä g():

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

Tuodessaan kaikki on hyvin:

>>> 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'

Yleinen virhe # 8: Nimikohtaus Python Standard Library -moduuleilla

Yksi Pythonin eduista on suuri määrä kirjastomoduuleja, joita sillä on alusta alkaen. Mutta seurauksena on, että jos et tietoisesti välttää tätä, ei ole niin vaikeaa törmätä nimiristiriitaan yhden moduulisi nimen ja Pythonin mukana toimitettavan standardikirjaston saman nimisen moduulin välillä (esimerkiksi Esimerkiksi koodissasi voi olla email.py -moduuli, joka olisi ristiriidassa samannimisen tavallisen kirjastomoduulin kanssa).

Tämä voi johtaa erittäin aggressiivisiin ongelmiin, kuten tuoda toinen kirjasto, joka puolestaan ​​yrittää tuoda moduulin Python-standardikirjastot-version, mutta koska sinulla on jo saman niminen moduuli, toinen paketti tuo virheellisesti versiosi sisään Python-standardikirjastossa olevasta, ja tässä esiintyy vakavimmat virheet.

Siksi on vältettävä samojen nimien käyttöä kuin tavalliset Python-kirjastomoduulit. Sinulle on paljon helpompaa nimetä moduuli uudelleen paketissasi kuin esittää Python-parannusehdotus. (PEP) pyytää nimimuutosta alkupäässä ja hyväksymään se.

Yleinen virhe # 9: Python 2: n ja Python 3: n erojen välttäminen

Harkitse seuraavaa tiedostoa 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()

Python 2: ssa tämä toimii hyvin:

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

Mutta nyt pyöritetään Python 3: a:

$ 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

Mitä täällä juuri tapahtui? 'Ongelma' on se, että Python 3: ssa poikkeusobjekti ei ole käytettävissä except -lohkon ulkopuolella. (Syynä tähän on se, että se muuten säilyttäisi vertailusilmukan pinokehyksen kanssa muistissa, kunnes roskien kerääjä juoksee ja puhdistaa viitteet muistista. Lisää teknisiä yksityiskohtia on saatavilla tässä ).

Yksi tapa kiertää tämä ongelma on pitää viittaus poikkeusobjektiin lukuun ottamatta lukulohkoa niin, että se pysyy käytettävissä. Tässä on yllä olevan esimerkin versio, joka käyttää tätä tekniikkaa, joten annostelee koodi ja tekee siitä yhteensopivamman Python 2: n ja Python 3: n kanssa:

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()

Tämän toteutus tapahtuu Py3k: ssä:

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

¡ Yupi!

(Muuten, meidän Python Palkkausopas käsittelee useita tärkeitä eroja, jotka on otettava huomioon, kun siirrät koodisi Python 2: sta Python 3: een.)

Yleinen virhe # 10: __del__: n väärinkäyttö

Oletetaan, että sinulla oli tämä tiedostossa nimeltä mod.py:

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

Ja sitten yritit tehdä tämän another_mod.py:

import mod mybar = mod.Bar()

Saat ruman poikkeuksen AttributeError.

Miksi? Koska, kuten on raportoitu tässä Kun tulkki kytketään pois päältä, moduulin globaaleiksi muuttujiksi asetetaan None. Seurauksena on, että yllä olevassa esimerkissä siinä kohdassa __del__ kutsutaan, nimi foo on jo asetettu arvoon None.

Ratkaisu tähän ongelmaan, joka on jonkin verran edistyneempi kuin Python-ohjelmointi, olisi käyttää atexit.register() sen sijaan. Tällä tavalla, kun ohjelma on suoritettu (tarkoitan normaalisti poistumista), rekisteröidyt esimiehesi erotetaan. ennen että tulkki sammuu.

Tämän tiedon avulla ratkaisu edelliselle koodille mod.py se voi olla jotain tällaista:

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

Tämä sovellus tarjoaa puhtaan ja luotettavan tavan kutsua kaikki tarvittavat puhdistustoiminnot ohjelman normaalin lopettamisen jälkeen. On selvää, että foo.cleanup on päättänyt, mitä tehdä nimelle self.myhandle liitettyyn esineeseen, mutta saat asian.

Loppuun

Python on tehokas ja joustava kieli, jolla on monia mekanismeja ja paradigmoja, jotka voivat parantaa huomattavasti tuottavuutta. Kuten minkä tahansa ohjelmiston tai kielityökalun kohdalla, rajoitettu ymmärrys tai kykyjen arvostaminen sen kyvyistä voi kuitenkin joskus olla enemmän este kuin omaisuus, jättäen meidät sananlaskuiseen tilaan 'tietää tarpeeksi vaaralliseksi'.

Tutustuminen Pythonin tärkeimpiin vivahteisiin, kuten (mutta ei suinkaan rajoitettu) tässä artikkelissa käsiteltyihin kohtalaisen edistyneisiin ohjelmointiongelmiin, auttaa optimoimaan kielen käyttöä välttäen joitain sen yleisimpiä virheitä.

Sinun tulisi tarkistaa meidän Sisäpiiriopas Python-haastatteluihin , ehdotuksia haastattelukysymyksistä, jotka voivat auttaa tunnistamaan Python-asiantuntijoita.

Toivomme, että tämän artikkelin vinkit ovat hyödyllisiä ja arvostamme palautettasi.

Palvelimettomien Node.js-toimintojen toteuttaminen Google Cloudin avulla

Taustaa

Palvelimettomien Node.js-toimintojen toteuttaminen Google Cloudin avulla
10 UX-toimitettavaa huippusuunnittelijaa käyttää

10 UX-toimitettavaa huippusuunnittelijaa käyttää

Ux-Suunnittelu

Suosittu Viestiä
Ultimate Guide to Processing Language Osa II: Yksinkertaisen pelin rakentaminen
Ultimate Guide to Processing Language Osa II: Yksinkertaisen pelin rakentaminen
Kuinka tehdä valokuvakollaasi iPhonessa: sovelluksia ja ideoita
Kuinka tehdä valokuvakollaasi iPhonessa: sovelluksia ja ideoita
Stork, osa 3: Lausekkeiden ja muuttujien toteuttaminen
Stork, osa 3: Lausekkeiden ja muuttujien toteuttaminen
3D-grafiikka: WebGL-opetusohjelma
3D-grafiikka: WebGL-opetusohjelma
Kulttuurienvälinen suunnittelu ja UX: n rooli
Kulttuurienvälinen suunnittelu ja UX: n rooli
 
Yrityksen lähtökohta: datatiede ja tekoäly
Yrityksen lähtökohta: datatiede ja tekoäly
Parhaat pilvitallennusvaihtoehdot iPhone-valokuvaajille
Parhaat pilvitallennusvaihtoehdot iPhone-valokuvaajille
Visuaalinen ohjelmointi Node-Red: llä: Internet-yhteyden muodostaminen helposti
Visuaalinen ohjelmointi Node-Red: llä: Internet-yhteyden muodostaminen helposti
Termilomakemallit - lausekkeet, joihin kannattaa kiinnittää huomiota neuvottelujen aikana
Termilomakemallit - lausekkeet, joihin kannattaa kiinnittää huomiota neuvottelujen aikana
Pythonin monisäikeinen ja moniprosessointiopetus
Pythonin monisäikeinen ja moniprosessointiopetus
Luokat
Tuote-Ihmiset Ja JoukkueetSuunnittelijan ElämäUi-SuunnitteluTaustaaKaukosäätimen NousuAmmuntaiOS-vinkkejäVarastointiMuuLiikevaihdon Kasvu

© 2023 | Kaikki Oikeudet Pidätetään

socialgekon.com