1-funcoes-objetos

55
Funções como objetos Luciano Ramalho [email protected]

Upload: diego-ponciano

Post on 17-Aug-2015

214 views

Category:

Documents


2 download

DESCRIPTION

Pyt

TRANSCRIPT

Funes como objetosLuciano [email protected] de primeira classeTerminologiaen pt-br!rst class function funo de primeira classe!rst class object objeto de primeira classe" !rst class citizen " cidado de primeira classeObjetos de 1 classePodem ser construdos em tempo de execuoEx: nmeros, strings, listas, dicts etc.at classes so objetos de 1 classe em Python (metaclasses constroem classes!)Podem ser atribudos a variveis, passados como argumentos, devolvidos como resultados de funesFunes de 1 classePodem manipuladas da mesma forma que outros objetos de primeira classe:criar em tempo de execuoatribuir a uma varivelarmazenar em uma estrutura de dadospassar como argumentoDemonstrao>>> def fatorial(n):... '''devolve n!'''... return 1 if n < 2 else n * fatorial(n-1)...>>> fatorial(42)1405006117752879898543142606244511569936384000000000>>> fat = fatorial>>> fat(5)120>>> map(fat, range(10))

>>> list(map(fat, range(11)))[1, 1, 2, 6, 24, 120, 720, 5040, 40320, 362880, 3628800]>>> fat

>>> type(fatorial)

>>>Demonstrao (cont.)>>> dir(fatorial)['__annotations__', '__call__', '__class__', '__closure__', '__code__', '__defaults__', '__delattr__', '__dict__', '__dir__', '__doc__', '__eq__', '__format__', '__ge__', '__get__', '__getattribute__', '__globals__', '__gt__', '__hash__', '__init__', '__kwdefaults__', '__le__', '__lt__', '__module__', '__name__', '__ne__', '__new__', '__qualname__', '__reduce__', '__reduce_ex__', '__repr__', '__setattr__', '__sizeof__', '__str__', '__subclasshook__']>>> fatorial.__name__'fatorial'>>> fatorial.__doc__'devolve n!'>>> fatorial.__code__

Demonstrao (cont.)>>> dir(fatorial.__code__)['__class__', '__delattr__', '__dir__', '__doc__', '__eq__', '__format__', '__ge__', '__getattribute__', '__gt__', '__hash__', '__init__', '__le__', '__lt__', '__ne__', '__new__', '__reduce__', '__reduce_ex__', '__repr__', '__setattr__', '__sizeof__', '__str__', '__subclasshook__', 'co_argcount', 'co_cellvars', 'co_code', 'co_consts', 'co_filename', 'co_firstlineno', 'co_flags', 'co_freevars', 'co_kwonlyargcount', 'co_lnotab', 'co_name', 'co_names', 'co_nlocals', 'co_stacksize', 'co_varnames']>>> fatorial.__code__.co_varnames('n',)>>> fatorial.__code__.co_codeb'|\x00\x00d\x01\x00k\x00\x00r\x10\x00d\x02\x00S|\x00\x00t\x00\x00|\x00\x00d\x02\x00\x18\x83\x01\x00\x14S'Demonstrao (cont.)>>> import dis>>> dis.dis(fatorial.__code__.co_code)0 LOAD_FAST 0 (0)3 LOAD_CONST1 (1)6 COMPARE_OP0 (> 16 LOAD_FAST 0 (0) 19 LOAD_GLOBAL 0 (0) 22 LOAD_FAST 0 (0) 25 LOAD_CONST2 (2) 28 BINARY_SUBTRACT 29 CALL_FUNCTION 1 (1 positional, 0 keyword pair) 32 BINARY_MULTIPLY 33 RETURN_VALUEAtributos de funesAtributos de funesRW__doc__ str documentao (docstring)RW__name__ str nome da funoRW__module__ str nome do mdulo onde a funo foi de!nidaRW__defaults__ tuple valores default dos parmetros formaisRW__code__ code bytecode do corpo da funo + metadadosR__globals__ dict variveis globais do mduloRW__dict__ dict atributos criados pelo programadorR__closure__ tuple associaes para as variveis livres RW__annotations__ dict anotaes de parmetros e retornoRW__kwdefaults__ dict valores default dos parmetros nomeadosImplicaes de funes de 1 classeFunes como objetos de primeira classe abrem novas possibilidades de organizao de programasParadigma funcional: mais de 50 anos de histriafundamentos matemticos: clculo lambdaRepensar padres de projeto!Peter Norvig:Design Patterns in Dynamic ProgrammingProgramao funcionalParadigma que enfatiza o uso de:funes purasestruturas de dados imutveiscomposio de funesfunes de ordem superiorFunes purasSem efeitos colaterais: no modi!cam seus argumentos nem alteram o estado do sistema exceto pela criao de um um novo objeto (o resultado da funo)Pascal define duas construes distintas:function x procedureFunes puras, impuraspuras impurasint(qtd) random.shu#e(cartas)sorted(colecao) lista.sort()texto.replace(x, ) dicionario.pop(x)format(66, x) print(0x2a)ast.literal_eval(2 + 2) eval(codigo_fonte)Funo de ordem superiorAceita funes como argumentose/ou devolve funo como resultado>>> frutas = ['pequi', 'uva', 'caju', 'banana', 'caqui', 'umbu']>>> sorted(frutas)['banana', 'caju', 'caqui', 'pequi', 'umbu', 'uva']>>> sorted(frutas, key=len)['uva', 'caju', 'umbu', 'pequi', 'caqui', 'banana']>>> def invertida(palavra):... return palavra[::-1]...>>> sorted(frutas, key=invertida)['banana', 'uva', 'caqui', 'pequi', 'umbu', 'caju']Objetos invocveisDocumentao o!cialLanguage Reference > Data modelhttp://docs.python.org/dev/reference/datamodel.htmlSeo 3.2 - The standard type hierarchy > Callable typesTipos invocveisUser-de!ned functions: def ou lambdaInstance methods: invocados via instnciasGenerator functions: funes com yieldBuilt-in functions: escritas em C (no CPython)Built-in methods: idemClasses: mtodos __new__ e __init__Class Instances: mtodo __call__def e lambda>>> def fatorial(n):... '''devolve n!'''... return 1 if n < 2 else n * fatorial(n-1)...>>> fat2 = lambda n: 1 if n < 2 else n * fat2(n-1)>>>>>> fat2(42)1405006117752879898543142606244511569936384000000000>>> type(fat2)

>>> type(fatorial)

>>>Funes annimaslambdaAcar sinttico para de!nir funes dentro de expressesNormalmente: em chamadas de funoLimitadas a uma expresso em PythonNo podem usar estruturas de controle, atribuio etc.>>> sorted(frutas, key=lambda s: s[::-1])['banana', 'uva', 'caqui', 'pequi', 'umbu', 'caju']lambda cria uma funo annima>>> fat2 = lambda n: 1 if n < 2 else n * fat2(n-1)>>> fat2.__name__''>>>lambda cria uma funo annima>>> fat2 = lambda n: 1 if n < 2 else n * fat2(n-1)>>> fat2.__name__''>>>Funes annimas tm um grave defeito: elas no tm nome.AnnimoMtodo Lundh para refatorar lambdas1. Escreva um comentrio explicando o que a funo annima faz.2. Estude o comentrio atentamente, e pense em um nome que capture a essncia do comentrio.3. Crie uma funo usando o comando def, usando este nome.4. Remova o lambda e o comentrio.Python Functional Programming How-to:http://bit.ly/159IQmULambdalista = sorted(frutas, key=lambda s: s[::-1])Lambda refatoradolista = sorted(frutas, key=lambda s: s[::-1])def invertida(palavra):return palavra[::-1]lista = sorted(frutas, key=invertida)DecoratorsDecorador simplesFuno de ordem superior: recebe uma funo como argumento, retorna outrafrom time import strftimedef logar(func):def logadora(*args, **kwargs):res = func(*args, **kwargs)print(strftime('%H:%M:%S.%m'), args, kwargs, '->', res)return resreturn logadora@logardef dobro(n):return n*2>>> x = dobro(21)13:32:42.10 (21,) {} -> 42>>> dobro.__name__'logadora'Como interpretado@decdef func(): pass@dec2(arg)def func(): passdef func(): passfunc = d1(arg)(d2(func))def func(): passfunc = dec(func)def func(): passfunc = dec2(arg)(func)@d1(arg)@d2def func(): pass o mesmo que o mesmo que o mesmo queDemo decorador>>> l = [dobro(x) for x in range(4)]13:15:24.10 (0,) {} -> 013:15:24.10 (1,) {} -> 213:15:24.10 (2,) {} -> 413:15:24.10 (3,) {} -> 6>>> l[0, 2, 4, 6]>>>>>> g = (dobro(x) for x in range(4))>>> g

>>> list(g)13:15:44.10 (0,) {} -> 013:15:44.10 (1,) {} -> 213:15:44.10 (2,) {} -> 413:15:44.10 (3,) {} -> 6[0, 2, 4, 6]>>>Decorator: memoizarimport functoolsdef memoizar(func):cache = {}@functools.wraps(func)def memoizada(*args, **kwargs):chave = (args, str(kwargs))if chave not in cache:cache[chave] = func(*args, **kwargs)return cache[chave]return memoizadaExemplo apenas didtico. A biblioteca padro j tem functools.lru_cache Demo @memoizar$ python3 fibonacci.pyfibo_loop 0.00060.00110.00160.00290.0030fibo_iter 0.00130.00200.00260.00340.0043fibo_calc 0.00140.00140.00150.00150.0014fibo_rec0.00210.02880.28223.2485 35.2203fibo_rec_memo 0.00150.00150.00170.00450.0367fibo_rec_lruc 0.00920.00850.00860.00890.0087$def fibo_rec(n):if n < 2:return nreturn fibo_rec(n-2) + fibo_rec(n-1)@memoizardef fibo_rec_memo(n):if n < 2:return nreturn fibo_rec(n-2) + fibo_rec(n-1)ComparandoComparandoComparandoComparandoComparandoComparandoDecoradores prontosBuilt-insproperty, classmethod, staticmethodPacote functoolslru_cache, partial, wrapOutros...Python Decorator Library (in wiki.python.org)https://wiki.python.org/moin/PythonDecoratorLibraryClosuresQual o problema?O corpo de uma funo pode conter variveis livres (no locais)Funes de primeira classe podem ser de!ndas em um contexto e usadas em outro contexto totalmente diferenteAo executar uma funo, como resolver as associaes das variveis livres?Escopo dinmico ou escopo lxico?Demo closure>>> conta1 = criar_conta(1000)>>> conta2 = criar_conta(500)>>> conta1()1000>>> conta1(100)1100>>> conta1()1100>>> conta2(-300)200>>> conta2(-300)Traceback (most recent call last):...ValueError: Saldo insuficiente>>> conta2()200Uma closuredef criar_conta(saldo_inicial):# precisamos de um objeto mutvelcontexto = {'saldo':saldo_inicial}def conta(movimento=0):if (contexto['saldo'] + movimento) < 0:raise ValueError('Saldo insuficiente')contexto['saldo'] += movimentoreturn contexto['saldo']return contaA closure da funo conta preserva a associao da varivel livre contexto com seu valor no escopo lxico (o local da de!nio da funo, no de sua invocao)Demo closure>>> conta1 = criar_conta(1000)>>> conta1()1000>>> conta1.__closure__[0].cell_contents{'saldo': 1000}>>> conta1(100)1100>>> conta1()1100Exemplo errado!!!def criar_conta(saldo_inicial):saldo = saldo_inicial def conta(movimento=0):if (saldo + movimento) < 0:raise ValueError('Saldo insuficiente')saldo += movimentoreturn saldoreturn conta>>> c1 = criar_conta(99)>>> c1()Traceback (most recent call last):File "", line 1, in File "/Users/luciano/prj/python.pro.br/github/func_objects/saldo0.py", line 35, in contaif (saldo + movimento) < 0:UnboundLocalError: local variable 'saldo' referenced before assignmentnonlocalDeclarao de varivel no-local, disponvel somente a partir do Python 3.0def criar_conta(saldo):def conta(movimento=0):nonlocal saldoif (saldo + movimento) < 0:raise ValueError('Saldo insuficiente')saldo += movimentoreturn saldoreturn contaAnnotationsPorqueanotaes?Usos interessantes citados por Raymond Hettinger e outros no StackOver$ow [1]:documentaoveri!cao de pr-condiesmulti-mtodos / sobrecarga [2][1] http://bit.ly/py-why-annotations[2] https://pypi.python.org/pypi/overloadDica: no para transformar Python em Java!Exemplo anotaesdef truncar(texto:str, largura:'int > 0'=80) -> str:'''devolve o texto truncado no primeiro espao at a largura, ou no primeiro espao aps a largura, se existir'''termino = Noneif len(texto) > largura:pos_espaco_antes = texto.rfind(' ', 0, largura)if pos_espaco_antes >= 0:termino = pos_espaco_anteselse:pos_espaco_depois = texto.rfind(' ', largura)if pos_espaco_depois >= 0:termino = pos_espaco_depoisif termino is None:return texto.rstrip()else:return texto[:termino].rstrip()Demo anotaes>>> truncar.__annotations__{'largura': 'int > 0', 'texto': , 'return': }>>> truncar.__defaults__(80,)>>> from inspect import signature>>> assi = signature(truncar)>>> assi.parametersmappingproxy(OrderedDict([('texto', ), ('largura', )]))>>> for nome, par in assi.parameters.items():... print(nome, ':', par.name, par.default, par.kind)...texto : texto POSITIONAL_OR_KEYWORDlargura : largura 80 POSITIONAL_OR_KEYWORDFerramentasMdulos que ajudam na programao funcionalMdulo functools: vrias funes de ordem superiorMdulo itertools: inspirado pela biblioteca padro de Haskell, uma das linguagens funcionais mais purasMdulo operator: operadores bsicos de Python implementados como funesmuito teis: attrgetter e itemgetter