6. Module¶
Dacă, atunci când lucrați în terminal, părăsiți interpretorul Python-ului, veți constata, la revenirea la promptul interpretorului, că toate definițiile scrise în linia de comandă (fie ele definiții de funcții ori de variabile) s-au pierdut. Din acest motiv, atunci când vă pregătiți să scrieți un program mai lung de câteva linii de cod, ar fi mai bine să folosiți un editor de text pentru prepararea acestui program și apoi să rulați programul folosind fișierul în cauză pe post de date de intrare în interpretor. O asemenea întreprindere este numită creare de script. Pe măsură ce programul dumneavoastră se lungește, veți dori să-l împărțiți în mai multe fișiere pentru a-l întreține mai ușor. De asemeni, veți ajunge în situația de a dori să întrebuințați o funcție bine scrisă chiar de dumneavoastră în cadrul unor programe diverse fără să-i copiați, de fiecare dată, codul în vreunul din programele respective.
Pentru a vă ajuta în această privință, Python-ul dispune de o modalitate de a introduce definiții într-un fișier și apoi de a le utiliza fie într-un script fie într-o instanță interactivă a interpretorului. Un astfel de fișier este numit modul; definițiile dintr-un modul pot fi importate atât în alte module cât și în modulul main (modulul principal, adică acea colecție de variabile la care aveți acces dintr-un script executat la nivelul cel mai înalt precum și atunci când lucrați în modul calculator – bucla REPL).
Modulul constituie, așadar, un fișier conținând definiții și instrucțiuni scrise în Python. Numele fișierului este numele modulului la care adăugăm sufixul .py
. În interiorul (codului Python al) unui modul, numele modulului (sub forma unui șir de caractere) ne va fi accesibil ca valoare a variabilei globale __name__
. Ca să vedem aceasta, vă rugăm să întrebuințați editorul dumneavoastră favorit de text pentru a crea fișierul cu numele fibo.py
, situat în directorul curent și având următorul conținut:
# un modul cu numerele lui Fibonacci
def fib(n):
"""Afișează termenii șirului lui Fibonacci de până la n."""
a, b = 0, 1
while a < n:
print(a, end=' ')
a, b = b, a+b
print()
def fib2(n):
"""Returnează termenii șirului lui Fibonacci de până la n."""
rezultat = []
a, b = 0, 1
while a < n:
rezultat.append(a)
a, b = b, a+b
return rezultat
Apoi, porniți interpretorul de Python și importați modulul tocmai scris cu comanda care urmează:
>>> import fibo
Execuția comenzii nu va adăuga numele funcțiilor definite în fibo
direct la namespace curent (vezi Python Scopes and Namespaces pentru mai multe detalii); tot ce va face va fi să adauge numele fibo
spațiului de nume respectiv. Apelând la numele modulului, veți putea accesa funcțiile acestuia:
>>> fibo.fib(1000)
0 1 1 2 3 5 8 13 21 34 55 89 144 233 377 610 987
>>> fibo.fib2(100)
[0, 1, 1, 2, 3, 5, 8, 13, 21, 34, 55, 89]
>>> fibo.__name__
'fibo'
Dacă intenționați să folosiți o anumită funcție în mod frecvent, atunci îi puteți atribui unei variabile locale numele acesteia:
>>> fib = fibo.fib
>>> fib(500)
0 1 1 2 3 5 8 13 21 34 55 89 144 233 377
6.1. Mai multe despre module¶
Orice modul poate conține atât instrucțiuni executabile cât și definiții de funcții. Aceste instrucțiuni sunt folosite la inițializarea modulului. Adică, ele vor fi executate doar prima dată când numele modulului în cauză va fi întâlnit de către interpretor într-o instrucțiune de import. [1] (Instrucțiunile vor fi rulate și atunci când fișierul – modulul – va fi executat ca script.)
Fiecare modul are propriul său spațiu de nume privat, care va fi utilizat ca spațiu de nume global de către toate funcțiile definite în modulul respectiv. Astfel, autorul unui modul poate întrebuința variabile globale în codul Python al modulului fără să se teamă de eventualele conflicte de nume dintre aceste variabile și variabilele globale ale utilizatorului (modulului). Pe de altă parte, dacă sunteți siguri pe ceea ce faceți, atunci veți putea întrebuința variabilele globale ale unui modul cu aceeași notație pe care o utilizați pentru a vă referi la funcțiile modulului, și anume nume_de_modul.nume_de_item
.
Modulele pot importa alte module. Se obișnuiește, fără a fi obligatoriu, să plasăm toate instrucțiunile import
la începutul oricărui modul (respectiv, al oricărui script). Numele de module importate, dacă sunt poziționate la nivelul cel mai de sus al modulului (adică, în exteriorul oricărui cod de funcție sau de clasă din modul), vor fi adăugate spațiului de nume global al modulului.
Există o variantă a instrucțiunii import
care realizează importurile de nume dintr-un modul direct în spațiul de nume al modulului importator. Iată un exemplu de folosire a ei:
>>> from fibo import fib, fib2
>>> fib(500)
0 1 1 2 3 5 8 13 21 34 55 89 144 233 377
Varianta nu va introduce numele modulului din care facem importurile în spațiul de nume local (așa că, în exemplul anterior, fibo
nu este definit).
Dispunem chiar și de o modalitate de a importa toate numele pe care le definește un modul:
>>> from fibo import *
>>> fib(500)
0 1 1 2 3 5 8 13 21 34 55 89 144 233 377
Această variantă a instrucțiunii despre care discutăm va importa toate numele cu excepția celor care încep cu o bară jos (_
). De cele mai multe ori, programatorii de Python nu vor apela la această facilitate a limbajului deoarece ea va introduce un set necunoscut de nume în spațiul de nume folosit de interpretor, putând astfel masca o serie de chestiuni deja definite de dumneavoastră.
Să remarcăm, așadar, că obiceiul de a importa *
fie dintr-un modul fie dintr-un pachet este descurajat de profesioniști, dat fiind că el cauzează, în majoritatea situațiilor, apariția unui cod greu de citit. Cu toate acestea, este permis să îl practicați în sesiunile interactive de lucru cu interpretorul de Python, ca să aveți mai puțin de tastat.
Dacă numele unui modul este urmat de as
, atunci numele situat la dreapta lui as
va fi legat direct de modulul importat.
>>> import fibo as fib
>>> fib.fib(500)
0 1 1 2 3 5 8 13 21 34 55 89 144 233 377
Procedând astfel, importul modulului se va realiza la fel ca în urma execuției instrucțiunii import fibo
, singura deosebire fiind aceea că modulul va fi disponibil sub numele (modificat convenabil) de fib
.
Efecte similare vor fi obținute dacă procedăm în acest mod și atunci când folosim versiunea cu from
a instrucțiunii de import:
>>> from fibo import fib as fibonacci
>>> fibonacci(500)
0 1 1 2 3 5 8 13 21 34 55 89 144 233 377
Notă
Din rațiuni de eficiență, orice modul va fi importat o singură dată într-o sesiune de lucru cu interpretorul. Așa că, dacă îi aduceți modificări modulului, va trebui să reporniți interpretorul – ori, dacă este vorba doar despre un singur modul ce trebuie testat interactiv, folosiți importlib.reload()
, precum, de exemplu, în import importlib; importlib.reload(numele_modulului)
.
6.1.1. Executând module ca scripturi¶
Atunci când rulați un modul Python cu
python fibo.py <argumente>
codul modulului va fi executat ca și cum modulul ar fi fost importat, cu excepția faptului că variabila __name__
va fi setată ca "__main__"
. Ceea ce înseamnă că, dacă ați adăuga următoarele linii de cod la sfârșitul modulului dumneavoastră:
if __name__ == "__main__":
import sys
fib(int(sys.argv[1]))
atunci ați face fișierul (modulului) utilizabil atât ca script cât și ca modul importabil doarece codul în care se parsează conținutul liniei de comandă va rula numai atunci când modulul se execută ca fișier „main”:
$ python fibo.py 50
0 1 1 2 3 5 8 13 21 34
În schimb, atunci când modulul este importat, codul nu va fi rulat:
>>> import fibo
>>>
Acest procedeu este frecvent întrebuințat fie pentru a-i adăuga modulului o interfață cu utilizatorul convenabilă fie în scop de testare (atunci când, la rularea modulului ca script, va fi executată o suită de teste).
6.1.2. Calea de căutare a modulelor¶
Atunci când un modul numit cutare
este importat, interpretorul va începe prin a căuta un modul predefinit cu același nume. Numele acestor module sunt înșiruite în tuplul sys.builtin_module_names
. Dacă nu îl găsește, atunci interpretorul va căuta un fișier intitulat cutare.py
într-o listă de directoare furnizată de variabila sys.path
. sys.path
este inițializată cu una din următoarele locații:
Directorul care conține scriptul folosit ca dată de intrare la interpretor (sau directorul curent dacă nu a fost precizat niciun fișier).
PYTHONPATH
(o listă cu nume de directoare, având aceeași sintaxă ca variabila interpretorului de comenzi – de la englezescul shell –PATH
).Locația implicită, depinzând de modalitatea de instalare (incluzând, în mod convențional, un director
site-packages
, manipulată de modululsite
).
Mai multe detalii se găsesc în The initialization of the sys.path module search path.
Notă
În sistemele de fișiere care suportă legături slabe (în englezește, ca jargon, symlink), directorul care conține scriptul de intrare va fi calculat numai după ce este urmată legătura. Cu alte cuvinte, directorul care conține legătura slabă nu îi va fi adăugat căii de căutare a modulelor.
Odată inițializate, programele Python pot modifica sys.path
-ul. Directorul care conține scriptul ce este rulat va fi plasat la începutul căii de căutare, înaintea căii bibliotecii standard. Ceea ce înseamnă că scripturile din directorul în cauză vor fi încărcate în locul modulelor cu același nume din directorul bibliotecii standard. Dacă aceasta nu este intenția dumneavoastră, atunci situația constituie o eroare serioasă. A se vedea Module standard pentru mai multe informații.
6.1.3. Fișiere Python „compilate”¶
Pentru a accelera încărcarea modulelor, Python-ul salvează în directorul de cache __pycache__
versiuni compilate ale tuturor modulelor, cu numele modulul.versiunea.pyc
, în care versiunea conține codificarea formatului pentru fișierul compilat; în general, ea include numărul de versiune al Python-ului. De exemplu, în ediția (de la englezescul release) 3.3 a CPython-ului, varianta compilată a lui cutare.py ar fi salvată drept __pycache__/cutare.cpython-33.pyc
. O atare convenție de nume le permite modulelor compilate ale diverselor ediții și diferitelor versiuni ale Python-ului să coexiste.
Python-ul compară data modificării fișierelor-sursă cu cea a versiunilor compilate pentru a stabili dacă acestea din urmă sunt depășite cronologic și este nevoie să fie recompilate. Procesul de comparare și recompilare este complet automatizat. În plus, modulele compilate sunt independente de platforma de calcul, astfel că aceeași bibliotecă poate fi partajată (de la englezescul shared) cu sisteme de arhitecturi diferite.
Python-ul nu verifică zona de cache în două circumstanțe. În prima, pentru că el recompilează de fiecare dată, fără a stoca rezultatul recompilării în directorul de cache, modulul care se încarcă din linia de comandă. În cea de-a doua situație, el nu verifică zona de cache atunci când modulul nu dispune de fișier-sursă. Pentru ca Python-ul să suporte o distribuție fără cod-sursă (adică una doar compilată), modulul compilat trebuie să fie localizat în directorul-sursă și nu trebuie să existe în distribuție niciun fișier-sursă al modulului în cauză.
Ponturi pentru experți:
Puteți întrebuința opțiunile
-O
sau-OO
ale unei comenzi Python pentru a reduce mărimea unui modul compilat. Opțiunea-O
elimină instrucțiunile de aserțiune (de la englezescul assert), iar opțiunea-OO
elimină atât instrucțiunile de aserțiune cât și șirurile __doc__. Dat fiind că anumite programe se bazează pe existența acestora, ar trebui să utilizați această opțiune numai atunci când sunteți sigur pe ceea ce faceți. Modulele „optimizate” au etichetaopt-
și sunt, de obicei, mai mici (decât originalele). Edițiile viitoare ale Python-ului pot modifica efectele acestor optimizări.Niciun program nu va rula mai repede atunci când este citit dintr-un fișier
.pyc
decât atunci când este citit dintr-un fișier.py
; tot ceea ce este mai rapid în privința fișierelor.pyc
este viteza cu care sunt încărcate.Modulul
compileall
le poate crea fișiere .pyc tuturor modulelor dintr-un director.Mult mai multe detalii despre acest proces al compilării modulelor, inclusiv un arbore de decizie util, se găsesc în PEP 3147.
6.2. Module standard¶
Python-ul vine însoțit de o bibliotecă de module standard, descrisă într-un document separat, Referința bibliotecii Python (denumit „Referința bibliotecii” de aici înainte). Anumite module sunt construite ca parte a interpretorului; ele oferă acces la operații care nu fac parte din corpul (de la englezescul core) limbajului dar sunt, cu toate acestea, incluse în el, fie din rațiuni de eficiență fie pentru a-i da acces utilizatorului la primitive ale sistemului de operare precum apelurile (de) sistem. Setul acestor module este accesibil printr-o opțiune de configurare care depinde, la rândul ei, de platforma de calcul folosită. De exemplu, modulul winreg
va fi disponibil numai pe sistemele de calcul cu Windows. Un modul particular merită atenția dumneavoastră: sys
, el fiind construit ca parte a oricărui interpretor de Python. Variabilele sys.ps1
și sys.ps2
au drept valori șirurile de caractere folosite pe post de promptul primar, respectiv de promptul secundar:
>>> import sys
>>> sys.ps1
'>>> '
>>> sys.ps2
'... '
>>> sys.ps1 = 'C> '
C> print('Văleu!')
Văleu!
C>
Aceste două variabile sunt definite doar dacă interpretorul lucrează în modul interactiv.
Variabila sys.path
este o listă formată din șiruri de caractere care determină calea de căutare a modulelor. Este inițializată cu o cale de acces implicită preluată din variabila de mediu PYTHONPATH
, ori dintr-o altă cale de acces prestabilită atunci când PYTHONPATH
nu a fost setată. O puteți modifica folosind operațiile tipice cu liste:
>>> import sys
>>> sys.path.append('/ufs/guido/lib/python')
6.3. Funcția dir()
¶
Funcția predefinită dir()
este întrebuințată la aflarea numelor pe care le definește un anumit modul. Ea returnează o listă sortată de șiruri de caractere:
>>> import fibo, sys
>>> dir(fibo)
['__name__', 'fib', 'fib2']
>>> dir(sys)
['__breakpointhook__', '__displayhook__', '__doc__', '__excepthook__',
'__interactivehook__', '__loader__', '__name__', '__package__', '__spec__',
'__stderr__', '__stdin__', '__stdout__', '__unraisablehook__',
'_clear_type_cache', '_current_frames', '_debugmallocstats', '_framework',
'_getframe', '_git', '_home', '_xoptions', 'abiflags', 'addaudithook',
'api_version', 'argv', 'audit', 'base_exec_prefix', 'base_prefix',
'breakpointhook', 'builtin_module_names', 'byteorder', 'call_tracing',
'callstats', 'copyright', 'displayhook', 'dont_write_bytecode', 'exc_info',
'excepthook', 'exec_prefix', 'executable', 'exit', 'flags', 'float_info',
'float_repr_style', 'get_asyncgen_hooks', 'get_coroutine_origin_tracking_depth',
'getallocatedblocks', 'getdefaultencoding', 'getdlopenflags',
'getfilesystemencodeerrors', 'getfilesystemencoding', 'getprofile',
'getrecursionlimit', 'getrefcount', 'getsizeof', 'getswitchinterval',
'gettrace', 'hash_info', 'hexversion', 'implementation', 'int_info',
'intern', 'is_finalizing', 'last_traceback', 'last_type', 'last_value',
'maxsize', 'maxunicode', 'meta_path', 'modules', 'path', 'path_hooks',
'path_importer_cache', 'platform', 'prefix', 'ps1', 'ps2', 'pycache_prefix',
'set_asyncgen_hooks', 'set_coroutine_origin_tracking_depth', 'setdlopenflags',
'setprofile', 'setrecursionlimit', 'setswitchinterval', 'settrace', 'stderr',
'stdin', 'stdout', 'thread_info', 'unraisablehook', 'version', 'version_info',
'warnoptions']
Utilizată fără argumente, dir()
va lista numele definite în sesiunea curentă:
>>> a = [1, 2, 3, 4, 5]
>>> import fibo
>>> fib = fibo.fib
>>> dir()
['__builtins__', '__name__', 'a', 'fib', 'fibo', 'sys']
Remarcați faptul că ea listează toate tipurile de nume: pe cele de variabile, pe cele de module, de funcții șamd.
dir()
nu listează nici numele funcțiilor predefinite (în englezește, ca jargon, built-in) și nici pe cele ale variabilelor predefinite. Dacă vă interesează lista acestora, atunci aflați că ele sunt definite în modulul standard builtins
:
>>> import builtins
>>> dir(builtins)
['ArithmeticError', 'AssertionError', 'AttributeError', 'BaseException',
'BlockingIOError', 'BrokenPipeError', 'BufferError', 'BytesWarning',
'ChildProcessError', 'ConnectionAbortedError', 'ConnectionError',
'ConnectionRefusedError', 'ConnectionResetError', 'DeprecationWarning',
'EOFError', 'Ellipsis', 'EnvironmentError', 'Exception', 'False',
'FileExistsError', 'FileNotFoundError', 'FloatingPointError',
'FutureWarning', 'GeneratorExit', 'IOError', 'ImportError',
'ImportWarning', 'IndentationError', 'IndexError', 'InterruptedError',
'IsADirectoryError', 'KeyError', 'KeyboardInterrupt', 'LookupError',
'MemoryError', 'NameError', 'None', 'NotADirectoryError', 'NotImplemented',
'NotImplementedError', 'OSError', 'OverflowError',
'PendingDeprecationWarning', 'PermissionError', 'ProcessLookupError',
'ReferenceError', 'ResourceWarning', 'RuntimeError', 'RuntimeWarning',
'StopIteration', 'SyntaxError', 'SyntaxWarning', 'SystemError',
'SystemExit', 'TabError', 'TimeoutError', 'True', 'TypeError',
'UnboundLocalError', 'UnicodeDecodeError', 'UnicodeEncodeError',
'UnicodeError', 'UnicodeTranslateError', 'UnicodeWarning', 'UserWarning',
'ValueError', 'Warning', 'ZeroDivisionError', '_', '__build_class__',
'__debug__', '__doc__', '__import__', '__name__', '__package__', 'abs',
'all', 'any', 'ascii', 'bin', 'bool', 'bytearray', 'bytes', 'callable',
'chr', 'classmethod', 'compile', 'complex', 'copyright', 'credits',
'delattr', 'dict', 'dir', 'divmod', 'enumerate', 'eval', 'exec', 'exit',
'filter', 'float', 'format', 'frozenset', 'getattr', 'globals', 'hasattr',
'hash', 'help', 'hex', 'id', 'input', 'int', 'isinstance', 'issubclass',
'iter', 'len', 'license', 'list', 'locals', 'map', 'max', 'memoryview',
'min', 'next', 'object', 'oct', 'open', 'ord', 'pow', 'print', 'property',
'quit', 'range', 'repr', 'reversed', 'round', 'set', 'setattr', 'slice',
'sorted', 'staticmethod', 'str', 'sum', 'super', 'tuple', 'type', 'vars',
'zip']
6.4. Pachete¶
Pachetele constituie o modalitate de structurare a spațiului de nume al modulelor Python folosind „nume-cu-puncte de module” (de la englezescul dotted). De exemplu, numele de modul A.B
se referă la (sub)modulul B
din pachetul numit A
. Tot așa cum utilizarea modulelor îl scapă pe autorul unui modul de grija eventualelor conflicte de nume cu variabilele globale din modulul altui autor, întrebuințarea de nume-cu-puncte de module îi scapă pe autorii unor pachete multi-modul precum NumPy sau Pillow de grija numelor de module ale altor autori de pachete multi-modul.
Să presupunem că doriți să proiectați o colecție de module (adică, un „pachet”) dedicate manipulării sistematice a fișierelor de sunet și a datelor audio. Deoarece există numeroase formate de fișier de sunet (recunoscute, de obicei, după extensiile lor, cum ar fi: .wav
, .aiff
, .au
), veți avea de construit și de întreținut o colecție crescândă de module care să se ocupe cu conversia între diversele formate de fișier. De asemeni, există felurite operații pe care veți dori să le aplicați unor date audio (precum mixaje, adăugarea de ecouri, aplicarea unor funcții de egalizare, crearea unor efecte stereo artificiale), astfel că, pe lângă cele de dinainte, veți scrie în permanență noi module pentru aceste operații. Iată o structură posibilă a pachetului dumneavoastră (exprimată în stilul unui sistem ierarhic de fișiere – în englezește, ca jargon, filesystem):
sunet/ Pachetul-rădăcină
__init__.py Inițializarea pachetului de sunet
formate/ Subpachetul conversiilor de formate de fișier
__init__.py
citesc_wav.py
scriu_wav.py
citesc_aiff.py
scriu_aiff.py
citesc_au.py
scriu_au.py
...
efecte/ Subpachetul efectelor audio
__init__.py
ecou.py
înconjurător.py
inversare.py
...
filtre/ Subpachetul filtrelor
__init__.py
egalizator.py
codare_vo.py
karaoke.py
...
Atunci când importăm un pachet, Python-ul va căuta subdirectorul pachetului în lista de directoare din sys.path
.
Prezența unui fișier __init__.py
într-un director obligă Python-ul să trateze respectivul director drept pachet (un astfel de fișier lipsește dacă folosim directorul pe post de namespace package, adică o caracteristică oarecum avansată a Python-ului despre care nu discutăm chiar acum). Acest procedeu face ca directoare cu nume banale, cum ar fi string
, să nu poată conține, din pură întâmplare, module de interes ce ar trebui să fie găsite abia la locații ulterioare din calea de căutare a modulelor. În cel mai simplu caz, __init__.py
este doar un fișier gol, însă el poate include, în general, cod de inițializare a pachetului ori o setare a variabilei __all__
, la care ne vom referi mai târziu.
Utilizatorii unui pachet pot importa module individuale din acesta, ca de exemplu:
import sunet.efecte.ecou
Această instrucțiune încarcă submodulul sunet.efecte.ecou
. Ne vom referi la el numai folosindu-i numele complet.
sunet.efecte.ecou.filtru_de_ecou(intrare, ieșire, întârziere=0.7, atenuare=4)
Alt fel de a importa submodulul anterior este dat de:
from sunet.efecte import ecou
Și această instrucțiune încarcă submodulul ecou
, pe care îl face accesibil fără prefixul cu nume de pachete, deci ne permite să-l întrebuințăm după cum urmează:
ecou.filtru_de_ecou(intrare, ieșire, întârziere=0.7, atenuare=4)
Încă o variație a modului de a importa este să importăm direct fie funcția fie variabila dorită:
from sunet.efecte.ecou import filtru_de_ecou
Ca și până acum, instrucțiunea va încărca submodulul ecou
însă ne va permite să folosim funcția filtru_de_ecou()
din acesta numai cu numele ei:
filtru_de_ecou(intrare, ieșire, întârziere=0.7, atenuare=4)
Să remarcăm că, atunci când executăm instrucțiunea from pachet import element
, elementul poate fi sau un submodul (ori un subpachet) al pachetului sau orice alt nume definit în pachet, adică o funcție, o clasă ori o variabilă. Instrucțiunea import
testează mai întâi dacă elementul este definit în pachet; dacă nu, atunci va presupune că elementul este numele unui modul și va încerca să-l încarce. În cazul în care presupusul modul nu va fi găsit, va fi ridicată o excepție ImportError
.
În schimb, atunci când facem uz de o sintaxă de felul import element.sub_element.sub_sub_element
, fiecare (sub)element, cu excepția ultimului, trebuie să fie nume de pachet; ultimul element poate fi sau nume de modul sau nume de pachet însă nu și numele vreunei clase, funcții ori variabile definite în elementul precedent.
6.4.1. Importând * dintr-un pachet¶
Ce se întâmplă însă atunci când utilizatorul folosește instrucțiunea from sunet.efecte import *
? În mod ideal, putem spera că interpretorul se duce la sistemul de fișiere, găsește toate (sub)modulele prezente în pachet și le importă. O atare activitate este de așteptat să dureze mult iar importul de sub-module poate avea și efecte secundare nedorite pe care nu le-am produce dacă importul unui sub-modul oarecare ar fi realizat în mod explicit.
Singurul mod în care putem evita situațiile neplăcute este ca autorul pachetului să ofere un index explicit al acestuia. Instrucțiunea import
folosește următoarea convenție: atunci când codul-sursă al fișierului __init__.py
dintr-un pachet definește o variabilă de tip listă numită __all__
, valoarea acestei variabile va fi folosită drept lista numelor de module ce vor trebui importate atunci când interpretorul va avea de executat instrucțiunea from pachet import *
. Actualizarea listei de nume, cu ocazia lansării unei noi versiuni a pachetului, rămâne numai la latitudinea autorului acestuia. Autorii de pachete pot decide chiar și să nu întrebuințeze această facilitate atunci când nu consideră că ar fi de vreun folos să se importe * din pachetele lor. Ca exemplu, să presupunem că fișierul sunet/efecte/__init__.py
ar conține următoarea linie de cod:
__all__ = ["ecou", "înconjurător", "inversare"]
Aceasta ar însemna că, la execuția lui from sunet.efecte import *
, vor fi importate cele trei submodule mai sus numite ale pachetului sunet.efecte
.
Băgați de seamă că numele de (sub)module pot fi acoperite (de la englezescul shadowed; adică, au devenit invizibile pentru instrucțiunea curentă) de nume definite local. De exemplu, dacă ați adăugat definiția unei funcții inversare
la codul-sursă al fișierului sunet/efecte/__init__.py
, atunci execuția lui from sunet.efecte import *
va produce importul celor două submodule ecou
și înconjurător
însă nu și importul submodulului inversare
deoarece numele acestuia este acoperit de cel al funcției inversare
definită local:
__all__ = [
"ecou", # se referă la fișierul 'ecou.py'
"înconjurător", # se referă la fișierul 'înconjurător.py'
"inversare", # !!! se referă la funcția 'inversare' !!!
]
def inversare(mesaj: str): # <-- acest nume îl acoperă pe cel al submodulului 'inversare.py'
return mesaj[::-1] # dacă se execută 'from sunet.efecte import *'
Atunci când __all__
nu este definită, execuția instrucțiunii from sunet.efecte import *
nu va produce importul tuturor submodulelor din pachetul sunet.efecte
în spațiul de nume curent; tot ceea ce va face este să se asigure mai întâi că pachetul sunet.efecte
a fost importat (ceea ce poate însemna inclusiv execuția codului de inițializare găsit în __init__.py
), după care va importa toate numele definite în pachet. Aceasta va include numele (precum și submodulele încărcate explicit) din __init__.py
. Va include, desigur, și toate submodulele pachetului care au fost deja încărcate explicit prin execuția precedentelor instrucțiuni import
. Să luăm în considerare următoarele linii de cod:
import sunet.efecte.ecou
import sunet.efecte.înconjurător
from sunet.efecte import *
În exemplul de față, modulele ecou
și înconjurător
sunt importate în spațiul de nume curent deoarece ele sunt definite în pachetul sunet.efecte
la momentul execuției instrucțiunii from...import
. (Ceea ce rămâne valabil și în cazul când __all__
este definită.)
Cu toate că anumite module au fost proiectate să nu exporte decât acele nume care respectă șabloane precise atunci când dumneavoastră veți folosi instrucțiunea import *
în raport de ele, întrebuințarea acestei instrucțiuni în codul destinat producției se socotește a fi o practică greșită.
Țineți minte, nu este nimic în neregulă dacă utilizați instrucțiunea from pachet import submodulul_cutare
! De fapt, aceasta constituie chiar formularea recomandată a oricărei instrucțiuni de import, cu excepția situației în care modulul importator are de folosit submodule omonime din pachete diferite.
6.4.2. Referințe intra-pachet¶
Atunci când pachetele sunt organizate în subpachete (cum este cazul pachetului sunet
din exemplul de mai sus), puteți folosi importurile absolute pentru a vă referi la submodule din (sub)pachete înrudite. Astfel, dacă presupunem că modulul sunet.filtre.codare_vo
trebuie să utilizeze modulul ecou
din pachetul sunet.efecte
, atunci putem întrebuința instrucțiunea from sunet.efecte import ecou
.
Puteți construi și instrucțiuni de import relativ, scrise sub forma from modul import nume
. Aceste instrucțiuni de import vor folosi puncte de sine stătătoare pentru a indica fie pachetul curent fie pachetul-părinte al pachetului curent la care se referă importul relativ în cauză. Dacă scrieți cod situat, de exemplu, în modulul înconjurător
, atunci ați putea utiliza formulele:
from . import ecou
from .. import formate
from ..filtre import egalizator
Să observăm că importurile relative se raportează la numele modulului curent. Dat fiind că numele modulului principal (de la englezescul main) este întotdeauna "__main__"
, modulele pe care intenționăm să le utilizăm pe post de modul principal al vreunei aplicații Python trebuie să folosească doar importuri absolute.
6.4.3. Pachete situate în mai multe directoare¶
Pachetele dispun de încă un atribut special, și anume de __path__
. El este inițializat cu o valoare de tipul sequence de șiruri de caractere care include numele directorului în care s-a aflat fișierul __init__.py
al unui anumit pachet înainte să înceapă execuția codului Python din fișierul respectiv. Valoarea acestui atribut poate fi modificată. Modificarea ei va afecta căutările de module și de subpachete ale pachetului în discuție.
Deși este o caracteristică de care avem nevoie rar, o putem folosi cu succes la lărgirea setului de module conținute într-un pachet.
Note de subsol