post

Python ro:Funcţii

Contents

Introducere

Funcţiile sunt porţiuni de program reutilizabile. Ele vă permit să daţi nume unui bloc de declaraţii şi puteţi rula acel bloc de declaraţii în program de câte ori vreţi. Asta se numeşte apel al funcţiei. Noi am folosit deja multe funcţii predefinite precum len şi range.

Conceptul de funcţie este probabil cel mai important bloc constructiv al oricărui program nonbanal (în orice limbaj de programare), deci vom explora diverse aspecte ale funcţiilor în acest capitol.

Funcţiile sunt definite folosind cuvântul cheie def. Acesta este urmat de un nume identificator pentru funcţie urmat de o pereche de paranteze care pot include nişte nume de variabile. În continuare este plasat blocul de declaraţii care compun funcţia. Un exemplu va arăta cât este de simplu:

Exemplu:

#!/usr/bin/python
# Fişier: function1.py

def sayHello():
    print('Hello World!') # blocul funcţiei
# Sfârşitul funcţiei

sayHello() # apel la funcţia sayHello()
sayHello() # din nou apel la funcţia sayHello()

Rezultat:

   $ python function1.py
   Hello World!
   Hello World!

Cum funcţionează:

Definim o funcţie numită sayHello folosind sintaxa explicată mai sus. Aceasta funcţie nu primeşte parametri şi deci nu sunt declarate variabile în paranteze. Parametrii pentru funcţie sunt doar nişte modalităţi de a-i transmite funcţiei diferite valori sau/şi de a extrage valorile corespunzătoare.

Observaţi că putem apela aceeaşi funcţie de două ori, ceea ce înseamnă că nu mai trebuie să scriem aceeaşi porţiune de cod din nou.

Parametrii funcţiilor

O funcţie poate accepta parametri, care sunt valori furnizate funcţiei pentru ca aceasta să poată face ceva cu aceste valori. Aceşti parametri sunt ca variabilele numai că valorile acestor variabile sunt definite în momentul apelului funcţiei şi deja le sunt atribuite valori la momentul executării blocului funcţiei.

Parametrii sunt specificaţi într-o pereche de paranteze în definiţia funcţiei, separate prin virgule. Când apelăm funcţia, furnizăm aceste valori într-un fel sau altul. Observaţi terminologia folosită – numele date în funcţie se numesc parametri în timp ce valorile pe care le furnizăm în apelul funcţiei se numesc argumente.

Exemplu:

#!/usr/bin/python
# Fişier: func_param.py

def printMax(a, b):
    if a > b:
        print(a, 'este maximum')
    elif a == b:
        print(a, 'este egal cu', b)
    else:
        print(b, 'este maximum')

printMax(3, 4) # argumente date prin literali

x = 5
y = 7

printMax(x, y) # argumente date prin variabile

Rezultat:

   $ python func_param.py
   4 este maximum
   7 este maximum

Cum funcţionează:

Aici definim o funcţie numită printMax care primeşte doi parametri numiţi a şi b. Găsim cel mai mare număr dintre ele folosind o simpla declaraţie if..else şi tipărim pe ecran cel mai mare număr.

În primul apel al funcţiei printMax, furnizăm argumentele în forma literală. În al doilea apel dăm funcţiei valorile parametrilor prin intermediul variabilelor. printMax(x, y) face ca valoarea variabilei x să fie atribuită parametrului a şi valoarea variabilei y să fie atribuită parametrului b. Funcţia printMax lucrează la fel în ambele cazuri.

Variabile locale

Când se declară variabile în interiorul definiţiei funcţiei, acestea nu au nici un fel de legătură cu alte variabile din afara definiţiei funcţiei, nici chiar dacă ar avea acelaşi nume, de aceea se numesc variabile locale funcţiei. Acesta este domeniul variabilei. Toate variabilele au ca domeniu blocul în care sunt declarate, începând cu punctul în care a fost definit numele ei.

Exemplu:

#!/usr/bin/python
# Fişier: func_local.py

x = 50

def func(x):
    print('x este', x)
    x = 2
    print('Am schimbat x local în ', x)

func(x)
print('x este tot ', x)

Rezultat:

   $ python func_local.py
   x este 50
   Am schimbat x local în 2
   x este tot 50

Cum funcţionează:

În funcţie, prima dată când folosim valoarea numelui x, Python foloseşte valoarea parametrului declarat în funcţie.

În continuare atribuim valoarea 2 lui x. Numele x este local funcţiei noastre. Prin urmare, când schimbăm valoarea lui x în funcţie, x definit în blocul principal rămâne neafectat.

În ultimul apel al funcţiei print, afişăm valoarea lui x din blocul principal ca să confirmăm că a rămas neafectată.

Folosirea declaraţiei global

Dacă vreţi să atribuiţi o valoare unui nume definit la nivelul cel mai înalt al programului (adică nu în interiorul domeniului funcţiei sau clasei), va trebui să-i spuneţi lui Python că acel nume nu este local ci global. Obţinem asta folosind declaraţia global. Este imposibil ca în interiorul unei funcţii să atribui o valoare unei variabile definite în afara funcţiei fără declaraţia global.

Puteţi folosi valorile definite în afara funcţiilor (presupunând că nu există o variabilă cu acelaşi nume definită în blocul funcţiei). Totuşi, acest fapt nu este încurajat şi trebuie evitat întrucât devine neclar cititorului unde este definiţia acelei variabile. Folosind declaraţia global marcăm foarte clar că variabila este definită în cel mai exterior bloc.

Exemplu:

#!/usr/bin/python
# Fişier: func_global.py

x = 50

def func():
    global x

    print('x is', x)
    x = 2
    print('Am schimbat x global în ', x)

func()
print('Valoarea lui x este', x)

Rezultat:

   $ python func_global.py
   x este 50
   Am schimbat x global în 2
   Valoarea lui x este 2

Cum funcţionează:

Declaraţia global este folosită pentru a declara că x este o variabilă globală – de aceea, când atribuim o valoare lui x în interiorul funcţiei, acea schimbare se reflectă când folosim valoarea lui x în blocul principal.

Puteţi specifica mai multe variabile globale folosind declaraţia global. De exemplu, global x, y, z.

Folosirea declaraţiei nonlocal

Am învăţat să accesăm variabile în domeniul local şi global. Mai există un domeniu specific funcţiilor, numit “nonlocal” şi care se află între cele două. Domeniile nonlocal se observă când definiţi funcţii în interiorul funcţiilor.

Întrucât totul în Python este cod executabil, se pot defini funcţii oriunde.

Să luăm un exemplu:

#!/usr/bin/python
# Fişier: func_nonlocal.py

def func_outer():
    x = 2
    print('x este', x)

    def func_inner():
        nonlocal x
        x = 5

    func_inner()
    print('x local a devenit ', x)

func_outer()

Rezultat:

   $ python func_nonlocal.py
   x este 2
   x local a devenit 5

Cum funcţionează:

Când ne aflăm în interiorul unei funcţii func_inner, ‘x’ definit în prima linie a funcţiei func_outer este undeva între global şi local. Declarăm că folosim acest x cu declaraţia nonlocal x şi astfel obţinem acces la acea variabilă.

Încercaţi să schimbaţi nonlocal x cu global x şi să eliminaţi complet declaraţia, ca să vedeţi ce diferenţe de comportament sunt în aceste cazuri.

Valori implicite ale argumentelor

Pentru unele funcţii, poate vreţi să faceţi unii parametri opţionali şi să folosiţi valori implicite în cazul în care utilizatorul nu furnizează o valoare pentru parametrul respectiv. Asta se face cu ajutorul valorilor implicite ale parametrilor. Puteţi specifica valorile implicite ale argumentelor în definiţia funcţiei, punând operatorul de atribuire (=) urmat de valoarea implicită.

Observaţi că valoarea implicită a argumentului trebuie să fie o constantă. Mai precis, trebuie să fie imuabilă – acest fapt va fi explicat în detaliu în capitolele următoare. Pentru moment reţineţi doar atât.

Exemplu:

#!/usr/bin/python
# Fişier: func_default.py

def say(mesaj, ori = 1):
    print(mesaj * ori)

say('Hello')
say('World', 5)

Rezultat:

   $ python func_default.py
   Hello
   WorldWorldWorldWorldWorld

Cum funcţionează:

Funcţia numită say este folosită pentru a tipări pe ecran un şir de atâtea ori cât se specifică. Dacă nu furnizăm acea valoare, atunci va fi folosită valoarea implicită, 1. Obţinem aceasta punând valoarea implicită 1 a parametrului ori.

La prima folosire a funcţiei say, dăm numai şirul şi ea îl tipăreşte o dată. În a doua folosire dăm funcţiei say şi şirul şi un argument 5 ceea ce spune că vrem să fie tipărit şirul de 5 ori.

Important
Numai parametrii de la sfârşitul listei de parametri pot avea valori implicite deci nu puteţi avea un parametru cu valoare implicită înaintea altuia fără valoare implicită în lista de parametri a funcţiei.
Motivul este că valorile sunt atribuite parametrilor prin poziţie. De exemplu, def func(a, b=5) este validă, dar def func(a=5, b) este invalidă.

Argumente cuvânt cheie

Dacă aveţi funcţii cu mulţi parametri şi vreţi să specificaţi numai pe unii, atunci puteţi să daţi valori parametrilor prin numele lor – acest mod de specificare se numeşte prin argumente cuvânt cheie – folosim cuvântul cheie (engl. keyword) în locul poziţiei (pe care am folosit-o până acum) pentru a specifica argumente funcţiei.

Există două avantaje – unu, folosirea funcţiei este mai uşoară, întrucât nu trebuie să ne preocupăm de ordinea parametrilor. Doi, putem da valori numai unor parametri selectaţi, cu condiţia ca toţi ceilalţi sa aibă în definiţia funcţiei valori implicite.

Exemplu:

#!/usr/bin/python
# Fişier: func_key.py

def func(a, b=5, c=10):
    print('a este', a, 'şi b este', b, 'şi c este', c)

func(3, 7)
func(25, c=24)
func(c=50, a=100)

Rezultat:

   $ python func_key.py
   a este 3 şi b este 7 şi c este 10
   a este 25 şi b este 5 şi c este 24
   a este 100 şi b este 5 şi c este 50

Cum funcţionează:

Funcţia numită func are un parametru fără valoare implicită, urmat de doi parametri cu valori implicite.

În primul apel, func(3, 7), parametrul a primeşte valoarea 3, parametrul b primeşte valoarea 7, iar c valoarea implicită, 10.

În al doilea apel, func(25, c=24), variabila a ia valoarea 25 datorită poziţiei argumentului. Pe urmă, parametrul c ia valoarea 24 prin nume – argument cuvânt cheie. Variabila b ia valoarea implicită, 5.

În al treilea apel, func(c=50, a=100), folosim numai tehnica nouă, a cuvintelor cheie. Observaţi, specificăm valoarea parametrului c înaintea parametrului a deşi a este definit înaintea variabilei c în definiţia funcţiei.

Parametri VarArgs

TODO
Să scriu despre asta într-un capitol următor, fiindcă nu am vorbit încă despre liste şi dicţionare?

Uneori aţi putea dori să definiţi o funcţie care să ia orice număr de parametri, asta se poate face folosind asteriscul:

#!/usr/bin/python
# Fişier: total.py

def total(iniţial=5, *numere, **keywords):
    numărător = iniţial
    for număr in numere:
        numărător += număr
    for cheie in keywords:
        numărător += keywords[cheie]
    return numărător

print(total(10, 1, 2, 3, legume=50, fructe=100))

Rezultat:

   $ python total.py
   166

Cum funcţionează:

Când declarăm un parametru cu asterisc precum *parametri, toţi parametrii poziţionali de la acel punct încolo sunt colectaţi întro listă numită ‘parametri’.

Similar, când declarăm un parametru cu două asteriscuri, precum **parametri, toate argumentele cuvânt cheie de la acel punct încolo sunt colectate într-un dicţionar numit ‘parametri’.

Vom explora listele şi dicţionarele întrun capitol următor.

Parametri exclusiv cuvânt cheie

Dacă vrem să specificăm anumiţi parametri cuvânt cheie pentru a fi disponibili numai în forma cuvânt cheie şi niciodată ca parametri poziţionali, aceştia pot fi declaraţi după un parametru cu asterisc:

#!/usr/bin/python
# Fişier: keyword_only.py

def total(iniţial=5, *numere, legume):
    numărător = iniţial
    for număr in numere:
        numărător += număr
    numărător += legume
    return numărător

print(total(10, 1, 2, 3, legume=50))
print(total(10, 1, 2, 3))
# Ridică o eroare pentru că nu am furnizat o valoare implicită pentru 'legume'

Rezultat:

   $ python keyword_only.py
   66
   Traceback (most recent call last):
     File "test.py", line 12, in <module>
   print(total(10, 1, 2, 3))
   TypeError: total() needs keyword-only argument legume

Cum funcţionează:

Declararea de parametri după un parametru cu asterisc (engl. starred parameter) produce argumente exclusiv cuvânt cheie. Dacă acestea nu sunt definite cu valori implicite, apelurile funcţiei vor ridica o eroare dacă nu se furnizează argumentul cuvânt cheie, aşa cum s-a văzut mai sus.

Dacă vreţi să aveţi parametri exclusiv cuvânt cheie, dar nu aveţi nevoie de nici un parametru cu asterisc, folosiţi un asterisc izolat, ca în exemplul:

def total(iniţial=5, *, legume).

Declaraţia return

Declaraţia return este folosită pentru a ne întoarce dintr-o funcţie (deci a evada din ea – engl. break out). Opţional putem întoarce o valoare la fel de bine.

Exemplu:

#!/usr/bin/python
# Fişier: func_return.py

def maximum(x, y):
    if x > y:
        return x
    else:
        return y

print(maximum(2, 3))

Rezultat:

   $ python func_return.py
   3

Cum funcţionează:

Funcţia maximum întoarce parametrul cel mai mare furnizat funcţiei. Ea foloseşte o declaraţie simplă if..else pentru a găsi numărul cel mai mare şi apoi întoarce (engl. return) acea valoare.

Observaţi că declaraţia return fără o valoare este echivalentă cu return None. None este un tip special în Python care reprezintă nimicul. De exemplu, este folosit pentru a indica faptul că o variabilă nu are nici o valoare, deci are valoarea None.

Orice funcţie, în mod implicit, conţine o declaraţie return None la sfârşitul blocului de declaraţii, cu excepţia cazului în care îi scrieţi o altă declaraţie return. Puteţi vedea asta rulând print o_funcţie_oarecare() în care nu este dată o declaraţie return precum:

def o_functie_oarecare():
    pass

Declaraţia pass este folosită în Python pentru a indica un bloc de declaraţii gol.

Notă
Există o funcţie predefinită numită max care implementează această funcţionalitate de a ‘găsi maximul’, deci folosirea aceste funcţii este posibilă oricand.

DocStrings

Python are o facilitate drăguţă numită documentation strings, numită de obicei pe numele scurt docstrings. DocStrings (rom. şiruri de documentaţie, sg. docstring) sunt o unealtă importantă pentru că vă ajută să documentaţi programele mai bine şi le face mai uşor de înţeles. Uimitor, putem chiar să extragem şirurile de documentare ale, să zicem, unei funcţii chiar în timp ce programul rulează!

Exemplu:

#!/usr/bin/python
# Fişier: func_doc.py

def printMax(x, y):
    '''Tipăreşte pe ecran cel mai mare din două numere.

    Cele două numere trebuie să fie întregi.'''
    x = int(x) # converteşte în integer, dacă este posibil
    y = int(y)

    if x > y:
        print(x, 'este maximum')
    else:
        print(y, 'este maximum')

print(printMax.__doc__)
printMax(3, 5)

Rezultat:

   $ python func_doc.py
   Tipăreşte pe ecran cel mai mare din două numere.

       Cele două numere trebuie să fie întregi.
   5 este maximum

Cum funcţionează:

Un şir pe prima linie logică a funcţiei devine docstring pentru acea funcţie. De retinut că DocStrings se aplică şi la module şi clase, despre care vom învăţa în capitolele respective.

Convenţia urmată pentru un docstring este: un şir multilinie în care prima linie începe cu majusculă şi se încheie cu punct. Apoi linia a doua este goală şi urmată de o explicaţie mai detaliată începand cu linia a treia. Vă sfătuim cu căldură să urmaţi aceasta convenţie pentru toate docstringurile tuturor funcţiilor nebanale pe care le scrieţi.

Putem accesa docstringul funcţiei printMax folosind atributul __doc__ (observaţi dublu underscore) al funcţiei. Amintiţi-vă că Python tratează totul ca obiect, inclusiv funcţiile. Vom învăţa mai mult despre obiecte în capitolul despre clase.

Daca aţi folosit help() în Python, aţi văzut deja cum se foloseşte docstring! Ceea ce face ea este că extrage atributul __doc__ al funcţiei şi îl afişează într-o maniera convenabilă. Puteţi încerca asta asupra funcţiei de mai sus – includeţi pur şi simplu declaraţia help(printMax) în program. Nu uitaţi să tastaţi q pentru a ieşi din help.

Utilitarele pot colecta automat documentaţia din programe în această maniera. De aceea vă recomand insistent să folosiţi docstring pentru orice funcţie nebanală pe care o scrieţi. Comanda pydoc inclusă în distribuţia Python funcţionează similar cu help() folosind docstringurile.

Adnotări

Funcţiile mai au o facilitate avansată numită adnotare (engl. annotations) care este o cale deşteaptă de a ataşa informaţie pentru fiecare din parametri precum şi pentru valoarea întoarsă. Întrucât limbajul Python în sine nu interpretează aceste adnotări în nici un fel (această funcţionalitate este lăsată bibliotecilor third-party să interpreteze ele în ce fel vor), vom trece peste această facilitate în discuţia noastră. Dacă sunteţi interesaţi despre adnotări, puteţi citi PEP No. 3107.

Rezumat

Am discutat multe aspecte ale funcţiilor, dar reţineţi că nu am acoperit toate aspectele posibile. Totuşi, am acoperit deja majoritatea aspectelor pe care le vom folosi în mod uzual.

Vom afla în continuare cum să folosim, dar şi să cream module Python.