6. As funções

6.1. Script [fonc_01]: escopo das variáveis
O script [fonc_01] apresenta exemplos de escopo de variáveis entre funções:
# escopo das variáveis
def f1():
# deve-se evitar o uso de variáveis globais
# variável global i
global i
i += 1
# variável local j
j = 10
print(f"f1[i,j]=[{i},{j}]")
def f2():
# deve-se evitar o uso de variáveis globais
# variável global i
global i
i += 1
# variável local j
j = 20
print(f"f2[i,j]=[{i},{j}]")
def f3():
# variável local i
i = 1
# variável local j
j = 30
print(f"f3[i,j]=[{i},{j}]")
# programa principal
i = 0
j = 0
# essas duas variáveis só serão conhecidas por uma função f
# a menos que esta declare explicitamente, por meio da instrução `global`, que deseja utilizá-las
# ou que a função utilize a variável global apenas para leitura
f1()
f2()
f3()
# j não mudou, mas i mudou
print(f"[i,j]=[{i},{j}]")
Resultados
C:\Data\st-2020\dev\python\cours-2020\python3-flask-2020\venv\Scripts\python.exe C:/Data/st-2020/dev/python/cours-2020/python3-flask-2020/fonctions/fonc_01.py
f1[i,j]=[1,10]
f2[i,j]=[2,20]
f3[i,j]=[1,30]
[i,j]=[2,0]
Process finished with exit code 0
Notas:
- o script mostra o uso da variável i, declarada como global nas funções f1 e f2. Nesse caso, o programa principal e as funções f1 e f2 compartilham a mesma variável i.
6.2. Script [fonc_02]: escopo das variáveis
O script [fonc_03] retoma o script [fonc_02] e mostra como evitar o uso de variáveis globais:
# escopo das variáveis
def f1(i):
# variável local i
i += 1
# variável local j
j = 10
print(f"f1[i,j]=[{i},{j}]")
# retornamos o valor alterado
return i
def f2(i):
# variável local i
i += 1
# variável local j
j = 20
print(f"f2[i,j]=[{i},{j}]")
# retorna o valor modificado
return i
def f3():
# variável local i
i = 1
# variável local j
j = 30
print(f"f3[i,j]=[{i},{j}]")
# programa principal
i = 0
j = 0
# essas duas variáveis só serão conhecidas por uma função f
# a menos que esta declare explicitamente, por meio da instrução global, que deseja utilizá-las
# ou se a função utilizar a variável global apenas para leitura
i = f1(i)
i = f2(i)
f3()
# j não mudou, mas i mudou
print(f"[i,j]=[{i},{j}]")
Comentários:
- linhas 2, 12: em vez de ser declarada como global, a variável [i] é passada como parâmetro para as funções f1 e f2;
- linhas 9, 19: as funções f1 e f2 devolvem a variável [i] alterada ao programa principal. Este a recupera nas linhas 36 e 37;
Resultados
C:\Data\st-2020\dev\python\cours-2020\python3-flask-2020\venv\Scripts\python.exe C:/Data/st-2020/dev/python/cours-2020/python3-flask-2020/fonctions/fonc_02.py
f1[i,j]=[1,10]
f2[i,j]=[2,20]
f3[i,j]=[1,30]
[i,j]=[2,0]
Process finished with exit code 0
6.3. Script [fonc_03]: escopo das variáveis
O script [fonc_03] mostra uma particularidade das variáveis utilizadas tanto em uma função quanto no código que a chama, dependendo se, na função, essa variável é utilizada apenas para leitura ou não.
def f1():
# aqui, a variável global i é conhecida
print(f"[f1] i={i}")
# aqui, a variável global j é conhecida
print(f"[f1] j={j}")
def f2():
# aqui, a variável global i não é conhecida
# pois a função f2 define uma variável local com o mesmo nome
# portanto, é ela que tem prioridade
try:
# tentativa de exibir a variável local i definida mais adiante
print(f"[f2] i={i}")
# a instrução a seguir define i como uma variável local da função f2
i = 7
except BaseException as erreur:
print(f"[f2] erreur={erreur}")
def f3():
# aqui, a variável global i não é reconhecida
# pois a função f3 define uma variável local com o mesmo nome
# portanto, é ela que tem prioridade
# a instrução a seguir define i como uma variável local
i = 7
# exibição — aqui, i é conhecida
print(f"[f3] i={i}")
# main -----------
# variáveis globais nas funções
i = 10
j = 20
# chamada de f1
f1()
print(f"[main] i={i}, j={j}")
# chamada de f2
f2()
print(f"[main] i={i}")
# chamada de f3
f3()
print(f"[main] i={i}")
Notas
- linha 34: o código principal define uma variável [i];
- linhas 1-5: a função f1 também utiliza uma variável [i] sem atribuir-lhe um valor. Trata-se de uma leitura da variável [i]. Nesse caso, a variável [i] utilizada é a do código chamador, linha 34;
- linhas 8-18: a função f2 também utiliza uma variável [i], mas atribui um valor a ela na linha 16. O fato de atribuir um valor à variável [i] em f2 torna automaticamente [i] uma variável local da função [f2]. Essa variável, portanto, “oculta” a variável [i] do código chamador;
- linha 14: a operação de gravação da variável local [i] falhará, pois ela não possui valor ao chegar à linha 14. Ela recebe seu valor na linha 16. Ocorrerá uma exceção. Por esse motivo, inserimos a linha 14 em um try/catch;
- linhas 21-29: a função f3 faz o mesmo que a função f2, mas define mais cedo sua variável local [i];
Resultados
C:\Data\st-2020\dev\python\cours-2020\python3-flask-2020\venv\Scripts\python.exe C:/Data/st-2020/dev/python/cours-2020/python3-flask-2020/fonctions/fonc_03.py
[f1] i=10
[f1] j=20
[main] i=10, j=20
[f2] erreur=local variable 'i' referenced before assignment
[main] i=10
[f3] i=7
[main] i=10
Process finished with exit code 0
6.4. Script [fonc_04]: modo de passagem de parâmetros
O script é o seguinte:
# função f1
def f1(a):
a = 2
# função f2
def f2(a, b):
a = 2
b = 3
return a, b
# ------------------------ main
x = 1
f1(x)
print(f"x={x}")
(x, y) = (-1, -1)
(x, y) = f2(x, y)
print(f"x={x}, y={y}")
Resultados
Notas:
- tudo é um objeto em Python. Alguns objetos são chamados de “imutáveis” (em inglês): não é possível alterá-los. É o caso de números, cadeias de caracteres e tuplas. Quando objetos Python são passados como parâmetros para funções, são suas referências que são passadas, a menos que esses objetos sejam “imutáveis”; nesse caso, é o valor do objeto que é passado;
- as funções f1 (linha 2) e f2 (linha 7) têm como objetivo ilustrar a passagem de um parâmetro de saída. Queremos que o parâmetro efetivo de uma função seja modificado pela própria função;
- linhas 2-3: a função f1 modifica seu parâmetro formal a. Queremos saber se o parâmetro efetivo também será modificado;
- linhas 14-15: o parâmetro efetivo é x=1. A linha 2 dos resultados mostra que o parâmetro efetivo não é alterado. Portanto, o parâmetro efetivo x e o parâmetro formal a são dois objetos diferentes;
- linhas 8-10: a função f2 modifica seus parâmetros formais a e b e os retorna como resultados;
- linhas 17-18: os parâmetros efetivos (x, y) são passados para f2, e o resultado de f2 é atribuído a (x, y). A linha 3 dos resultados mostra que os parâmetros efetivos (x, y) foram alterados.
Conclui-se que, quando objetos “imutáveis” são parâmetros de saída, eles devem fazer parte dos resultados retornados pela função.
6.5. Script [fonc_05]: ordem de execução das funções em um script
O script [fonc_05] mostra que não é possível chamar uma função se ela não tiver sido encontrada anteriormente no código:
# ------------------------ main
print(f2(100, 200))
# função f1
def f1(a):
return a + 10
# função f2
def f2(a, b):
return f1(a + b)
Observações
- a linha 2 causará um erro porque utiliza a função f2, que ainda não foi definida no script;
Resultados
6.6. Script [fonc_06]: ordem de escrita das funções em um script
O script [fonc_06] mostra que o que se aplica ao código chamador não se aplica às funções:
# função f2
def f2(a, b):
return f1(a + b)
# função f1
def f1(a):
return a + 10
# ------------------------ main
print(f2(100, 200))
Notas
- linha 3: a função [f2] utiliza a função [f1] definida mais adiante no script. No entanto, isso não causa nenhum erro. Portanto, pode-se concluir que a ordem de definição das funções em um script Python não tem importância;
Resultados
6.7. Script [fonc_07]: uso de módulos
O script [fonc_07] mostra como isolar funções em um módulo.

Isolamos funções reutilizáveis em um módulo. Em vez de transportá-las de um script para outro:
- colocamo-las em um arquivo separado, que declaramos de uma maneira específica;
- os scripts que precisam dessas funções “importam” o módulo que as contém;
O script [fonctions_module_01] é o seguinte:
# função f2
def f2(a, b):
return f1(a + b)
# função f1
def f1(a):
return a + 10
Para que as funções do script [fonctions_module_01] possam ser referenciadas por outros scripts, há diferentes maneiras de proceder. Elas variam dependendo se o script é executado ou não dentro do [PyCharm].
No [PyCharm], os módulos importados são procurados em pastas específicas chamadas [Sources Root]. Há duas maneiras de transformar uma pasta em um [Sources Root]:

- no [4], a pasta mudou de cor;
Após essa operação, a pasta [fonctions/modules] é reconhecida como uma pasta de origem. É possível, então, escrever em um script:
Para importar/utilizar a função f2 definida no módulo [fonctions_module_01.py].
![]() |
Outro método é utilizar as propriedades do projeto:
- acima, a sequência [1-6] permite transformar a pasta [shared] em uma pasta para armazenar módulos a serem importados;
Por enquanto, não vamos declarar nenhuma pasta como [Sources Root], exceto a raiz do projeto:

Feito isso, podemos escrever o seguinte script [fonc-07]:
# uso de módulos
import sys
# ------------------------ main
print(f"Python path={sys.path}")
from fonctions.shared.fonctions_module_01 import f2
print(f2(100, 200))
- linha 2: importa-se o objeto [sys] para poder utilizar, na linha 5, seu atributo [path], que fornece o que é chamado de [Python Path]: uma lista de pastas que serão exploradas em busca de módulos importados;
- linha 6: importa-se a função f2 do módulo [fonctions_module_01]. Para designar esse módulo, utiliza-se o caminho que vai da raiz do projeto até o módulo. No PyCharm, a raiz do projeto sempre faz parte das pastas exploradas quando se procura um módulo importado em um script. Essa pasta, portanto, faz parte do [Python Path] do projeto. É isso que a linha 5 nos permitirá verificar;
- linha 6: se descrevêssemos o caminho que vai da raiz do projeto até a pasta [fonctions_module_01], escreveríamos [fonctions/shared/fonctions_module_01]. No caminho de um módulo, o sinal / é substituído pelo ponto. Portanto, escrevemos [fonctions.modules.fonctions_module_01];
- após a linha 6, a função f2 já é conhecida. Ela é utilizada na linha 8;
Resultados
C:\Data\st-2020\dev\python\cours-2020\python3-flask-2020\venv\Scripts\python.exe C:/Data/st-2020/dev/python/cours-2020/python3-flask-2020/fonctions/fonc_07.py
Python path=['C:\\Data\\st-2020\\dev\\python\\cours-2020\\python3-flask-2020\\fonctions', 'C:\\Data\\st-2020\\dev\\python\\cours-2020\\python3-flask-2020', 'C:\\Data\\st-2020\\dev\\python\\cours-2020\\python3-flask-2020\\fonctions\\shared', 'C:\\myprograms\\Python38\\python38.zip', 'C:\\myprograms\\Python38\\DLLs', 'C:\\myprograms\\Python38\\lib', 'C:\\myprograms\\Python38', 'C:\\Data\\st-2020\\dev\\python\\cours-2020\\python3-flask-2020\\venv', 'C:\\Data\\st-2020\\dev\\python\\cours-2020\\python3-flask-2020\\venv\\lib\\site-packages']
310
Process finished with exit code 0
Acima:
- destacado em verde, vemos que a raiz do projeto faz parte do [Python Path];
- destacado em amarelo, percebe-se que a pasta do script executado também faz parte do [Python Path];
- os demais elementos do [Python Path] provêm diretamente da pasta de instalação do Python;
O que acontece quando não se usa o PyCharm para executar o [fonc-07]?


- no [1], executa-se o script [fonc-07]. Estamos na pasta [fonctions];
- no [2], percebe-se que a pasta de execução faz parte do [Python Path]. É sempre assim. Também é possível observar que a pasta raiz [C:\\Data\\st-2020\\dev\\python\\cours-2020\\python3-flask-2020] não faz parte do [Python Path];
- em [3], o interpretador Python informa que não encontra o módulo [fonctions];
Para procurar o módulo importado [fonctions.shared.fonctions_module_01], o interpretador Python procura nas pastas do [Python Path] por uma subpasta chamada [fonctions]. Ele não o encontra em lugar algum. De fato, a subpasta [fonctions] está dentro da pasta [C:\Data\st-2020\dev\python\cours-2020\python3-flask-2020], que não faz parte do [Python Path].
O script [fonc-08] oferece uma solução possível para esse problema.
6.8. Script [fonc_08]: adicionar pastas ao [Python Path]
É possível modificar o [Python Path] por meio de programação, conforme mostra o script [fonc-08]:
# uso de módulos
import os
import sys
# pasta do script
script_dir = os.path.dirname(os.path.abspath(__file__))
# Python Path antes da modificação
print(f"Python path avant={sys.path}")
# adicionamos a pasta [shared] ao Python Path
sys.path.append(f"{script_dir}/shared")
# Python Path após a modificação
print(f"Python path après={sys.path}")
# import f2
from fonctions_module_01 import f2
# ------------------------ main
print(f2(100, 200))
Notas
- linha 4: a variável especial [__file__] é o nome do script que está sendo executado. Dependendo do contexto de execução, esse nome pode ser absoluto (Pycharm) ou relativo (console). A função [os.path.abspath] retorna o nome absoluto do arquivo cujo nome é passado a ela. A função [os.path.dirname] retorna o nome absoluto da pasta que contém o arquivo cujo nome é passado a ela;
- linha 10: [sys.path] é uma matriz que contém os nomes das pastas a serem exploradas quando um módulo é procurado. Adiciona-se a essa matriz a raiz do projeto definida na linha 4;
- exibimos o [Python Path] antes (linha 8) e depois (linha 12) da modificação;
- linha 15: importa-se o módulo [fonctions_module_01], que contém a função f2;
A execução no PyCharm produz os seguintes resultados:
C:\Data\st-2020\dev\python\cours-2020\python3-flask-2020\venv\Scripts\python.exe C:/Data/st-2020/dev/python/cours-2020/python3-flask-2020/fonctions/fonc_08.py
Python path avant=['C:\\Data\\st-2020\\dev\\python\\cours-2020\\python3-flask-2020\\fonctions', 'C:\\Data\\st-2020\\dev\\python\\cours-2020\\python3-flask-2020', 'C:\\Data\\st-2020\\dev\\python\\cours-2020\\python3-flask-2020\\fonctions\\shared', 'C:\\myprograms\\Python38\\python38.zip', 'C:\\myprograms\\Python38\\DLLs', 'C:\\myprograms\\Python38\\lib', 'C:\\myprograms\\Python38', 'C:\\Data\\st-2020\\dev\\python\\cours-2020\\python3-flask-2020\\venv', 'C:\\Data\\st-2020\\dev\\python\\cours-2020\\python3-flask-2020\\venv\\lib\\site-packages']
Python path après=['C:\\Data\\st-2020\\dev\\python\\cours-2020\\python3-flask-2020\\fonctions', 'C:\\Data\\st-2020\\dev\\python\\cours-2020\\python3-flask-2020', 'C:\\Data\\st-2020\\dev\\python\\cours-2020\\python3-flask-2020\\fonctions\\shared', 'C:\\myprograms\\Python38\\python38.zip', 'C:\\myprograms\\Python38\\DLLs', 'C:\\myprograms\\Python38\\lib', 'C:\\myprograms\\Python38', 'C:\\Data\\st-2020\\dev\\python\\cours-2020\\python3-flask-2020\\venv', 'C:\\Data\\st-2020\\dev\\python\\cours-2020\\python3-flask-2020\\venv\\lib\\site-packages', 'C:\\Data\\st-2020\\dev\\python\\cours-2020\\python3-flask-2020\\fonctions/shared']
310
Process finished with exit code 0
- linha 3: percebe-se que a pasta [shared] aparece duas vezes na pasta [Python Path]. É possível evitar isso, mas, neste caso, não causa nenhum problema;
- linha 4: a função f2 foi executada corretamente;
Agora, vamos executar o [fonc-08] em um terminal:
(venv) C:\Data\st-2020\dev\python\cours-2020\python3-flask-2020\fonctions>python fonc_08.py
Python path avant=['C:\\Data\\st-2020\\dev\\python\\cours-2020\\python3-flask-2020\\fonctions', 'C:\\myprograms\\Python38\\python38.zip', 'C:\\myprograms\\Python38\\DLLs', 'C:\\myprograms\\Python38\\lib', 'C:\\myprograms\\Python38', 'C:\\Data\\st-2020\\dev\\python\\cours-2020\\python3-flask-2020\\venv', 'C:\\Data\\st-2020\\dev\\python\\cours-2020\\python3-flask-2020\\venv\\lib\\site-packages']
Python path après=['C:\\Data\\st-2020\\dev\\python\\cours-2020\\python3-flask-2020\\fonctions', 'C:\\myprograms\\Python38\\python38.zip', 'C:\\myprograms\\Python38\\DLLs', 'C:\\myprograms\\Python38\\lib', 'C:\\myprograms\\Python38', 'C:\\Data\\st-2020\\dev\\python\\cours-2020\\python3-flask-2020\\venv', 'C:\\Data\\st-2020\\dev\\python\\cours-2020\\python3-flask-2020\\venv\\lib\\site-packages', 'C:\\Data\\st-2020\\dev\\python\\cours-2020\\python3-flask-2020\\fonctions/shared']
310
- linha 2: assim como antes, a pasta [shared] não está no [Python Path];
- linha 3: agora ela está lá;
- linha 4: a função f2 foi encontrada;
6.9. Script [fonc_09]: declaração do tipo dos parâmetros
O script [fonc_09] mostra que é possível declarar o tipo dos parâmetros de uma função, bem como o do resultado. No entanto, essa declaração serve apenas para fins de documentação da função. O interpretador Python não verifica se os parâmetros efetivos da função realmente têm o tipo esperado. No entanto, o PyCharm sinaliza as inconsistências de tipo entre os parâmetros efetivos e formais. Esse motivo por si só já é suficiente para tornar as declarações de tipo indispensáveis.
O script é o seguinte:
# uma função com indicação do tipo dos parâmetros
# isso serve apenas como documentação, pois o interpretador do Python não leva isso em consideração
def show(param: int) -> int:
print(f"param={param}, type(param)={type(param)}")
return param + 1
# main -------------------------
print(show(4))
show("xyz")
Notas:
- linha 5: declara-se que o parâmetro formal [param] é do tipo [int] e que o resultado da função também é do tipo [int];
- linha 11: o parâmetro efetivo da função [show] tem o tipo correto;
- linha 12: o parâmetro efetivo da função [show] não tem o tipo correto;
Resultados
- linha 10: o tipo do parâmetro [param] é do tipo [str]. Quando essa mensagem é exibida, já se está dentro do código da função [show]. O interpretador Python, portanto, aceitou que o parâmetro efetivo da função [show] fosse do tipo [str];
- a linha 7 do código provoca a exceção refletida nas linhas 4 a 10 dos resultados;
PyCharm indica, no entanto, que há uma anomalia:

Em [1], PyCharm destacou a chamada incorreta.
6.10. Script [fonc_10]: parâmetros nomeados
Para passar parâmetros a uma função, é possível utilizar os nomes dos parâmetros formais da mesma. Nesse caso, não é necessário respeitar a ordem dos parâmetros formais:
# é possível designar os parâmetros efetivos por seus nomes formais
def f(x, y):
return x + y
# main
print(f(y=10, x=3))
Notas
- linha 2: a função f possui dois parâmetros formais, x e y;
- linha 7: ao chamar a função f, é possível utilizar os nomes dos parâmetros formais. Essa prática pode ser útil em pelo menos dois casos:
- a função possui muitos parâmetros, dos quais a maioria tem um valor padrão. Ao chamá-la, a técnica anterior permite inicializar apenas os parâmetros para os quais não se deseja usar o valor padrão;
- se os parâmetros formais tiverem nomes significativos, o uso de parâmetros nomeados na chamada da função melhora a legibilidade do código;
Resultados
6.11. Script [fonc_11]: função recursiva
O script [fonc_11] é um exemplo de função recursiva (que chama a si mesma):
# função recursiva
def fact(i: int) -> int:
# fatorial(1) é 1
# uma função recursiva deve terminar em algum momento
if i == 1:
return 1
else:
# fatorial(i) = i * fatorial(i-1)
return i * fact(i - 1)
# ---------- main
print(f"fact(8)={fact(8)}")
Comentários
- linhas 1-9: a função fatorial;
- linha 9: a função [factorielle] chama a si mesma;
- linhas 5-6: uma função recursiva deve sempre parar quando uma condição for satisfeita; caso contrário, ocorre uma recursão infinita;
Resultados
6.12. Script [fonc_12]: função recursiva
A função [fonc_12] fornece mais detalhes sobre o funcionamento da recursividade:
# função recursiva
# comportamento do parâmetro j
def fact(i: int, j: int) -> int:
# encerramento da função recursiva
if i == 1:
print(f"j={j}")
return 1
else:
# manipulamos j
j += 1
print(f"avant fact j={j}")
# recursividade
f = fact(i - 1, j)
print(f"après fact j={j}")
# resultado
return i * f
# ---------- main
print(f"fact(8)={fact(8, 0)}")
Comentários
- linha 5: continuamos nos concentrando na função fatorial. Adicionamos a ela o parâmetro [j];
- linha 12: a variável j é incrementada regularmente a cada fatorial. Exibimos seu valor antes (linha 12) e depois (linha 16) da recursão (linha 15);
Resultados
- linhas 2-8: observa-se que o valor de [j] aumenta enquanto a recursão continua, até atingir a condição em que a recursão é interrompida. A partir desse momento, o retorno das chamadas à função [fact] ocorre na ordem inversa às chamadas;
- linhas 10-16: essas exibições refletem os retornos sucessivos da chamada da fatorial. A variável [j] retorna aos seus valores até atingir seu valor inicial, 1;
