Planeta

  • Goncalo Gomes

    One of the many great advantages of python is introspection. Python’s introspective design allows you to read, modify, write (and in some cases call) the functions, objects, variables defined in the current running instance of python. Amongst other things it allows for your programs to do smart things such as listing all methods and properties of an object instance, obtaining a list of frames and accessing your call stack.

    One such case I needed to use introspection was in a debug function where I want it to print who the caller is, so that if I get a flood of debug messages, I know what their caller was. For my own needs, I don’t need to be specific as to the frame address because I know each parent would only have a single call to my debugging function, I just can’t tell which order they would be calling in due to the dynamic nature of my program.

    That said, there is a simple way to do this. Python comes bundled with an extensive library of modules, one of which is the inspect module. This module defines a function called stack which returns a list of records for the stack above the caller’s frame, where 0 is the parent function to your inspect.stack() call and -1 (which is an alias to the last item in a list) is the <module>, or the bare python interpreter prior to __main__. The return list is essentially a list of record frames packed as tupples. Each record contains a frame object, filename, line number, function name, a list of lines of context, and index within the context.

    #!/usr/bin/python
    import inspect
    
    def foo():
        debug("starting...")
    
    def main():
        foo()
    
    def debug(x):
        print "caller=%s :: %s" % (inspect.stack()[1][3], x)
    
    if __name__ == '__main__':
        main()
    

    The output is as follows:

    $ python foo.py
    caller=foo :: starting...
    

  • Sérgio Silva
    Um post muito interessante de Steven Lott sobre as principais características da linguagem de programação Python e qual o "melhor" caminho a seguir para se alcançar um melhor domínio da linguagem Python. O post é principalmente direccionado para a utilização do Python no processamento de grandes volumes de dados mas é facilmente aplicável a outras áreas de aplicação. ...


    Ver post completo




    "Big Picture do Python. Qual o caminho a seguir?" é um post originalmente publicado no blog Sergio Silva Blog

  • Sérgio Silva
    Muito bom post de Yusuke Tsutsumi sobre a utilização de Decorators em Python para facilitar a reutilização de código - conceito de programação DRY (Don't Repeat Yourself). Um post muito didáctico, com exemplos, bem escrito e de fácil leitura. ...


    Ver post completo




    "Reutilização de código em Python com o uso de decorators - programação DRY" é um post originalmente publicado no blog Sergio Silva Blog

  • Sérgio Silva
    É conveniente quando se escreve código seguir um conjunto de regras que facilitem a leitura desse mesmo código. Nas minhas pesquisas sobre a linguagem de programação Python encontrei um documento do Google com uma lista de boas práticas na escrita de programas com código Python. O documento pode ser acedido a partir daqui Google Python Style Guide ...


    Ver post completo




    "Guia de boas práticas para código Python do Google" é um post originalmente publicado no blog Sergio Silva Blog

  • Sérgio Silva
    Ainda haveria mais a dizer sobre os ciclos na linguagem de programação Python, mas acho que o principal ficou bem expresso nesta série de posts. Como conclusão: - Quando definimos uma classe, podemos torná-la iterável implementando o método especial __iter__(); - List comprehensions são uma forma compacta de criar listas sobre iteráveis. Permitem tornar o código muito conciso; ...


    Ver post completo




    "Ciclos em Python, conclusão" é um post originalmente publicado no blog Sergio Silva Blog

  • Sérgio Silva
    A melhor forma de abstrair as nossas iterações é fazer os nossos próprios objectos iteráveis. Neste post veremos como podemos tornar os nossos objectos em iteráveis. ...


    Ver post completo




    "Ciclos em Python, como tornar os nossos objectos em Iteráveis" é um post originalmente publicado no blog Sergio Silva Blog

  • Sérgio Silva
    Neste post vou falar um pouco sobre os mecanismos de baixo nível presentes na iteração da linguagem de programação Python. Existem dois tipos de objectos envolvidos na iteração. Até agora tenho falado nos iteráveis que são objectos que contêm um conjunto de valores. Os iteráveis não podem contudo ser percorridos directamente, para fazer isso é necessário solicitar ao iterável o iterador. ...


    Ver post completo




    "Ciclos em Python, operações de baixo nível" é um post originalmente publicado no blog Sergio Silva Blog

  • Sérgio Silva
    Neste post veremos outro exemplo da forma de repensarmos a iteração. Supondo que temos de percorrer uma estrutura bidimensional, como uma folha de cálculo. Uma forma de o fazer seria com dois ciclos um dentro do outro, o primeiro ciclo percorria as linhas e o segundo as colunas. As variáveis dos dois ciclos juntos seriam usadas para aceder ao conteúdo das células da folha de cálculo. ...


    Ver post completo




    "Ciclos em Python, Generators - parte 3" é um post originalmente publicado no blog Blogue pessoal de Sergio Silva

  • Sérgio Silva
    Os generatores são uma poderosa ferramenta, providenciada pela linguagem de programação Python, para a abstracção da iteração. Tal como as funções são boas para abstrair uma série de expressões, e as classes são boas para abstrair uma colecção de dados e métodos para os manipular, os generatores são bons para abstrair a iteração. ...


    Ver post completo




    "Ciclos em Python, Generators - parte 2" é um post originalmente publicado no blog sergiosilvablog.com

  • Sérgio Silva
    Os generators são uma forma de criar iteráveis através de uma função.

    Uma função normal retorna um valor com o uso da palavra return. Um generator é tal como uma função, mas em vez de usar a palavra return usa a palavra yield uma ou mais vezes (pode também usar a palavra return para sair da função). Ao chamar a função generator é criado um iterável, e ao iterar sobre o mesmo é executado o código do generetor. Cada vez que um yield é executado ele produz um novo valor no stream do iterável.


    def ola_mundo():
    yield "Ola"
    yield "mundo"

    for x in ola_mundo():
    print x


    Ola
    mundo



    Este pequeno exemplo ilustrativo de um generator, contém apenas duas chamadas da expressão yield. Ao iterarmos sobre a função ola_mundo() com um ciclo for verificamos que este generator produz uma stream de dois valores, "Ola" e "mundo".

    Os generatores normalmente não são assim tão simples, tipicamente o código de um generator terá um ciclo for com chamadas à expressão yield.


    def numeros_pares(stream):
    for n in stream:
    if n % 2 == 0:
    yield n

    for n in numeros_pares(nums):
    faz_algo(n)

    Podemos reescrever a função numeros_pares() como um generator. O código é praticamente o mesmo, mas em vez de produzir uma lista e devolvê-la no fim da função, simplesmente produz os valores assim que os encontra.

    Chamamos esta função numeros_pares() da mesma forma, passando-lhe o iterável que queremos que seja trabalhado e a função devolve-nos um iterador que produzirá os valores pares desse iterável.

    A função anterior de numeros_pares() construía uma lista de números pares que nos devolvia no fim, isso significa que nada mais acontecia no nosso programa enquanto a função não examinasse a sequência completa do iterável e seleccionasse os valores pares para a nova lista. Para um iterável pequeno isso poderá não ser um problema mas será certamente para um iterável grande.

    Já a versão generator da função devolverá o primeiro número par que encontrar deixando o nosso programa disponível para prosseguir com o seu trabalho antes que toda a sequência do iterável seja examinada. Isto significa que a função pode trabalhar indefinidamente ou mesmo com streams infinitas.

    Os generatores têm a vantagem de serem "preguiçosos", eles não "trabalham" até que o primeiro valor lhe seja pedido, e quando isso acontece limitam-se a dar esse valor e voltam a parar, não voltando a "trabalhar" até que o próximo valor seja pedido. Isto faz com que os generators usem poucos recursos e sejam mais indicados para certos tipos de iteráveis.

    Os generators podem ser inicialmente confusos, eles são muito semelhantes a funções mas têm diferenças importantes. Primeiro chamar um generator não faz com que o seu código seja imediatamente executado, ele simplesmente cria um iterável. Só quando um ciclo for ou outro "consumidor" do iterável começar a pedir valores do stream é que o código do generator começa a ser executado.

    Quando a expressão yield é encontrada, a execução é suspensa, e o valor é usado como o próximo valor da stream. Numa função normal a expressão return termina a função. Mas num generator o estado actual é memorizado e quando o próximo valor for solicitado, a execução do generator continua onde tinha ficado anteriormente, depois da expressão yield. O próximo valor da stream é produzido quando uma expressão yield é novamente executada. Todas as variáveis locais do generator mantêm os seus valores durante todo o percurso do stream. Isto torna os generatores muito convenientes para escrever iteráveis.

    O iterador termina quando o generator retorna, quer explicitamente com a expressão return, ou quando executa a última expressão do código do generator.




    A série de posts sobre ciclos em Python tem continuação nos próximos capítulos.




    A inspiração para este post veio daqui.


    "Ciclos em Python, Generators - parte 1" é um post originalmente publicado no blog sergiosilvablog.com