{ "cells": [ { "cell_type": "markdown", "metadata": {}, "source": [ "# Mais detalhes sobre funções" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## Escopo" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Escopo é o nome dado à parte do código onde um identificador é válido.\n", "\n", "Dois tipos de escopo importantes em Python são os chamados **escopo global** e **escopo local**.\n", "\n", "O *escopo global* se aplica a todos os identificadores que são definidos em um módulo. Veremos módulos mais adiante. Por enquanto, basta saber que todos os identificadores criados diretamente no interpretador fazem parte de um módulo.\n", "\n", "O *escopo local* se aplica a identificadores criados dentro de uma função.\n", "\n", "Identificadores no escopo global podem ser referenciados em qualquer lugar no módulo, enquanto identificadores locais somente podem ser referenciados na função onde foram criados.\n", "\n", "Assim, no código abaixo, `variavel_global` e `f` (o nome da função) são identificadores globais, enquanto `variavel_local`, `par1` e `par2` (os parâmetros) são identificadores locais da função `f`." ] }, { "cell_type": "code", "execution_count": 1, "metadata": { "collapsed": true }, "outputs": [], "source": [ "variavel_global = 10\n", "def f(par1, par2):\n", " variavel_local = 12\n", " return par1 + par2 - variavel_global + variavel_local" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Os identificadores globais são acessíveis no escopo do módulo (direto no interpretador, neste caso), mas os locais não." ] }, { "cell_type": "code", "execution_count": 2, "metadata": { "collapsed": false }, "outputs": [ { "data": { "text/plain": [ "10" ] }, "execution_count": 2, "metadata": {}, "output_type": "execute_result" } ], "source": [ "variavel_global" ] }, { "cell_type": "code", "execution_count": 3, "metadata": { "collapsed": false }, "outputs": [ { "data": { "text/plain": [ "" ] }, "execution_count": 3, "metadata": {}, "output_type": "execute_result" } ], "source": [ "f" ] }, { "cell_type": "code", "execution_count": 4, "metadata": { "collapsed": false }, "outputs": [ { "ename": "NameError", "evalue": "name 'variavel_local' is not defined", "output_type": "error", "traceback": [ "\u001b[0;31m---------------------------------------------------------------------------\u001b[0m", "\u001b[0;31mNameError\u001b[0m Traceback (most recent call last)", "\u001b[0;32m\u001b[0m in \u001b[0;36m\u001b[0;34m()\u001b[0m\n\u001b[0;32m----> 1\u001b[0;31m \u001b[0mvariavel_local\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0m", "\u001b[0;31mNameError\u001b[0m: name 'variavel_local' is not defined" ] } ], "source": [ "variavel_local" ] }, { "cell_type": "code", "execution_count": 5, "metadata": { "collapsed": false }, "outputs": [ { "ename": "NameError", "evalue": "name 'par1' is not defined", "output_type": "error", "traceback": [ "\u001b[0;31m---------------------------------------------------------------------------\u001b[0m", "\u001b[0;31mNameError\u001b[0m Traceback (most recent call last)", "\u001b[0;32m\u001b[0m in \u001b[0;36m\u001b[0;34m()\u001b[0m\n\u001b[0;32m----> 1\u001b[0;31m \u001b[0mpar1\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0m", "\u001b[0;31mNameError\u001b[0m: name 'par1' is not defined" ] } ], "source": [ "par1" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Cada função define um novo escopo local. Assim, quando uma função é definida dentro da outra, os identificadores locais da função externa são acessíveis na função externa (mas não o contrário)." ] }, { "cell_type": "code", "execution_count": 6, "metadata": { "collapsed": true }, "outputs": [], "source": [ "def g(x):\n", " def h(y):\n", " return y ** x\n", " return x + h(2)\n" ] }, { "cell_type": "code", "execution_count": 7, "metadata": { "collapsed": false }, "outputs": [ { "data": { "text/plain": [ "11" ] }, "execution_count": 7, "metadata": {}, "output_type": "execute_result" } ], "source": [ "g(3)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Por outro lado, como a função define um novo escopo, se criarmos uma variável (através de uma atribuição) com o mesmo identificador de uma variável externa, ela irá esconder o valor da variável externa." ] }, { "cell_type": "code", "execution_count": 8, "metadata": { "collapsed": true }, "outputs": [], "source": [ "variavel_global = 10\n", "def f(par1, par2):\n", " variavel_local = 12\n", " variavel_global = 8 # Esta é uma nova variável no escopo de f\n", " return par1 + par2 - variavel_global + variavel_local" ] }, { "cell_type": "code", "execution_count": 9, "metadata": { "collapsed": false }, "outputs": [ { "data": { "text/plain": [ "9" ] }, "execution_count": 9, "metadata": {}, "output_type": "execute_result" } ], "source": [ "f(2,3)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "O valor de `variavel_global` não foi afetado pela execução de `f`." ] }, { "cell_type": "code", "execution_count": 10, "metadata": { "collapsed": false }, "outputs": [ { "data": { "text/plain": [ "10" ] }, "execution_count": 10, "metadata": {}, "output_type": "execute_result" } ], "source": [ "variavel_global" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "O efeito dessas regras é que as variáveis externas podem ser acessíveis dentro de escopos internos, desde que queiramos apenas acessar os objetos a que essas variáveis se referenciam, mas sem atribuir novos objetos a essas variáveis.\n", "\n", "Se quisermos mudar o objeto associado com a variável externa, devemos declarar isso explicitamente usando a palavra-chave `global` (para acesso a variáveis globais):" ] }, { "cell_type": "code", "execution_count": 11, "metadata": { "collapsed": true }, "outputs": [], "source": [ "variavel_global = 10\n", "def f(par1, par2):\n", " global variavel_global\n", " variavel_local = 12\n", " variavel_global = 8\n", " return par1 + par2 - variavel_global + variavel_local" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Neste caso, qualquer alteraço do objeto associado a `variavel_global` feita dentro de `f` será refletida na variável global:" ] }, { "cell_type": "code", "execution_count": 12, "metadata": { "collapsed": false }, "outputs": [ { "data": { "text/plain": [ "9" ] }, "execution_count": 12, "metadata": {}, "output_type": "execute_result" } ], "source": [ "f(2,3)" ] }, { "cell_type": "code", "execution_count": 13, "metadata": { "collapsed": false }, "outputs": [ { "data": { "text/plain": [ "8" ] }, "execution_count": 13, "metadata": {}, "output_type": "execute_result" } ], "source": [ "variavel_global" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Mas não se confunda: sem o uso de `global`, não podemos mudar o objeto a que a variável global se referencia, mas podemos, se o objeto for de um tipo mutável, alterar seu valor:" ] }, { "cell_type": "code", "execution_count": 14, "metadata": { "collapsed": true }, "outputs": [], "source": [ "lista = []\n", "def estraga(x):\n", " lista.append(x)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Ao executar essa função, adicionamos um elemento à lista referenciada pela variável global `lista`, mudando portanto seu valor." ] }, { "cell_type": "code", "execution_count": 15, "metadata": { "collapsed": false }, "outputs": [ { "data": { "text/plain": [ "[2]" ] }, "execution_count": 15, "metadata": {}, "output_type": "execute_result" } ], "source": [ "estraga(2)\n", "lista" ] }, { "cell_type": "code", "execution_count": 16, "metadata": { "collapsed": false }, "outputs": [ { "data": { "text/plain": [ "[2, 3]" ] }, "execution_count": 16, "metadata": {}, "output_type": "execute_result" } ], "source": [ "estraga(3)\n", "lista" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Quando uma nova variável é declarada em escopo mais interno com o mesmo nome de uma variável existente em escopo externo, ela \"esconde\" a variável externa." ] }, { "cell_type": "code", "execution_count": 17, "metadata": { "collapsed": false }, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "x fora: 1\n" ] } ], "source": [ "x = 1\n", "def f():\n", " x = 2\n", " def g():\n", " x = 3\n", " def h():\n", " print('x em h:', x)\n", " h()\n", " print('x em g:', x)\n", " g()\n", " print('x em f:', x)\n", "print('x fora:', x)" ] }, { "cell_type": "code", "execution_count": 18, "metadata": { "collapsed": false }, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "x em h: 3\n", "x em g: 3\n", "x em f: 2\n" ] } ], "source": [ "f()" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Se queremos nos referir em um escopo mais interno a uma variável de escopo local mais externo, devemos usar a palavra-chave `nonlocal`, que tem efeito similar, para esses casos, ao que `global` faz para variáveis globais." ] }, { "cell_type": "code", "execution_count": 19, "metadata": { "collapsed": true }, "outputs": [], "source": [ "def f():\n", " x = 2\n", " def g():\n", " nonlocal x\n", " x = 3\n", " print('x em g:', x)\n", " print('x em f (antes de g):', x)\n", " g()\n", " print('x em f (depois de g):', x)" ] }, { "cell_type": "code", "execution_count": 20, "metadata": { "collapsed": false }, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "x em f (antes de g): 2\n", "x em g: 3\n", "x em f (depois de g): 3\n" ] } ], "source": [ "f()" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "### Closures" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Uma característica interessante de funções em Python é que elas podem guardar informações sobre o ambiente onde foram definidas. Assim, se uma função `g` é definida dentro de outra função `f`, `g` pode guardar referência para os objetos de `f` de quando ela foi definida.\n", "\n", "O termo técnico para isso é **closure**.\n", "\n", "Como funções são objetos, elas podem ser retornadas por outra funções. Por exemplo, no código abaixo, a função `f`, quando chamada, retorna uma *closure* que lembra o objeto `x` usado em sua definição:" ] }, { "cell_type": "code", "execution_count": 21, "metadata": { "collapsed": true }, "outputs": [], "source": [ "def f(x):\n", " def g(y):\n", " return x + y\n", " return g" ] }, { "cell_type": "code", "execution_count": 22, "metadata": { "collapsed": true }, "outputs": [], "source": [ "a = f(2)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Note que agora `a` é uma função:" ] }, { "cell_type": "code", "execution_count": 23, "metadata": { "collapsed": false }, "outputs": [ { "data": { "text/plain": [ ".g>" ] }, "execution_count": 23, "metadata": {}, "output_type": "execute_result" } ], "source": [ "a" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Podemos usá-la então passando o parâmetro que ela precisa:" ] }, { "cell_type": "code", "execution_count": 26, "metadata": { "collapsed": false }, "outputs": [ { "data": { "text/plain": [ "3" ] }, "execution_count": 26, "metadata": {}, "output_type": "execute_result" } ], "source": [ "a(1)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "O resultado aqui foi 3, pois a função `a` soma ao valor passado para ela o valor 2, que foi passado a `f` para a criação de `a`." ] }, { "cell_type": "code", "execution_count": 25, "metadata": { "collapsed": false }, "outputs": [ { "data": { "text/plain": [ "6" ] }, "execution_count": 25, "metadata": {}, "output_type": "execute_result" } ], "source": [ "b = f(5)\n", "b(1)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Abaixo uma função que \"memoriza\" uma lista de números passados em execuções sucessivas:" ] }, { "cell_type": "code", "execution_count": 27, "metadata": { "collapsed": true }, "outputs": [], "source": [ "def f(usados):\n", " def g(x):\n", " usados.append(x)\n", " print('Usados até agora:', usados)\n", " return len(usados)\n", " return g" ] }, { "cell_type": "code", "execution_count": 28, "metadata": { "collapsed": true }, "outputs": [], "source": [ "a = f([])" ] }, { "cell_type": "code", "execution_count": 29, "metadata": { "collapsed": false }, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "Usados até agora: [1]\n" ] }, { "data": { "text/plain": [ "1" ] }, "execution_count": 29, "metadata": {}, "output_type": "execute_result" } ], "source": [ "a(1)" ] }, { "cell_type": "code", "execution_count": 30, "metadata": { "collapsed": false }, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "Usados até agora: [1, 2]\n" ] }, { "data": { "text/plain": [ "2" ] }, "execution_count": 30, "metadata": {}, "output_type": "execute_result" } ], "source": [ "a(2)" ] }, { "cell_type": "code", "execution_count": 31, "metadata": { "collapsed": false }, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "Usados até agora: [1, 2, 0]\n" ] }, { "data": { "text/plain": [ "3" ] }, "execution_count": 31, "metadata": {}, "output_type": "execute_result" } ], "source": [ "a(0)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## Argumentos" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Uma função especifica os parâmetros que serão usados em sua execução. Ao executar a função, precisamos fornecer valores para todos esses parâmetros. Como vimos, os parâmetros são variáveis, que guardarão uma referência para os objetos passados em sua chamada.\n", "\n", "A correspondência entre os valores passados e os parâmetros pode ser feita de diversas formas em Python. A primeira e mais simples é a *posicional*, que é a mesma usada em C. Neste caso, o primeiro valor fornecido será associado ao primeiro parâmetro, o segundo valor ao segundo parâmetro, e assim por diante." ] }, { "cell_type": "code", "execution_count": 32, "metadata": { "collapsed": true }, "outputs": [], "source": [ "def f(x, y):\n", " print('x =', x, 'y =', y)" ] }, { "cell_type": "code", "execution_count": 33, "metadata": { "collapsed": false }, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "x = 1 y = 2\n" ] } ], "source": [ "f(1,2)" ] }, { "cell_type": "code", "execution_count": 34, "metadata": { "collapsed": false }, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "x = 5 y = 55\n" ] } ], "source": [ "f(5,55)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "O número de valores passados deve corresponder ao número de parâmetros esperados." ] }, { "cell_type": "code", "execution_count": 35, "metadata": { "collapsed": false }, "outputs": [ { "ename": "TypeError", "evalue": "f() missing 1 required positional argument: 'y'", "output_type": "error", "traceback": [ "\u001b[0;31m---------------------------------------------------------------------------\u001b[0m", "\u001b[0;31mTypeError\u001b[0m Traceback (most recent call last)", "\u001b[0;32m\u001b[0m in \u001b[0;36m\u001b[0;34m()\u001b[0m\n\u001b[0;32m----> 1\u001b[0;31m \u001b[0mf\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0;36m1\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0m", "\u001b[0;31mTypeError\u001b[0m: f() missing 1 required positional argument: 'y'" ] } ], "source": [ "f(1)" ] }, { "cell_type": "code", "execution_count": 36, "metadata": { "collapsed": false, "scrolled": true }, "outputs": [ { "ename": "TypeError", "evalue": "f() takes 2 positional arguments but 3 were given", "output_type": "error", "traceback": [ "\u001b[0;31m---------------------------------------------------------------------------\u001b[0m", "\u001b[0;31mTypeError\u001b[0m Traceback (most recent call last)", "\u001b[0;32m\u001b[0m in \u001b[0;36m\u001b[0;34m()\u001b[0m\n\u001b[0;32m----> 1\u001b[0;31m \u001b[0mf\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0;36m1\u001b[0m\u001b[0;34m,\u001b[0m\u001b[0;36m2\u001b[0m\u001b[0;34m,\u001b[0m\u001b[0;36m3\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0m", "\u001b[0;31mTypeError\u001b[0m: f() takes 2 positional arguments but 3 were given" ] } ], "source": [ "f(1,2,3)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Adicionalmente, Python permite a passagem pela sintaxe `chave=valor`, onde chave é o nome de um parâmetro da função e valor é o valor que ele irá receber. Note que **nesse caso, os parâmetros não precisam ser passados na mesma ordem**." ] }, { "cell_type": "code", "execution_count": 37, "metadata": { "collapsed": false }, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "x = 1 y = 2\n" ] } ], "source": [ "f(x=1, y=2)" ] }, { "cell_type": "code", "execution_count": 38, "metadata": { "collapsed": false }, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "x = 2 y = 1\n" ] } ], "source": [ "f(y=1, x=2)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Outra versatilidade ocorre pois podemos fornecer um valor *default* para um parâmetro. Neste caso, se um valor para o parâmetro não for especificado, o valor *default* será usado." ] }, { "cell_type": "code", "execution_count": 39, "metadata": { "collapsed": true }, "outputs": [], "source": [ "def f(x, y, z = 0):\n", " print ('x =', x, ', y =', y, ', z =', z)" ] }, { "cell_type": "code", "execution_count": 40, "metadata": { "collapsed": false }, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "x = 1 , y = 2 , z = 3\n" ] } ], "source": [ "f(1,2,3)" ] }, { "cell_type": "code", "execution_count": 41, "metadata": { "collapsed": false }, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "x = 1 , y = 4 , z = 2\n" ] } ], "source": [ "f(z = 2, y = 4, x = 1)" ] }, { "cell_type": "code", "execution_count": 42, "metadata": { "collapsed": false }, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "x = 2 , y = 3 , z = 0\n" ] } ], "source": [ "f(2,3)" ] }, { "cell_type": "code", "execution_count": 43, "metadata": { "collapsed": false }, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "x = 3 , y = 4 , z = 0\n" ] } ], "source": [ "f(x=3,y=4)" ] }, { "cell_type": "code", "execution_count": 44, "metadata": { "collapsed": false }, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "x = 7 , y = 8 , z = 0\n" ] } ], "source": [ "f(7, y=8)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "É frequente usar-se `None` como o valor default, e então sabemos que um valor não foi especificado na chamada comparando o parâmetro com `None`." ] }, { "cell_type": "code", "execution_count": 45, "metadata": { "collapsed": true }, "outputs": [], "source": [ "def f(x, y, z = None):\n", " if z is None:\n", " z = x - y\n", " print(x, y, z)" ] }, { "cell_type": "code", "execution_count": 46, "metadata": { "collapsed": false }, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "3 5 7\n" ] } ], "source": [ "f(3,5,7)" ] }, { "cell_type": "code", "execution_count": 47, "metadata": { "collapsed": false }, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "3 5 -2\n" ] } ], "source": [ "f(3,5)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Uma limitação é que na chamada os parâmetros que usam `chave=valor` devem ser colocados após todos os parâmetros posicionais desejados." ] }, { "cell_type": "code", "execution_count": 48, "metadata": { "collapsed": false }, "outputs": [ { "ename": "SyntaxError", "evalue": "positional argument follows keyword argument (, line 1)", "output_type": "error", "traceback": [ "\u001b[0;36m File \u001b[0;32m\"\"\u001b[0;36m, line \u001b[0;32m1\u001b[0m\n\u001b[0;31m f(y = 1, 2)\u001b[0m\n\u001b[0m ^\u001b[0m\n\u001b[0;31mSyntaxError\u001b[0m\u001b[0;31m:\u001b[0m positional argument follows keyword argument\n" ] } ], "source": [ "f(y = 1, 2)" ] }, { "cell_type": "code", "execution_count": 49, "metadata": { "collapsed": false, "scrolled": true }, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "2 3 1\n" ] } ], "source": [ "f(2, z=1, y = 3)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Cada parâmetro deve receber exatamente um valor. Quer dizer, não podemos deixar sem valor nem fornecer múltiplos valores (este último caso costuma ocorrer por engano quando usamos `chave=valor` para um parâmetro que já recebeu valor posicional." ] }, { "cell_type": "code", "execution_count": 50, "metadata": { "collapsed": false }, "outputs": [ { "ename": "TypeError", "evalue": "f() got multiple values for argument 'x'", "output_type": "error", "traceback": [ "\u001b[0;31m---------------------------------------------------------------------------\u001b[0m", "\u001b[0;31mTypeError\u001b[0m Traceback (most recent call last)", "\u001b[0;32m\u001b[0m in \u001b[0;36m\u001b[0;34m()\u001b[0m\n\u001b[0;32m----> 1\u001b[0;31m \u001b[0mf\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0;36m2\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0mx\u001b[0m \u001b[0;34m=\u001b[0m \u001b[0;36m2\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0m", "\u001b[0;31mTypeError\u001b[0m: f() got multiple values for argument 'x'" ] } ], "source": [ "f(2, x = 2)" ] }, { "cell_type": "code", "execution_count": 51, "metadata": { "collapsed": false, "scrolled": true }, "outputs": [ { "ename": "TypeError", "evalue": "f() missing 1 required positional argument: 'y'", "output_type": "error", "traceback": [ "\u001b[0;31m---------------------------------------------------------------------------\u001b[0m", "\u001b[0;31mTypeError\u001b[0m Traceback (most recent call last)", "\u001b[0;32m\u001b[0m in \u001b[0;36m\u001b[0;34m()\u001b[0m\n\u001b[0;32m----> 1\u001b[0;31m \u001b[0mf\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0;36m2\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0mz\u001b[0m \u001b[0;34m=\u001b[0m \u001b[0;36m4\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0m", "\u001b[0;31mTypeError\u001b[0m: f() missing 1 required positional argument: 'y'" ] } ], "source": [ "f(2, z = 4)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Frequentemente desejamos definir funções que recebem número arbitrário de parâmetros. Um exemplo é a função do Python `min`." ] }, { "cell_type": "code", "execution_count": 52, "metadata": { "collapsed": false }, "outputs": [ { "data": { "text/plain": [ "-2" ] }, "execution_count": 52, "metadata": {}, "output_type": "execute_result" } ], "source": [ "min(1,4,7,-2,0,3)" ] }, { "cell_type": "code", "execution_count": 53, "metadata": { "collapsed": false }, "outputs": [ { "data": { "text/plain": [ "2" ] }, "execution_count": 53, "metadata": {}, "output_type": "execute_result" } ], "source": [ "min(2,5)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Essa função só precisa ter pelo menos 2 valores para funcionar. Se ela recebe 1 valor só, espera que esse valor seja um objeto que pode ser percorrido (como por um `for`)." ] }, { "cell_type": "code", "execution_count": 54, "metadata": { "collapsed": false }, "outputs": [ { "data": { "text/plain": [ "-3" ] }, "execution_count": 54, "metadata": {}, "output_type": "execute_result" } ], "source": [ "min([2,5,8,1,9,-3,4,0])" ] }, { "cell_type": "code", "execution_count": 55, "metadata": { "collapsed": false }, "outputs": [ { "ename": "TypeError", "evalue": "'int' object is not iterable", "output_type": "error", "traceback": [ "\u001b[0;31m---------------------------------------------------------------------------\u001b[0m", "\u001b[0;31mTypeError\u001b[0m Traceback (most recent call last)", "\u001b[0;32m\u001b[0m in \u001b[0;36m\u001b[0;34m()\u001b[0m\n\u001b[0;32m----> 1\u001b[0;31m \u001b[0mmin\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0;36m1\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0m", "\u001b[0;31mTypeError\u001b[0m: 'int' object is not iterable" ] } ], "source": [ "min(1)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Podemos definir uma função similar ao `min` mas com comportamento distinto para apenas um valor usando a sintaxe de coleta de múltiplos valores:" ] }, { "cell_type": "code", "execution_count": 56, "metadata": { "collapsed": true }, "outputs": [], "source": [ "def meu_min(x, *resto):\n", " print('x =', x, 'resto =', resto)\n", " minimo = x\n", " for y in resto:\n", " if y < minimo:\n", " minimo = y\n", " return minimo" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Nessa função, `x` receberá o primeiro valor passado, enquanto `resto` receberá uma **tupla** com todos os outros valores:" ] }, { "cell_type": "code", "execution_count": 57, "metadata": { "collapsed": false }, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "x = 2 resto = (3, -6, 10, 1, 7, 0, 5)\n" ] }, { "data": { "text/plain": [ "-6" ] }, "execution_count": 57, "metadata": {}, "output_type": "execute_result" } ], "source": [ "meu_min(2,3,-6,10,1,7,0,5)" ] }, { "cell_type": "code", "execution_count": 58, "metadata": { "collapsed": false, "scrolled": true }, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "x = 1 resto = ()\n" ] }, { "data": { "text/plain": [ "1" ] }, "execution_count": 58, "metadata": {}, "output_type": "execute_result" } ], "source": [ "meu_min(1)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "A sintaxe `*resto` coleta todos os valores **posicionais** passados na chamada. Se quisermos coletar valores do tipo `chave=valor` precisamos usar dois asteriscos:" ] }, { "cell_type": "code", "execution_count": 59, "metadata": { "collapsed": true }, "outputs": [], "source": [ "def f(**args):\n", " for k in args.keys():\n", " print('Para', k, 'temos o valor', args[k])" ] }, { "cell_type": "code", "execution_count": 60, "metadata": { "collapsed": false }, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "Para idade temos o valor 21\n", "Para altura temos o valor 1.67\n", "Para peso temos o valor 54\n" ] } ], "source": [ "f(altura=1.67, peso=54, idade=21)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Note como os valores são coletados em um *dicionário*, cujas chaves são cadeias de caracteres correspondentes às chaves na chamada da função.\n", "\n", "É também possível coletar simultaneamente valores posicionais e `chave=valor`:" ] }, { "cell_type": "code", "execution_count": 61, "metadata": { "collapsed": true }, "outputs": [], "source": [ "def f(x, *y, **z):\n", " print(x, y, z)" ] }, { "cell_type": "code", "execution_count": 62, "metadata": { "collapsed": false }, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "1 (2, 3, 4, 5) {}\n" ] } ], "source": [ "f(1,2,3,4,5)" ] }, { "cell_type": "code", "execution_count": 63, "metadata": { "collapsed": false }, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "1 (2, 3) {'b': 5, 'a': 4}\n" ] } ], "source": [ "f(1,2,3,a=4,b=5)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Os códigos acima mostram como diversos valores passados como argumentos podem ser coletados no corpo da função.\n", "\n", "A operação inversa também é possível: Podemos pegar elementos coletados e espalhá-los para os diversos parâmetros da função.\n", "\n", "Por exemplo, a função abaixo requer 3 parâmetros." ] }, { "cell_type": "code", "execution_count": 64, "metadata": { "collapsed": true }, "outputs": [], "source": [ "def g(x,y,z):\n", " return x * y - z" ] }, { "cell_type": "code", "execution_count": 65, "metadata": { "collapsed": false }, "outputs": [ { "data": { "text/plain": [ "2" ] }, "execution_count": 65, "metadata": {}, "output_type": "execute_result" } ], "source": [ "g(2,3,4)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Se tivermos os valores a passar na ordem certa em uma tupla, uma forma de fazer a chamada seria:" ] }, { "cell_type": "code", "execution_count": 66, "metadata": { "collapsed": false }, "outputs": [ { "data": { "text/plain": [ "2" ] }, "execution_count": 66, "metadata": {}, "output_type": "execute_result" } ], "source": [ "t = (2,3,4)\n", "g(t[0],t[1],t[2])" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "No entanto, isso pode ser feito automaticamente pelo Python com o uso de um asterisco:" ] }, { "cell_type": "code", "execution_count": 67, "metadata": { "collapsed": false }, "outputs": [ { "data": { "text/plain": [ "2" ] }, "execution_count": 67, "metadata": {}, "output_type": "execute_result" } ], "source": [ "g(*t)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Para o caso `chave=valor` temos algo similar, mas os elementos devem estar num dicionário (com as chaves como cadeias de caracteres) e usamos dois asteriscos:" ] }, { "cell_type": "code", "execution_count": 68, "metadata": { "collapsed": true }, "outputs": [], "source": [ "d = {'x': 2, 'y': 3, 'z': 4}" ] }, { "cell_type": "code", "execution_count": 69, "metadata": { "collapsed": false }, "outputs": [ { "data": { "text/plain": [ "2" ] }, "execution_count": 69, "metadata": {}, "output_type": "execute_result" } ], "source": [ "g(**d)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Uma sintaxe adicional em Python é a de parâmetros que precisam *necessariamente* ser especificados no formato `chave=valor` durante a chamada. Qualquer parâmetro normal especificado **após** um parâmetro de coleta posicional será desse tipo, como no caso do parâmetro `z` da função abaixo." ] }, { "cell_type": "code", "execution_count": 70, "metadata": { "collapsed": true }, "outputs": [], "source": [ "def h(x, *y, z = 0):\n", " print(x, y, z)" ] }, { "cell_type": "code", "execution_count": 71, "metadata": { "collapsed": false }, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "1 (2,) 3\n" ] } ], "source": [ "h(1,2,z=3)" ] }, { "cell_type": "code", "execution_count": 72, "metadata": { "collapsed": false, "scrolled": true }, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "1 (2, 3) 0\n" ] } ], "source": [ "h(1,2,3)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Quando usando valores assumidos, deve-se tomar muito cuidado com valores que sejam mutáveis (por exemplo, uma lista).\n", "\n", "Veja o exemplo abaixo de uma função cujo parâmetro `y` é uma lista vazia se não for especificado." ] }, { "cell_type": "code", "execution_count": 73, "metadata": { "collapsed": true }, "outputs": [], "source": [ "def crazy(x, y = []):\n", " y.append(x)\n", " print(y)" ] }, { "cell_type": "code", "execution_count": 74, "metadata": { "collapsed": false }, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "[1, 2, 3]\n" ] } ], "source": [ "lista = [1,2]\n", "crazy(3,lista)" ] }, { "cell_type": "code", "execution_count": 75, "metadata": { "collapsed": false }, "outputs": [ { "data": { "text/plain": [ "[1, 2, 3]" ] }, "execution_count": 75, "metadata": {}, "output_type": "execute_result" } ], "source": [ "lista" ] }, { "cell_type": "code", "execution_count": 76, "metadata": { "collapsed": false }, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "[1, 2, 3, 4]\n" ] } ], "source": [ "crazy(4, lista)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Até agora, tudo ocorre como esperado. Vamos ver o que acontece se não especificamos a lista:" ] }, { "cell_type": "code", "execution_count": 77, "metadata": { "collapsed": false }, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "[4]\n" ] } ], "source": [ "crazy(4)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Por enquanto é o que esperávamos. Um 4 foi adicionado a uma lista vazia.\n", "\n", "O que acontece se chamamos novamente sem especificar uma lista?" ] }, { "cell_type": "code", "execution_count": 78, "metadata": { "collapsed": false }, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "[4, 5]\n" ] } ], "source": [ "crazy(5)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Note como o 5 *não foi adicionado a uma lista vazia*, mas sim à mesma lista usada para adicionar o 4.\n", "\n", "Isto ocorre porque os objetos usados na inicialização são calculados apenas uma vez. Depois sempre que um objeto default for necessário, esse mesmo objeto será utilizado.\n", "\n", "A regra para lidar com isso é **evitar usar objetos mutáveis em valores default**. Ao invés disso, usamos `None`, e inicializamos com um novo objeto quando necessário: " ] }, { "cell_type": "code", "execution_count": 79, "metadata": { "collapsed": true }, "outputs": [], "source": [ "def noncrazy(x, y = None):\n", " if y is None:\n", " y = []\n", " y.append(x)\n", " print(y)" ] }, { "cell_type": "code", "execution_count": 80, "metadata": { "collapsed": false }, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "[1, 2, 3, 4, 6]\n" ] } ], "source": [ "noncrazy(6, lista)" ] }, { "cell_type": "code", "execution_count": 81, "metadata": { "collapsed": false }, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "[7]\n" ] } ], "source": [ "noncrazy(7)" ] }, { "cell_type": "code", "execution_count": 82, "metadata": { "collapsed": false }, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "[8]\n" ] } ], "source": [ "noncrazy(8)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "### Recursão\n", "\n", "Funções Python podem ser chamadas recursivamente. Para exemplificar, veja a implementação interessante (mas não eficiente) de quicksort abaixo:" ] }, { "cell_type": "code", "execution_count": 83, "metadata": { "collapsed": true }, "outputs": [], "source": [ "def qsort(lista):\n", " if len(lista) <= 1: # Retorna se nada a ordenar\n", " return lista\n", " chave = lista[0] # Escolhe primeiro elemento como chave\n", " menores = []\n", " maiores = []\n", " # Separa maiores e menores que chave em listas\n", " for val in lista[1:]: # Começa no 1, pois 0 é a chave\n", " if val < chave:\n", " menores.append(val)\n", " else:\n", " maiores.append(val)\n", " # Ordena recursivamente menores e maiores, depois junta\n", " return qsort(menores) + [chave] + qsort(maiores)" ] }, { "cell_type": "code", "execution_count": 84, "metadata": { "collapsed": false }, "outputs": [ { "data": { "text/plain": [ "[2, 4, 5, 6, 7, 9]" ] }, "execution_count": 84, "metadata": {}, "output_type": "execute_result" } ], "source": [ "qsort([2,4,6,9,7,5])" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## Funções lambda" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Em muitas situações, precisamos de uma função simples, que possivelmente não será mais usada em outros lugares. Neste caso, definir uma nova função com `def` pode ser mais complicado que o necessário.\n", "\n", "Isso é resolvido pela definição de *funções lambda*, que são funções simples que não precisam receber um nome.\n", "\n", "A sintaxe é a seguinte:" ] }, { "cell_type": "code", "execution_count": 85, "metadata": { "collapsed": true }, "outputs": [], "source": [ "f1 = lambda x, y: 2 * x - y" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Esse código definiu uma função de dois parâmetros (chamados `x` e `y`) que retorna o valor da expressão `2*x-y`.\n", "\n", "Neste caso, estamos colocando essa função na variável `f1`, que pode ser usada para a execução da função." ] }, { "cell_type": "code", "execution_count": 86, "metadata": { "collapsed": false }, "outputs": [ { "data": { "text/plain": [ ">" ] }, "execution_count": 86, "metadata": {}, "output_type": "execute_result" } ], "source": [ "f1" ] }, { "cell_type": "code", "execution_count": 87, "metadata": { "collapsed": false }, "outputs": [ { "data": { "text/plain": [ "4" ] }, "execution_count": 87, "metadata": {}, "output_type": "execute_result" } ], "source": [ "f1(3,2)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Isto seria equivalente à definição abaixo (mas mais conciso): " ] }, { "cell_type": "code", "execution_count": 88, "metadata": { "collapsed": true }, "outputs": [], "source": [ "def f2(x,y):\n", " return 2 * x - y" ] }, { "cell_type": "code", "execution_count": 89, "metadata": { "collapsed": false }, "outputs": [ { "data": { "text/plain": [ "4" ] }, "execution_count": 89, "metadata": {}, "output_type": "execute_result" } ], "source": [ "f2(3,2)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## Map e filter\n", "\n", "Uma situação onde funções lambda são úteis é no uso da funções `map` e `filter`.\n", "\n", "`map` recebe uma função e um objeto do qual se podem percorrer os elementos. Ela aplica a função recebida, gerando um elemento para cada elemento original." ] }, { "cell_type": "code", "execution_count": 90, "metadata": { "collapsed": false }, "outputs": [ { "data": { "text/plain": [ "[5, 10, 15, 20]" ] }, "execution_count": 90, "metadata": {}, "output_type": "execute_result" } ], "source": [ "list(map(lambda x: x//2, [10,20,30,40]))" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "No código acima, definimos uma função lambda que divide o valor de seu parâmetro por 2. Essa função é aplicada (por `map`) a todos os elementos da lista fornecida. O resultado é coletado em uma nova lista.\n", "\n", "Um exemplo similar que usa `range` segue." ] }, { "cell_type": "code", "execution_count": 91, "metadata": { "collapsed": false }, "outputs": [ { "data": { "text/plain": [ "[0, 2, 8, 18, 32, 50, 72, 98, 128, 162]" ] }, "execution_count": 91, "metadata": {}, "output_type": "execute_result" } ], "source": [ "list(map(lambda x: 2*x**2, range(10)))" ] }, { "cell_type": "code", "execution_count": 92, "metadata": { "collapsed": false }, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "The slowest run took 4.90 times longer than the fastest. This could mean that an intermediate result is being cached.\n", "100000 loops, best of 3: 9.58 µs per loop\n" ] } ], "source": [ "%timeit list(map(lambda x: 2*x**2, range(10)))" ] }, { "cell_type": "code", "execution_count": 93, "metadata": { "collapsed": false }, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "The slowest run took 5.19 times longer than the fastest. This could mean that an intermediate result is being cached.\n", "100000 loops, best of 3: 6.99 µs per loop\n" ] } ], "source": [ "%timeit [2*x**2 for x in range(10)]" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Se a função fornecida necessita de mais parâmetros, precisamos fornecer uma lista para cada parâmetro.\n", "\n", "No codigo abaixo, `map` irá aplicar `f1(1,0), f1(2,1), f1(3,2), ...`" ] }, { "cell_type": "code", "execution_count": 94, "metadata": { "collapsed": false }, "outputs": [ { "data": { "text/plain": [ "[2, 3, 4, 5, 6, 7, 8, 9, 10, 11]" ] }, "execution_count": 94, "metadata": {}, "output_type": "execute_result" } ], "source": [ "list(map(f1, range(1,11), range(0,10)))" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "A função `filter` é similar a `map`, mas a função fornecida como primeiro parâmetro deve retornar um booleano (`True` ou `False`). Essa função é aplicada a cada elemento da coleção fornecida. Cada elemento para o qual a função retornar `True` será inserido no resultado, enquanto os outros serão descartados." ] }, { "cell_type": "code", "execution_count": 95, "metadata": { "collapsed": false }, "outputs": [ { "data": { "text/plain": [ "[0, 2, 4, 6, 8]" ] }, "execution_count": 95, "metadata": {}, "output_type": "execute_result" } ], "source": [ "list(filter(lambda x: x % 2 == 0, range(10)))" ] }, { "cell_type": "code", "execution_count": null, "metadata": { "collapsed": true }, "outputs": [], "source": [] } ], "metadata": { "kernelspec": { "display_name": "Python [default]", "language": "python", "name": "python3" }, "language_info": { "codemirror_mode": { "name": "ipython", "version": 3 }, "file_extension": ".py", "mimetype": "text/x-python", "name": "python", "nbconvert_exporter": "python", "pygments_lexer": "ipython3", "version": "3.5.2" } }, "nbformat": 4, "nbformat_minor": 0 }