123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306 |
- <!--
- Introdução à Programação - 2017 - Prof. Leoônidas de Oliveira Brandão
- Introdução ao uso de funções em Python: variáveis locais, globais e aninhamento de funções
- LInE (Laboratory of Informatics in Education) - http://www.usp.br/line
- IME - USP
- Material didático
- Pode usar livrevemente este material para fins não comerciais, devendo sempre fazer referência à autoria.
- Sugestões/apontamento são bem vindos: leo@ime.usp.br (favor indicar no assunto "material de introducao 'a programacao")
- Prof. Leônidas de Oliveira Brandão
- http://www.ime.usp.br/~leo
- http://line.ime.usp.br
- http://www.matemtica.br
- -->
- <meta http-equiv='Content-Type' content='text/html; charset=UTF-8'>
- <meta name='keywords' content='mac0122, material, professores, leonidas de oliveira brandao'>
- <link rel='stylesheet' type='text/css' href='css_img_js_conf/all.css'>
- <link rel='stylesheet' type='text/css' href='css_img_js_conf/line_introducao_programacao.css'>
- <script src="css_img_js_conf/defineLInE.js"></script> <!-- para referencias 'a documentos internos -->
- <div class="pagina">
- <p class="secao">Introdução ao uso de funções em Python: variáveis locais, globais e aninhamento de funções</p>
- <p>
- Nesta seção apresento um conceito mais "sutil" de programação, para entendê-lo é essencial que você copie os códigos apresentados,
- "cole" em seu editor preferido e os teste até que esteja tudo claro.
- Experimente alterar os exemplos e criar outros: grave-os e os <i>rode</i>.
- <b style="color:#0000aa">Para prender programação é preciso programar!</b>
- </p>
- <p class="subsubsecao">Cuidado com a sintaxe de função (erro de recorrência não desejada)</p>
- <p>
- Um erro que as vezes ocorre é usar de modo equivocado o conceito de <i>devolução</i> do resultado, provocando uma
- recorrência infinita.
- Como a cada chamada da função todas suas variáveis locais precisam ser alocadas na memória, a cada chamada um novo espaço
- de memória é reservado, logo, em algum tempo o seu computador
- <b style="color:#aa0000;" title="não terá mais espaço disponível">não mais "aguentará"</b> e lançará uma mensagem de erro
- indicando algo como <b style="color:#aa0000;" title="RuntimeError: maximum recursion depth exceeded">recursão excedeu profundidade máxima</b>.
- O código abaixo ilustra esse engano, experimente copiá-lo e colá-lo em seu editor preferido e rode-o.
- </p>
- <p>
- <center><div class="codigo"><pre><verm>def</verm> fat (n) :
- f = 1; i = 2;
- while (i<=n) :
- f *= i;
- i += 1;
- return fat(n); <cyan># Erro aqui! RuntimeError: maximum recursion depth exceeded</cyan>
- <verd>print</verd>(fat(5));</pre>
- </div><br/>
- <i>Cód. 1. Exemplo de engano implicando em recorrência "infinita"</i>.
- </center>
- </p>
- <p>
- Para corrigir o engano do código 1, troque a linha de <i>devolução</i> por <tt>return f;</tt>.
- Isso eliminará as recorrências.
- Por outro lado, é possível implementar uma versão correta e recursiva,
- veja este
- <a href="#" onclick="trocaPagina('introducao_recursividade.html')" title="examinar texto sobre funções recorrentes">texto sobre recursividade</a>.
- </p>
- <p class="subsubsecao">Sobre o aninhamento de funções na linguagem <i>Python</i></p>
- <p>
- Em várias linguagens de programação, <i>Python</i> em particular, é possível declarar função dentro de função (<b>aninhar</b>).
- Por exemplo, dentro de uma função de nome <i>funcao2</i>, pode-se declarar outra função de nome <i>funcao3</i>.
- O resultado é que, dentro da primeira função, pode-se invocar a segunda, mas em nenhum outro local do código seria possível
- invocar esssa função <i>funcao3</i>.
- </p>
- <p>
- Mas vale destacar que esse recurso só é efetivamente útil se a função interna (<i>funcao3</i>) só fizer sentido dentro da função que a contém.
- Pois em caso contrário, você poderia invocar a função interna em outros contextos.
- Um exemplo dessa situação é ilustrado no parágrafo seguinte, a função <i>fatorial</i> poderia ficar melhor se declarada fora da função <i>combinacao</i>.
- </p>
- <p>
- Um exemplo simples de função contendo outra função, poderia ser o caso do cálculo da combinação de <i>n</i>, tomados <i>k</i>-a-<i>k</i>:
- <i>C<sub>n,k</sub> = n!/(k!(n-k)!)</i>.
- Como para o calculo de <i>C<sub>n,k</sub></i> é necessário invocar o cálculo do fatorial 3 vezes, podemos desejar fazer um implementação que
- deixe essa dependência clara, definindo uma função de nome <i>combinacao</i> e, dentro dela, declarar a função
- <i>fatorial</i>. Vide exemplo 1.
- </p>
- <p>
- Porém, como anteriormente comentado, como a função <i>fatorial</i> tem potencial de aplicação mais amplo, ela ficaria melhor se declarada
- <b style="color:#0000aa">fora</b> da função <i>combinacao</i>
- (vide a crítica a esse organização de código logo após o exemplo 1).
- </p>
- <div class="exemplo"><i>Exemplo 1</i>. Declaração aninhada de funções.
- </div><!-- class="exemplo" -->
- <div class="codigo"><pre><verm>def</verm> combinacao (n, k) : <cyan># combinacao de n, k-a-k</cyan>
- <verm>def</verm> fatorial (n) : <cyan># fatorial de n (supondo n natural)</cyan>
- fat = 1;
- for i in range(2, n+1) :
- fat *= i;
- return fat;
- return fatorial(n) / (fatorial(k)*fatorial(n-k));
- <verm>def</verm> main () :
- n = 5; k = 3;
- <verd>print</verd>("Combinacao %d, %d-a-%d = %d" % (n, k, k, combinacao(n,k)));
- <cyan># Problema de declarar 'fatorial()' dentro de 'combinacao()': a linha abaixo daria erro!</cyan>
- <cyan># Erro: NameError: global name 'fatorial' is not defined</cyan>
- <cyan># <verd>print</verd>("fat(0)=%d, fat(1)=%d, fat(2)=%d, fat(3)=%d" % (fatorial(0), fatorial(1), fatorial(2), fatorial(3)));</cyan>
- main();</pre>
- </div>
- <p>
- Note que o código acima apresenta a linha comentada
- <tt><verd>print</verd>("fat(0)=%d, fat(1)=%d, fat(2)=%d, fat(3)=%d" % (fatorial(0), fatorial(1), fatorial(2), fatorial(3)));</tt>.
- A razão é que ela resultaria erro, pois a função <i>fatorial</i> foi declarada dentro da função <i>combinacao</i>
- e portanto só pode ser usando (invocada) dentro dessa segunda!
- </p>
- <p>
- Uma vez que o uso do cálculo de fatorial é muito comum e pensando que o código acima seria parte de um maior,
- então provavelmente o desenho de código usado não é bom, pois implicaria em precisarmos definir outra função <i>fatorial</i>,
- essa fora do contexto da função <i>combinacao</i>.
- Nesse sentido, poderia ser melhor usar o código (pensando que ele seja o início de um sistema maior) do
- exemplo a seguir.
- </p>
- <div class="exemplo"><i>Exemplo 2</i>. Declaração de funções sem aninhamento.
- </div><!-- class="exemplo" -->
- <div class="codigo"><pre><verm>def</verm> fatorial (n) : <cyan># fatorial de n (supondo n natural)</cyan>
- fat = 1;
- for i in range(2, n+1) :
- fat *= i;
- return fat;
- <verm>def</verm> combinacao (n, k) : <cyan># combinacao de n, k-a-k</cyan>
- return fatorial(n) / (fatorial(k)*fatorial(n-k));
- <verm>def</verm> main () :
- n = 5; k = 3;
- <verd>print</verd>("Combinacao %d, %d-a-%d = %d" % (n, k, k, combinacao(n,k)));
- <cyan># Note que agora pode-se invocar 'fatorial()' dentro da 'main'</cyan>
- <verd>print</verd>("fat(0)=%d, fat(1)=%d, fat(2)=%d, fat(3)=%d" % (fatorial(0), fatorial(1), fatorial(2), fatorial(3)));
- main();</pre>
- </div>
- <a name="variaveis">
- <p class="subsubsecao">Variáveis globais e locais</p>
- </a>
- <p>
- Outro conceito importante relacionado com funções é o de declaração de variáveis.
- Assim, uma variável é <b>local</b> se declarada dentro de uma função qualquer, quer dizer,
- essa variável será "conhecida" (poderá ser usada) apenas dentro dessa função.
- Por exemplo, no <i>exemplo 2</i> acima, a variável <i>fat</i> é conhecida apenas dentro da função
- <i>fatorial</i>, portanto seu uso deve-se restringir a esse contexto.
- </p>
- <p>
- Por outro lado, pode-se usar o mesmo nome para variáveis que estão em contextos distintos.
- Por exemplo, no código do <i>exemplo 1</i>, se fosse necessário poderíamos usar o nome de variável
- local <i>i</i> dentro da função <i>combinacao</i> e não haveria qualquer colisão com a variável
- <i>i</i> de <i>fatorial</i>.
- </p>
- <p>
- Entretanto é necessário um critério que elimine <i>ambiguidades</i>, isto é, que seja possível identificar
- unicamente cada uma das variáries. Para isso usa-se o <b>princípio da proximidade</b> (ou da <b>localidade</b>),
- quer dizer, ao usar uma variável considera-se como sua declarção aquela mais próxima.
- Por isso, que poderiamos usar <i>i</i> tanto em <i>combinacao</i>, quanto em <i>fatorial</i>,
- </p>
- <center>
- <p>
- <img src="img/funcoes_var_locais.png" title="imagem ilustrando principio da localidade para variáveis"/>
- <br/>
- <i>Fig. 1. Princípio da <i>localidade</i> para variáveis: <tt>var2</tt> e <tt>var1</tt> são aquelas declaradas dentro da <tt>funcao2</tt>.</i>
- </p>
- </center>
- <p>
- Já uma variável <b>global</b> pode ser acessada em qualquer ponto do código.
- Mas por essa mesma razão deve evitada, sendo necessária apenas para casos excepcionais,
- sempre com um nome significativo, que evite qualquer confusão.
- </p>
- <p>
- Em <i>Python</i> existem dois modos para se declarar uma variável <b>global</b>, ou seja, uma variável
- que pode ser acessada em qualquer ponto do código (em qualquer função).
- Ela pode estar no código "principal"
- (quer dizer não estar dentro de qualquer função) ou sendo declarada dentro de uma função usando
- a diretiva especial <b>global</b>, como ilustrado no próximo exemplo.
- </p>
- <div class="exemplo"><i>Exemplo 3</i>. Declaração de variável global.
- </div><!-- class="exemplo" -->
- <div class="codigo"><pre>var_global1 = "essa variavel e' uma global (1)";
- <verm>def</verm> funcao1 () :
- <verd>print</verd>("funcao1: var_global1 = %s" % var_global1);
- <verm>def</verm> funcao2 () :
- global var_global2; <cyan># define 'var_global2' como global </cyan>
- var_global2 = "essa variavel e' outra global (2)"; <cyan># atribui um primeiro valor a 'var_global2'</cyan>
- <verd>print</verd>("funcao2: var_global1 = %s" % var_global1);
- <verd>print</verd>("funcao2: var_global2 = %s" % var_global2);
- <verm>def</verm> main () :
- <verd>print</verd>("main : var_global1 = %s" % var_global1);
- #Erro: <verd>print</verd>("main : var_global2 = %s" % var_global2); <cyan># NAO pode usar essa linha pois 'var_global2' ainda NAO foi definida</cyan>
- funcao1();
- funcao2();
- <verd>print</verd>("main : var_global2 = %s" % var_global2); <cyan># NAO pode usar essa linha pois 'var_global2' ainda NAO foi definida</cyan>
- main();</pre>
- </div>
- <p class="subsubsecao">Aninhando funções e variáveis locais e globais</p>
- <p>
- Deve-se notar que é possível aninhar tantas funções quantas forem necessárias e o mesmo para variáveis locais.
- O exemplo abaixo ilustra esses aninhamentos e o princípio da proximidade.
- Examine-o com atenção, copie-o em seu computador, utilizando um interpretador <i>Python</i>
- e altere seu código até que esteja certo de ter assimilado os conceitos.
- </p>
- <div class="exemplo"><i>Exemplo 4</i>. Declaração de variáveis locais, usando globais e aninhamento de funções.
- </div><!-- class="exemplo" -->
- <div class="codigo"><pre>glob1 = "glob1: variavel global, conhecida em qualquer local";
- <verm>def</verm> funcao1 () :
- loc1 = "loc1: conhecida apenas dentro da funcao 'funcao1'";
- glob1 = "funcao1.glob1: declarada dentro de 'funcao1' => uma variavel local `a funcao 'funcao1'"; <!-- " -->
- <verd>print</verd>("funcao1: loc1 = %s" % loc1); <cyan># note que: pode haver m</cyan>
- <verd>print</verd>("funcao1: glob1 = %s\n" % glob1);
- <verm>def</verm> funcao2 (param1) :
- <verm>def</verm> funcao3 () : <cyan># essa funcao so' e' conhecida dentro de 'funcao2'!</cyan>
- loc1 = "loc1: conhecida apenas dentro da funcao 'funcao3' - NAO sou a 'funcao1.loc1' e nem a 'funcao2.loc1'!";
- glob1 = "funcao3.glob1: declarada dentro de 'funcao3' => var. local `a funcao 'funcao3' - NAO e' global 'glob1' e nem 'funcao1.glob1'!";
- <verd>print</verd>("funcao3: loc1 = %s" % loc1); <cyan># note que: e' local com outra com esse nome; "principio do mais proximo" em acao!</cyan>
- <verd>print</verd>("funcao3: loc2 = %s" % loc2); <cyan># note que: e' local `a funcao 'funcao2' - variavel declarada fora de funcao3!</cyan>
- <verd>print</verd>("funcao3: glob1 = %s" % glob1);
- global glob2;
- glob2 = "glob2: variavel global, conhecida em qualquer local (mas declarada em 'funcao2')"; <cyan># mas depois desse comando ser executado!</cyan>
- loc1 = "loc1: conhecida apenas dentro da funcao 'funcao2 - NAO sou a 'funcao1.loc1'!'";
- loc2 = "loc2: conhecida apenas dentro da funcao 'funcao2'";
- <verd>print</verd>("funcao2: param1 = %s" % param1);
- <verd>print</verd>("funcao2: loc1 = %s" % loc1); <cyan># note que: e' local com outra com esse nome; "principio do mais proximo" em acao!</cyan>
- <verd>print</verd>("funcao2: loc2 = %s" % loc2); <cyan># note que: e' local</cyan>
- <verd>print</verd>("funcao2: glob1 = %s" % glob1);
- <verd>print</verd>("funcao2: chama funcao3\n");
- funcao3();
- <verd>print</verd>("funcao2: chama funcao1\n");
- funcao1();
- <verm>def</verm> funcao4 () :
- <verd>print</verd>("funcao4: glob2 = %s" % glob2);
- <verm>def</verm> main () :
- loc1 = "loc1: conhecida apenas dentro da funcao 'main'";
- <verd>print</verd>("main : loc1 = %s" % loc1); <cyan># note que: e' local com outra com esse nome; "principio do mais proximo" em acao!</cyan>
- <verd>print</verd>("main : glob1 = %s" % glob1);
- <cyan># A linha abaixo NAO pode ser usada, pois apesar de glob2 ser declarada como global (usando a diretiva 'global')</cyan>
- <cyan># isso e' feito apenas dentro da funcao 'funcao2', entao ela so' ficara' disponivel apos 'funcao2' ser executada!</cyan>
- <cyan># Se tirar o comentario da linha abaixo, resultaria o erro: NameError: global name 'glob2' is not defined</cyan>
- <cyan># <verd>print</verd>("main : glob2 = %s" % glob2);</cyan>
- <verd>print</verd>("main : chama funcao1\n");
- funcao1();
- <verd>print</verd>("main : chama funcao2\n");
- funcao2("parametro efetivo passado para a funcao 'funcao2'");
- <cyan># Atencao: como a funcao 'funcao3' foi declarada dentro da funcao 'funcao2', entao ela so' pode se invocada em 'funcao2'</cyan>
- <cyan># Por exemplo, a linha abaixo resultaria no erro: NameError: global name 'funcao3' is not defined</cyan>
- <cyan># funcao3();</cyan>
- <cyan># Mas nesse ponto a global 'glob2' ja' foi definida</cyan>
- <verd>print</verd>("main : glob2 = %s" % glob2);
- <verd>print</verd>("main : chama funcao4\n");
- funcao4(); <cyan># note que 'funcao4' tambem pode usar a global 'glob2' (declarada dentro da 'funcao2'</cyan>
- main();</pre>
- </div>
- <p>
- <a href="https://www.ime.usp.br/~leo" target="_blank" title="seguir para a pagina do prof. Leônidas">Leônidas de Oliveira Brandão</a><br/>
- <a href="http://www.ime.usp.br/~leo" target="_blank" title="seguir para a página do LInE">http://line.ime.usp.br</a>
- </p>
- <p class="rodape">
- <b>Alterações</b>:<br/>
- 2020/08/09: formato, revisão geral;<br/>
- 2020/05/12: versão inicial<br/>
- </p>
- </div>
|