Wat zijn Python-naamruimten (en waarom zijn ze nodig?)

Naamconflicten gebeuren de hele tijd in het echte leven. Elke school die ik ooit bezocht, had bijvoorbeeld minstens twee studenten in mijn klas die dezelfde voornaam hadden. Als iemand in de klas zou komen en vroeg om student X, vroegen we enthousiast: "Over welke vraag je het? Er zijn twee studenten genaamd X." Daarna zou de onderzoekende persoon ons een achternaam geven en we zouden hem aan de rechterkant X introduceren.

Al deze verwarring en het proces van het bepalen van de exacte persoon waar we het over hebben bij het zoeken naar andere informatie naast een voornaam kan worden vermeden als iedereen een unieke naam zou hebben. Dit is geen probleem in een klas van 30 studenten. Het zal echter steeds moeilijker worden om met een te komen uniek, belangrijk en makkelijk te onthouden naam voor elk kind in een school, stad, stad, land of de hele wereld. Een ander probleem bij het verstrekken van elk kind aan een unieke naam is dat het proces om te bepalen of iemand anders ook zijn kind Macey, Maci of Macie heeft genoemd erg vermoeiend kan zijn..

Een zeer vergelijkbaar conflict kan ook optreden bij het programmeren. Wanneer u een programma schrijft van slechts 30 regels zonder externe afhankelijkheden, is het heel eenvoudig om unieke en betekenisvolle namen aan al uw variabelen te geven. Het probleem doet zich voor wanneer er duizenden regels in een programma zijn en u ook enkele externe modules hebt geladen. In deze zelfstudie leert u over naamruimten, het belang ervan en de resolutie van de bereikbaarheid in Python. 

Wat zijn naamruimten?

Een naamruimte is in feite een systeem om ervoor te zorgen dat alle namen in een programma uniek zijn en zonder enig conflict kunnen worden gebruikt. Je weet misschien al dat alles in Python-achtige strings, lijsten, functies, enz. Een object is. Een ander interessant feit is dat Python namespaces implementeert als woordenboeken. Er is een naam-tot-object-toewijzing, met de namen als sleutels en de objecten als waarden. Meerdere naamruimten kunnen dezelfde naam gebruiken en aan een ander object toewijzen. Hier zijn een paar voorbeelden van naamruimten:

  • Lokale naamruimte: deze naamruimte bevat lokale namen in een functie. Deze naamruimte wordt gemaakt wanneer een functie wordt aangeroepen en duurt slechts totdat de functie terugkeert.
  • Globale naamruimte: deze naamruimte bevat namen van verschillende geïmporteerde modules die u in een project gebruikt. Het wordt gemaakt wanneer de module wordt opgenomen in het project en het duurt tot het script eindigt.
  • Ingebouwde naamruimte: deze naamruimte bevat ingebouwde functies en ingebouwde uitzonderingsnamen.

In de Mathematical Modules in Python-serie over Envato Tuts + schreef ik over nuttige wiskundige functies die beschikbaar zijn in verschillende modules. De modules math en cmath hebben bijvoorbeeld veel functies die beide gemeenschappelijk hebben, zoals log10 (), acos (), cos (), exp (), enz. Als u beide modules in hetzelfde programma gebruikt, is de enige manier om deze functies ondubbelzinnig te gebruiken, om ze te voorzien van de naam van de module, zoals math.log10 () en cmath.log10 ().

Wat is bereik?

Met naamruimten kunnen we alle namen in een programma op unieke wijze identificeren. Dit betekent echter niet dat we een variabele naam kunnen gebruiken waar we maar willen. Een naam heeft ook een scope die de delen van het programma definieert waar je die naam zou kunnen gebruiken zonder een voorvoegsel te gebruiken. Net als naamruimten zijn er ook meerdere scopes in een programma. Hier is een lijst van een aantal scopes die kunnen bestaan ​​tijdens de uitvoering van een programma.

  • Een lokaal bereik, het binnenste bereik dat een lijst met lokale namen bevat die beschikbaar zijn in de huidige functie.
  • Een bereik van alle omsluitende functies. Het zoeken naar een naam begint bij het dichtstbijzijnde omsluitende kader en gaat naar buiten.
  • Een bereik op moduleniveau dat alle globale namen uit de huidige module bevat.
  • De buitenste scope die een lijst bevat met alle ingebouwde namen. Dit bereik wordt als laatste doorzocht om de naam te vinden waarnaar u hebt verwezen. 

In de volgende secties van deze tutorial zullen we uitgebreid gebruik maken van de ingebouwde functie Python dir () om een ​​lijst met namen in de huidige lokale scope te retourneren. Hiermee kunt u het concept van naamruimten en bereik beter begrijpen.

Bereikresolutie

Zoals ik in het vorige gedeelte al heb vermeld, start het zoeken naar een bepaalde naam vanuit de binnenste functie en gaat dan verder en hoger totdat het programma die naam aan een object kan toewijzen. Als een dergelijke naam in geen van de naamruimten wordt gevonden, wordt in het programma een a NameError uitzondering.

Voordat we beginnen, probeer dan te typen dir () in IDLE of een andere Python IDE.

dir () # ['__builtins__', '__doc__', '__loader__', '__name__', '__package__', '__spec__']

Al deze namen worden vermeld door dir () zijn beschikbaar in elk Python-programma. Kortheidshalve zal ik daarnaar verwijzen als '__builtins __' ... '__spec__' in de rest van de voorbeelden.

Laten we de output van de bekijken dir () functie na het definiëren van een variabele en een functie.

a_num = 10 dir () # ['__builtins __' ... '__spec__', 'a_num'] def some_func (): b_num = 11 print (dir ()) some_func () # ['b_num'] dir () # ['__builtins__ '...' __spec__ ',' a_num ',' some_func '] 

De dir () functie voert alleen de lijst met namen binnen het huidige bereik uit. Dat is de reden waarom binnen de reikwijdte van some_func (), er is maar één naam genoemd b_num. Roeping dir () na het definiëren some_func () voegt het toe aan de lijst met namen die beschikbaar zijn in de globale naamruimte.

Laten we nu eens kijken naar de lijst met namen in sommige geneste functies. De code in dit blok gaat verder vanaf het vorige blok.

def outer_func (): c_num = 12 def inner_func (): d_num = 13 print (dir (), '- names in inner_func') e_num = 14 inner_func () print (dir (), '- names in outer_func') outer_func ( ) # ['d_num'] - namen in inner_func # ['c_num', 'e_num', 'inner_func'] - namen in outer_func

De bovenstaande code definieert twee variabelen en een functie binnen de scope van outer_func (). Binnen inner_func (), de dir () function drukt alleen de naam af d_num. Dit lijkt eerlijk als d_num is de enige hierin gedefinieerde variabele.

Tenzij expliciet aangegeven door gebruik van globaal, opnieuw toewijzen van een globale naam binnen een lokale naamruimte maakt een nieuwe lokale variabele met dezelfde naam. Dit is te zien aan de volgende code.

a_num = 10 b_num = 11 def outer_func (): globaal a_num a_num = 15 b_num = 16 def inner_func (): globaal a_num a_num = 20 b_num = 21 print ('a_num inside inner_func:', a_num) print ('b_num inside inner_func: ', b_num) inner_func () print (' a_num inside outer_func: ', a_num) print (' b_num inside outer_func: ', b_num) outer_func () print (' a_num outside all functions: ', a_num) print (' b_num outside all functies: ', b_num) # a_num inside inner_func: 20 # b_num inside inner_func: 21 # a_num inside outer_func: 20 # b_num inside outer_func: 16 # a_num outside all functions: 20 # b_num buiten alle functies: 11

In zowel de outer_func () en inner_func (), a_num is verklaard als een globale variabele. We stellen alleen een andere waarde in voor dezelfde globale variabele. Dit is de reden dat de waarde van a_num op alle locaties is 20. Aan de andere kant creëert elke functie zijn eigen functie b_num variabele met een lokale scope, en de afdrukken() functie drukt de waarde af van deze lokaal geschaalde variabele.

Modules correct importeren

Het is heel gebruikelijk om externe modules in uw projecten te importeren om de ontwikkeling te versnellen. Er zijn drie verschillende manieren om modules te importeren. In deze sectie leert u over al deze methoden en bespreekt u hun voor- en nadelen in detail.

  • van module-import *: Deze methode voor het importeren van een module importeert alle namen uit de gegeven module rechtstreeks in uw huidige naamruimte. U kunt in de verleiding komen om deze methode te gebruiken, omdat u hiermee direct een functie kunt gebruiken zonder de naam van de module als een voorvoegsel toe te voegen. Het is echter erg foutgevoelig en u verliest ook de mogelijkheid om te vertellen welke module die functie daadwerkelijk heeft geïmporteerd. Hier is een voorbeeld van het gebruik van deze methode:
dir () # ['__builtins __' ... '__spec__'] uit wiskundige import * dir () # ['__builtins __' ... '__spec__', 'acos', 'acosh', 'asin', 'asinh', # 'atan' , 'atan2', 'atanh', 'ceil', 'copysign', 'cos', 'cosh', 'degrees', # 'e', ​​'erf', 'erfc', 'exp', 'expm1', 'fabs', 'factorial', 'floor', 'fmod', # 'frexp', 'fsum', 'gamma', 'gcd', 'hypot', 'inf', 'isclose', 'isfinite', # 'isinf', 'isnan', 'ldexp', 'lgamma', 'log', 'log10', 'log1p', 'log2', # 'modf', 'nan', 'pi', 'pow', ' radians ',' sin ',' sinh ',' sqrt ',' tan ', #' tanh ',' trunc '] log10 (125) # 2.0969100130080562 van cmath import * dir () # [' __builtins __ '...' __spec__ ' , 'acos', 'acosh', 'asin', 'asinh', 'atan', # 'atan2', 'atanh', 'ceil', 'copysign', 'cos', 'cosh', 'degrees', 'e', 'erf', # 'erfc', 'exp', 'expm1', 'fabs', 'factorial', 'floor', 'fmod', 'frexp', 'fsum', # 'gamma', 'gcd', 'hypot', 'inf', 'isclose', 'isfinite', 'isinf', 'isnan', # 'ldexp', 'lgamma', 'log', 'log10', 'log1p', ' log2 ',' modf ',' nan ',' phase ', #' pi ',' polar ',' pow ',' radians ',' re ct ',' sin ',' sinh ',' sqrt ',' tan ',' tanh ', #' trunc '] log10 (125) # (2.0969100130080562 + 0j)

Als u bekend bent met de wiskunde en CMATH modules, je weet al dat er een paar algemene namen zijn die in beide modules zijn gedefinieerd, maar die respectievelijk van toepassing zijn op echte en complexe getallen. 

Omdat we het CMATH module na de wiskunde module overschrijft het de functiedefinities van deze algemene functies uit de wiskunde module. Dit is waarom de eerste log10 (125) geeft een reëel getal en de tweede log10 (125) geeft een complex getal terug. Er is geen manier om de. Te gebruiken log10 () functioneer nu vanuit de wiskundemodule. Zelfs als je probeerde te typen math.log10 (125), u krijgt een NameError-uitzondering omdat wiskunde bestaat niet echt in de naamruimte.

De bottom line is dat je deze manier van importeren van functies uit verschillende modules niet zou moeten gebruiken om een ​​paar toetsaanslagen te bewaren.

  • van module importnaamA, naamB: Als u weet dat u slechts één of twee namen uit een module gaat gebruiken, kunt u deze rechtstreeks met behulp van deze methode importeren. Op deze manier kunt u de code beknopter schrijven terwijl de verontreiniging van de naamruimte tot een minimum wordt beperkt. Houd er echter rekening mee dat u nog steeds geen andere naam uit de module kunt gebruiken met module.nameZ. Elke functie met dezelfde naam in uw programma overschrijft ook de definitie van die functie die uit de module is geïmporteerd. Hierdoor wordt de geïmporteerde functie onbruikbaar. Hier is een voorbeeld van het gebruik van deze methode:
dir () # ['__builtins __' ... '__spec__'] uit wiskunde import log2, log10 dir () # ['__builtins __' ... '__spec__', 'log10', 'log2'] log10 (125) # 2.0969100130080562
  • importmodule: Dit is de veiligste en aanbevolen manier om een ​​module te importeren. Het enige nadeel is dat je de naam van de module moet prefixen naar alle namen die je in het programma gaat gebruiken. U kunt echter verontreiniging van de naamruimte voorkomen en ook functies definiëren waarvan de naam overeenkomt met de naam van de functies van de module.
dir () # ['__builtins __' ... '__spec__'] importeer math dir () # ['__builtins __' ... '__spec__', 'math'] math.log10 (125) # 2.0969100130080562

Laatste gedachten

Ik hoop dat deze tutorial je geholpen heeft naamruimten en hun belang te begrijpen. U moet nu de reikwijdte van verschillende namen in een programma kunnen bepalen en mogelijke valkuilen kunnen vermijden. 

Aarzel niet om te zien wat we te koop aanbieden en om te studeren op de markt, en aarzel niet om vragen te stellen en uw waardevolle feedback te geven met behulp van de onderstaande feed.

Het laatste deel van het artikel ging over verschillende manieren om modules in Python te importeren en de voor- en nadelen van elk van hen. Als u vragen hebt over dit onderwerp, kunt u me dit laten weten in de opmerkingen.

Leer Python

Leer Python met onze complete python-handleiding, of je nu net begint of dat je een ervaren coder bent die op zoek is naar nieuwe vaardigheden.