Vad är Python Namnrymder (och varför behövs de?)

Namnkonflikter händer hela tiden i det verkliga livet. Till exempel hade varje skola jag någonsin åkt haft minst två elever i min klass som delade samma förnamn. Om någon kom in i klassen och bad om student X frågade vi entusiastiskt, "Vilken pratar du om? Det finns två studenter som heter X." Efter det skulle den frågande personen ge oss efternamn, och vi skulle presentera honom till höger X.

All denna förvirring och processen att bestämma den exakta personen vi pratar om genom att leta efter annan information förutom ett förnamn kunde undvikas om alla hade ett unikt namn. Detta är inte ett problem i en klass på 30 studenter. Det blir emellertid allt svårare att komma med en unik, meningsfull och lätt att komma ihåg namn för varje barn i en skola, stad, stad, land eller hela världen. En annan fråga när det gäller att ge varje barn ett unikt namn är att processen för att bestämma om någon annan har kallat sitt barn Macey, Maci eller Macie kan vara mycket tröttsamt.

En mycket liknande konflikt kan också uppstå i programmeringen. När du skriver ett program med bara 30 rader utan externa beroenden är det väldigt enkelt att ge unika och meningsfulla namn till alla dina variabler. Problemet uppstår när det finns tusentals linjer i ett program och du har också laddat några externa moduler. I den här handledningen lär du dig information om namnområden, deras betydelse och omfattningsupplösning i Python. 

Vad är namnområden?

En namnrymd är i grunden ett system för att se till att alla namn i ett program är unika och kan användas utan konflikter. Du kanske redan vet att allt i Python-liknande strängar, listor, funktioner, etc.-är ett objekt. Ett annat intressant faktum är att Python implementerar namnområden som ordböcker. Det finns en namn-till-objekt-kartläggning, med namnen som nycklar och objekten som värden. Flera namnområden kan använda samma namn och kartlägga det till ett annat objekt. Här är några exempel på namnområden:

  • Lokal namnrymd: Denna namnrymd innehåller lokala namn i en funktion. Denna namnrymd skapas när en funktion kallas, och den varar endast tills funktionen återvänder.
  • Global Namnrymd: Den här namnen innehåller namn från olika importerade moduler som du använder i ett projekt. Den skapas när modulen ingår i projektet, och den varar tills skriptet slutar.
  • Inbyggd namnrymd: Denna namnrymd innehåller inbyggda funktioner och inbyggda undantagsnamn.

I de matematiska modulerna i Python-serien på Envato Tuts + skrev jag om användbara matematiska funktioner som finns tillgängliga i olika moduler. Matematik- och cmathmodulerna har till exempel många funktioner som är vanliga för dem båda, som log10 (), Acos (), cos (), exp (), etc. Om du använder båda dessa moduler i samma program, är det enda sättet att använda dessa funktioner entydigt att prefixa dem med namnet på modulen, som math.log10 () och cmath.log10 ().

Vad är räckvidd?

Namnrymder hjälper oss att unikt identifiera alla namn i ett program. Detta innebär emellertid inte att vi kan använda ett variabelnamn någonstans vi vill ha. Ett namn har också ett räckvidd som definierar de delar av programmet där du kan använda det namnet utan att använda något prefix. Precis som namnrymden finns det också flera områden i ett program. Här är en lista över några områden som kan existera under genomförandet av ett program.

  • En lokal räckvidd, som är det innersta räckvidd som innehåller en lista över lokala namn som är tillgängliga i den aktuella funktionen.
  • En omfattning av alla omslutande funktioner. Sökningen efter ett namn börjar från närmaste omslutande räckvidd och flyttas utåt.
  • En modulnivå omfattning som innehåller alla globala namn från den aktuella modulen.
  • Det yttersta räckvidd som innehåller en lista över alla inbyggda namn. Detta räckvidd söker senast för att hitta det namn du refererade till. 

I de följande delarna av denna handledning använder vi i stor utsträckning den inbyggda Python dir () -funktionen för att returnera en lista med namn i det nuvarande lokala tillämpningsområdet. Detta hjälper dig att förstå begreppet namnområden och omfattning tydligare.

Omfattning Upplösning

Som jag nämnde i föregående avsnitt startar sökningen efter ett visst namn från den innersta funktionen och flyttar sedan högre och högre tills programmet kan kartlägga det namnet på ett objekt. När inget sådant namn finns i något av namnområdena, höjer programmet a NameError undantag.

Innan vi börjar, försök att skriva dir () i IDLE eller någon annan Python IDE.

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

Alla dessa namn anges av dir () finns i alla Python-program. För korthetens skull börjar jag hänvisa till dem som '__builtins __' ... '__spec__' i de övriga exemplen.

Låt oss se produktionen av dir () funktion efter att definiera en variabel och en funktion.

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

De dir () funktionen utmatar endast listan över namn inom det aktuella området. Det är därför inom ramen för some_func (), det finns bara ett namn som heter b_num. Kallelse dir () efter att ha definierat some_func () lägger till den i listan över namn som finns i den globala namnrymden.

Nu, låt oss se listan över namn inom några kapslade funktioner. Koden i det här blocket fortsätter från föregående block.

def outer_func (): c_num = 12 def inner_func (): d_num = 13 skriv ut (dir (), '- namn i inner_func') e_num = 14 inner_func () print (dir (), '- namn i outer_func') outer_func ) # ['d_num'] - namn i inner_func # ['c_num', 'e_num', 'inner_func'] - namn i outer_func

Ovanstående kod definierar två variabler och en funktion inom ramen för outer_func (). Inuti inner_func (), de dir () funktionen skriver endast ut namnet d_num. Detta verkar rättvist som d_num är den enda variabeln som definieras där.

Om inte uttryckligen specificeras genom att använda global, Omfördela ett globalt namn i ett lokalt namnrymd skapar en ny lokal variabel med samma namn. Detta framgår av följande kod.

a_num = 10 b_num = 11 def outer_func (): globalt a_num a_num = 15 b_num = 16 def inner_func (): globalt a_num a_num = 20 b_num = 21 skriv ut ('a_num inside inner_func:', a_num) print ('b_num inside inside_func: ', b_num) inner_func () skriv ut (' a_num inside outer_func: ', a_num) print (' b_num inside outer_func: ', b_num) outer_func () print (' a_num utanför alla funktioner: ', a_num) funktioner: ', b_num) # a_num inne inner_func: 20 # b_num inne inner_func: 21 # a_num inside outer_func: 20 # b_num inside outer_func: 16 # a_num utanför alla funktioner: 20 # b_num utanför alla funktioner: 11

Inuti båda outer_func () och inner_func (), a_num har förklarats vara en global variabel. Vi ställer bara ett annat värde för samma globala variabel. Detta är anledningen till att värdet av a_num På alla ställen är 20. Å andra sidan skapar varje funktion sin egen b_num variabel med en lokal räckvidd, och skriva ut() funktionen skriver ut värdet av denna lokalt scoped variabel.

Korrekt Importerande Moduler

Det är mycket vanligt att importera externa moduler i dina projekt för att påskynda utvecklingen. Det finns tre olika sätt att importera moduler. I det här avsnittet kommer du att lära dig om alla dessa metoder, diskutera deras fördelar och nackdelar i detalj.

  • från modulen import *: Den här metoden för att importera en modul importerar alla namn från den angivna modulen direkt i din nuvarande namnrymd. Du kan vara frestad att använda den här metoden eftersom den låter dig använda en funktion direkt utan att lägga till namnet på modulen som ett prefix. Det är dock mycket felaktigt, och du förlorar också förmågan att berätta vilken modul som faktiskt importerade den funktionen. Här är ett exempel på hur du använder den här metoden:
dir () # ['__builtins __' ... '__spec__'] från matteimport * dir () # ['__builtins __' ... '__spec__', 'acos', 'acosh', 'asin', 'asinh', # 'atan' , 'atan2', 'atanh', 'ceil', 'copysign', 'cos', 'cosh', 'grader', # 'e', ​​'erf', 'erfc', 'exp', 'expm1' "fabs", "factorial", "floor", "fmod", # "frexp", "fsum", "gamma", "gcd", "hypot", "inf", "isclose", "isfinite" 'isinf', 'isnan', 'ldexp', 'lgamma', 'logg', 'log10', 'log1p', 'log2', # 'modf', 'nan', 'pi', 'pow' radans ',' sin ',' sinh ',' sqrt ',' tan ', #' tanh ',' trunc '] log10 (125) # 2.0969100130080562 från 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', 'isgose', 'isfinite', 'isinf', 'isnan', # 'ldexp', 'lgamma', 'logg', 'log10', 'log1p' log2 ',' modf ',' nan ',' fas ', #' pi ',' polar ',' pow ',' radianer ',' re ct ',' sin ',' sinh ',' sqrt ',' tan ',' tanh ', #' trunc '] log10 (125) # (2,0969100130080562 + 0j)

Om du är bekant med matematik och CMATH moduler, vet du redan att det finns några vanliga namn som definieras i båda dessa moduler men gäller för respektive reala och komplexa nummer. 

Sedan vi har importerat CMATH modul efter matematik modulen, överstiger den funktionsdefinitionerna för dessa gemensamma funktioner från matematik modul. Det är därför den första log10 (125) returnerar ett reellt tal och den andra log10 (125) returnerar ett komplext nummer. Det finns inget sätt för dig att använda log10 () funktionen från matematikmodulen nu. Även om du försökte skriva math.log10 (125), Du kommer att få ett NameError-undantag eftersom matematik existerar faktiskt inte i navigeringsfältet.

Grunden är att du inte bör använda detta sätt att importera funktioner från olika moduler bara för att spara några tangenttryckningar.

  • från modulens importnamnA, namnB: Om du vet att du bara ska använda ett eller två namn från en modul, kan du importera dem direkt med den här metoden. På det här sättet kan du skriva koden mer kortfattat och samtidigt behålla navneutrymmet i ett minimum. Men kom ihåg att du fortfarande inte kan använda något annat namn från modulen genom att använda module.nameZ. Alla funktioner som har samma namn i ditt program kommer också att överskriva definitionen av den funktionen som importeras från modulen. Detta gör den importerade funktionen oanvändbar. Här är ett exempel på hur du använder den här metoden:
dir () # ['__builtins __' ... '__spec__'] från matematik import log2, log10 dir () # ['__builtins __' ... '__spec__', 'log10', 'log2'] log10 (125) # 2.0969100130080562
  • importmodul: Detta är det säkraste och rekommenderade sättet att importera en modul. Den enda nackdelen är att du måste prefixa namnet på modulen till alla namn som du ska använda i programmet. Du kommer emellertid att kunna undvika namnrumsföroreningar och definiera även funktioner vars namn matchar namnet på funktionerna från modulen.
dir () # ['__builtins __' ... '__spec__'] matematik dir () # ['__builtins __' ... '__spec__', 'math'] math.log10 (125) # 2.0969100130080562

Slutgiltiga tankar

Jag hoppas att denna handledning hjälpte dig att förstå namnområden och deras betydelse. Du bör nu kunna bestämma omfattningen av olika namn i ett program och undvika potentiella fallgropar. 

Tveka inte att se vad vi har till salu och studera på marknaden, och tveka inte att ställa några frågor och ge din värdefulla feedback genom att använda foderet nedan.

Den sista delen av artikeln diskuterade olika sätt att importera moduler i Python och fördelarna och nackdelarna för var och en av dem. Om du har några frågor relaterade till detta ämne, låt mig meddela mig i kommentarerna.

Lär Python

Lär dig Python med vår kompletta handledning för pythonhandledning, oavsett om du bara har börjat eller du är en erfaren kodare som vill lära dig nya färdigheter.