Python

O que é Python?

  • Linguagem interpretada.
  • Dinamicamente tipada.
  • Principais usos em pesquisas e web.
    • Assim como o Linux "vazou" dos servidores para os Desktops, Python parece estar "vazando" do meio acadêmico/pesquisa para os servidores.

O Zen de Python

Começou em uma brincadeira do Tim Peters (um dos desenvolvedores do Python), mas acabou virando um PEP (PEP 20) e hoje faz parte da filosofia de desenvolvimento do Python.

  • Bonito é melhor que feio.
  • Explícito é melhor que implícito.
  • Simples é melhor que complexo.
  • Complexo é melhor que complicado.
  • Plano é melhor que aninhado.
  • Esparço é melhor que denso.
  • Legibilidade conta.
  • Casos especiais não são especiais o suficiente para quebrar as regras.
    • Embora praticabilidade ganhe de puridade.
  • Erros nunca devem passam silenciosamente.
    • A não ser que sejam explicitamente silenciados.
  • Em caso de ambiguidade, evite a tentação de adivinhar.
  • Deve haver um -- e preferencialmente apenas um -- modo óbvio de fazer algo.
    • Embora talvez não seja tão óbvio de primeira a não ser que você seja Holandês.
  • Agora é melhor do que nunca.
    • Embora nunca seja melhor que agora mesmo.
  • Se a implementação é difícil de explicar, é uma péssima idéia.
  • Se a implementação é fácil de explicar, pode ser uma boa idéia.
  • Namespaces são uma grande idéia - vamos fazer mais desses!

As Esquisitices de Python

PEP8

  1. PEPs (Python Enhancement Proposal) são as RFCs que os desenvolvedores Python utilizam para comunicar novas funcionalidades.
  2. Uma funcionalidade não é simplesmente "aceita": É preciso escrever uma proposta de melhoria explicando o problema encontra e como corrigí-lo.
  3. PEP8 define a melhor forma de se programar em Python.
  4. PEP8 não é forçado pelo compilador.
  5. PEP8 garante que qualquer programador Python consiga facilmente encontrar coisas em códigos criados por terceiros.

Um pouco do PEP8:

Variáveis e funções utilizam a notação separado_por_underscores; classes usam CamelCase.

Correto:


MinhaClasse

minha_funcao()
                        

Errado:


minhaClasse

minhaFuncao()
                        

CONSTANTES
                        
Blocos são marcados com 4 espaços e não tabulações.

A maior parte dos editores de código hoje permitem configurar isso facilmente e fazem com que os 4 espaços no começo sejam tratados como tabulação.

Isso garante que o código irá sempre aparecer da mesma forma em qualquer editor, não importando a configuração do autor.

Coluna máxima é 80.

Nenhum código deve passar da coluna 80, não importa o tamanho do seu monitor.

Facilita a leitura e a movimentação dentro do código.

Duas linhas em branco antes de classes, 1 linha em branco antes de funções.
Espaços entre operadores (+, -, /, *), nada de espaços antes de parentêses, colchetes ou chaves.

Correto:


(1 + 2)
array[1]
                        

Errado:


(1+2)
( 1 + 2 )
array [ 1 ]
                        
Nada de espaços entre parâmetros nomeados.

Correto:


funcao(param=1)
                        

Errado:


funcao(param = 1)
                        

Blocos

Em Python, uma identação define um bloco.

Não existe { / }, não existe end, nada. Só identação.

Um bloco sem nada dentro (por algum motivo), deve ser preenchido com, pelo menos, pass.

O interpretador Python


$ python

Python 2.7.5 (default, Jun 25 2014, 10:19:55)
[GCC 4.8.2 20131212 (Red Hat 4.8.2-7)] on linux2
Type "help", "copyright", "credits" or "license" for more information.
>>>
                        

Executando scripts Python:


python meuscript.py
                        

Tipos de Variáveis

Tipos Mutáveis e Tipos Imutáveis

Em Python, o tipo da variável pode ser mutável ou imutável, mas a definição é data pelo tipo e não pelo usuário.

Uma variável do tipo "imutável" não pode ser alterada depois de criada. Tentar modificar o conteúdo da variável vai criar uma nova instância.

Uma variável do tipo "mutável" é o contrário: tentar alterar vai alterar o objeto, não criar um novo.

A importância disto será visto mais pra frente, mas tenha isso em mente.

... ainda...

Existem tipos que são, na verdade, objetos e tem toda uma gama de funções para alterar/manipular/editar o conteúdo de uma variável.

Outros são tipos simples que não são objetos.

None: O nada. Tipo simples.


>>> a = None
                        

bool: Tipo booleano. Tipo simples.


>>> a = True
>>> b = False
                        

int: Um inteiro. Simples.


>>> a = 1
                        

float: Um número com ponto flutuante. Simples.


>>> a = 1.1
>>> b = 1.0
                        

str: Strings. Objeto imutável.


>>> a = 'Python'
>>> b = "Python"
>>> c = """Python
>>> Rocks!"""
                        

unicode: Strings em Unicode. Objeto imutável.


>>> a = u'Python'
                        

list: Listas. Objeto mutável.


>>> a = [1, 2, 'Python', ['Outra lista']]
                        

dict: Um dicionário/objeto/mapa/documento. Objeto mutável.


>>> a = {'Python': 'Rocks',
>>>      1: 1.0}
                        

tuple: Um conjunto de elementos. Objeto imutável.


>>> a = ('Python', 1)
>>> b = (2,)
                        

set: Conjunto de elementos não repetíveis. Objeto mutável.


>>> a = set()
>>> a.add('a')
>>> a
set(['a'])
>>> a.add('a')
>>> a
set(['a'])

                        

E ainda (mas menos importantes):

  • Long (a = 1L)
  • Lambdas (a = lambda a: a + 2)
  • Raw strings (r'string\s', usadas para regex)

Estruturas de Controle

(... que é o nome bonito para coisas tipo if, for...)

Antes de mais nada...

Blocos

if [condição]


>>> if a == 1:
>>>     b = 2
>>> c = 3
                        

while [condição]


>>> a = 1
>>> while True:
>>>     a += 1
>>>     if a > 10:
>>>         break
                        

[condição]

Comparação pode ter mais de um elemento:


>>> a = 1; b = 5; c = 3; d = 1
>>> a == b == c == d
False
>>> a < b > c
True
                        

[condição] (cont.)

Valores "em branco" são considerados falsos:

  • None é falso.
  • "", string vazia, é falso.
  • 0 é falso em qualquer precisão.
  • [], lista vazia, é falso.
  • {}, dicionário vazio, é falso.
  • set(), set vazio, é falso.

[condição] (cont.)

Como valores em branco são falsos, para garantir None usa-se is.


>>> a = 0
>>> not a
True
>>> a is None
False
>>> a = None
>>> not a
True
>>> a is None
True
                        

for [iterável]


>>> soma = 0
>>> for valor em [345, 123, 123, 34]:
>>>     soma += valor
                        

The fuck "ITERÁVEL"?

Um objeto "iterável" é aquele que pode ter elementos acessados usando [ e ].

Tipos iteráveis:

  • Listas (a[2])
  • Tuplas (a[2])
  • Dicionários (a['Python'])
  • Strings/Unicodes (a[2])

Strings como iteráveis:


>>> for l in 'Python':
>>>     print l
                        

Dicionários como iteráveis:


>>> d =  {'Python': 'Rocks', 'Parrot': 'Dead', 'Favorite Color': 'Blue'}
>>> for key in d:
>>>    print key, d[key]
                        

Ou ainda:


>>> d =  {'Python': 'Rocks', 'Parrot': 'Dead', 'Favorite Color': 'Blue'}
>>> for (key, value) in d.iteritems():
>>>     print key, value
                        

Forma considerada "correta" é a anterior.

Slices

Slice é uma "extensão" de indíces de acesso.

Com slices, é possível "cortar" iteráveis, retornando um novo iterável.


iterável[start:end:step]
                        

(end é exclusívo.)


>>> a = [1, 2, 3, 4]
>>> print a[1:2]
[2]
                        

Deixar um índice em branco indica que:

  • start = 0
  • end = len(iterável)
  • step = 1


>>> a = [1, 2, 3, 4]
>>> print a[:2]
[1, 2]
                        

Índices negativos começam do final do iterável.


>>> a = [1, 2, 3, 4]
>>> print a[1:-1]
[2, 3]
                        

Lembre-se que strings também são iteráveis.


>>> a = 'Python Rocks'
>>> print a[7:-1]
'Rock'
                        

Deixar os dois índices em branco cria uma cópia "flat".


>>> a = [1, 2, 3, 4]
>>> print a[:]
[1, 2, 3, 4]
                        

Para fazer uma cópia de uma lista com outros iteráveis internos, existe o módulo deepcopy.

Todos os iteráveis podem ser "medidos" com len():

  • len(list) ⇛ Número de elementos na lista.
  • len(tuple) ⇛ Número de elementos na tupla.
  • len(str) ⇛ Número de caracteres na string.
  • len(dict) ⇛ Número de chaves no dicionário.
  • len(set) ⇛ Número de elementos no set.

Funções

def [nome_da_função]([parâmetro], [parâmetro], ...):


>>> def funcao(a, b, c):
>>>     return (a + b) / c
                        

Parâmetros podem ser nomeados.


>>> def funcao(a, b, c):
>>>     return (a + b) / c
>>>
>>> funcao(b=2, c=3, a=10)
4
                        

Classes

Ok, algumas coisas a serem vistas antes de entrar em classes:

Existem dois tipos de classes: old-style e new-style.

A diferença é que classes "new-style" sempre extendem da classe object, enquanto que "old-style" não extendem ninguém.

Por baixo dos panos, "new-style" e "old-style" funcionam de forma diferente, mas isso não é visível para o programador.

Para todos os casos e efeitos, "old-style" não deve mais ser usado.

No Python 3, não existem mais classes "old-style", mas a sintaxe removeu a necessidade de extender object.

(Ou seja, no Python 3 uma classe se parece com o "old-style" do Python 2.)

this/self não é uma variável implícita da classe: Ela tem que constar sempre na definição do método.

O construtor da classe é chamado __init__.

Não existe função para o destrutor.

Existem ainda outras funções mágicas, mas não vamos falar sobre elas nesse momento.


>>> class MyClasse(object):
>>>     def __init__(self):
>>>         self.valor = 0
>>>     def show(self):
>>>         print self.valor
                        

Para instanciar uma classe, basta chamar a classe como se fosse uma função.


>>> my = MyClasse()
>>> my.show()
0
                        

Se o construtor tiver parâmetros, estes devem ser passados durante a instanciação, como se a "função" classe tivesse parâmetros.


>>> class MyClasse(object):
>>>     def __init__(self, name):
>>>         self.name = name
>>>     def show(self):
>>>         print self.name

>>> my = MyClasse('Julio')
>>> my.show()
Julio
                        

Herança


>>> class A(object):
>>>     def __init__(self):
>>>         self.value = 10
>>> class B(A):
>>>     def __init__(self):
>>>         super(B, self).__init__()
>>>         self.name = 'AAAA'
                        

Herança Múltipla


>>> class A(object):
>>>     def __init__(self):
>>>         self.value = 10
>>> class B(object):
>>>     def __init__(self):
>>>         self.name = 'AAAA'
>>> class C(A, B):
>>>     def __init__(self):
>>>         super(C, self).__init__()
                        

No Python 3, basta usar super().__init__().

Propriedades podem ser injetadas a qualquer momento.


>>> class A(object):
>>>     def __init__(self):
>>>         self.value = 10
>>>
>>> a = A()
>>> a.name = 'Julio'
                        

As Esquisitices de Python

Strings São Imutáveis


>>> a = 'string 1'
>>> b = 'string 2'
>>> c = a + ' ' + b
                        

  • Cria um objeto que é o conteúdo de "a" com um espaço.
  • Cria um novo objeto que é o novo objeto mais o conteúdo de "b".
  • Atribui o novo objeto à "c".

Forma correta de concatenar strings:


>>> a = 'string 1'
>>> b = 'string 2'
>>> c = ' '.join([a, b])
                        

Listas São Mutáveis


>>> def a(l=[]):
>>>     l.append(1)
>>>     print l
>>>
>>> a()
[1]
>>> a()
[1, 1]
                        

Forma correta de lidar com parâmetros mutáveis:


>>> def a(l=None):
>>>     if not l:
>>>         l = []
>>>     l.append(1)
>>>
>>> a()
[1]
>>> a()
[1]
                        

Stars

"Stars" servem para empacotar e desempacotar parâmetros indefinidos.


>>> def a(*args):
>>>     print args
>>>
>>> a(1)
[1]
>>> a(1, 2, 3, 4, 5)
[1, 2, 3, 4, 5]
                        

* pega somente os parâmetros que não tem nome.


>>> def a(**kwargs):
>>>     print kwargs
>>>
>>> a(a=1)
{'a': 1}
>>> a(value1=10, a=2)
{'value1': 10, 'a': 2}
                        

** pega somente os parâmetros que tem nome.


>>> def a(*args, **kwargs):
>>>     print args
>>>     print kwargs
>>>
>>> a(a=1)
[]
{'a': 1}
>>> a(1, 2, 3, a=5)
[1, 2, 3]
{'a': 5}
                        


>>> def a(a, b, *args, name=None, **kwargs):
>>>     print 'a =', a
>>>     print 'b =', b
>>>     print 'args =', args
>>>     print 'name = ', name
>>>     print 'kwargs =', kwargs
                        

Saída de uma chamada desta função fica a cargo do leitor.

"Functions are First Class Citizens"


>>> def funcao(a, b, c):
>>>     return (a + b) / c
>>>
>>> def check(a, b, c, condition, function):
>>>     if condition:
>>>         print function(a, b, c)
>>>
>>> check(1, 2, 3, True, funcao)
1
>>> check(1, 2, 3, False, funcao)
>>>
                        

Como funções são cidadãos de primeira classe e classes podem ter funções injetadas, pode-se extender uma classe em tempo de execução.


>>> class A(object):
>>>     def __init__(self):
>>>         self.value = 10
>>>
>>> def show_name(self):
>>>     print 'Julio'
>>>
>>> a = A()
>>> a.show = show_name
>>> a.show()
                        

Decorators

A idéia dos decorators é cria uma função que altera a funcionalidade de uma função.

A forma mais simples de entender decorators é pensar neles como funções que encapsulam callbacks.


>>> def retrieve(connection):
>>>     # faz algo com a conexão para recuperar dados.
                        

Problema: antes de sair executando algo na conexão, tem que ser verificado se a conexão está ativa.

Solução menos óbvia: Criar uma função que verifica a conexão e, se ela estiver ok, chama a função.


>>> def retrieve(connection):
>>>     # faz algo com a conexão para recuperar dados.
>>>
>>> def update(connection):
>>>     # atualiza algo usando a função
>>>
>>> def check(connection, call):
>>>     if not connection.is_connected:
>>>         connection.retry()
>>>     call(connection)
                        

Novo problema: Todo lugar onde antes era chamado retrieve agora precisa ser alterado para check(connection, retrieve) e todo lungar onde era chamado update precisa ser alterado para check(connection, update).

Solução mais simples: decorators.


>>> from functools import wrap
>>>
>>> def check(func):
>>>     def check_conn(*args, **kwargs):
>>>         # acha a conexão em args ou kwargs
>>>         if not connection.is_connected:
>>>             connection.retry()
>>>         return func(*args, **kwargs)
>>>     return check_conn
>>>
>>> @check
>>> def retrieve(connection):
>>>     # faz algo com a conexão para recuperar dados
                        

Não precisa alterar nenhuma chamada de retrieve.

Lembre-se: O resultado de uma função decorator é uma função.

Decorators com classes

>>> class CheckConn(object):
>>>    def __init__(self, func):
>>>        self.func = func
>>>
>>>     def __call__(self, *args, **kwargs):
>>>         if 'connection' in kwargs:
>>>             connection = kwargs['connection']
>>>         else:
>>>             connection = args[0]
>>>
>>>         if not connection.is_connected:
>>>             connection.retry()
>>>         self.func(*args, **kwargs)

>>> @CheckCon
>>> def retrieve(connection):
>>>     # retrieve
                        
Decorators mais comuns:
  • @property
  • @staticmethod
@property

>>> class CheckConn(object):
>>>    def __init__(self, func):
>>>        self._func = func
>>>
>>>    @property
>>>    def func(self):
>>>        return self._func
>>>
>>>    @func.setter
>>>    def func(self, value):
>>>        self._func = func
                        
@staticmethod

>>> class CheckConn(object):
>>>    def __init__(self, func):
>>>        self._func = func
>>>
>>>    @staticmethod
>>>    def from_text(self, text):
>>>        return CheckConn(getattr(self, text))
                        

Comprehensions e Generators

Python permite criar listas processando listas sem for com list comprehensions.


>>> a = [1, 2, 3]
>>> [item * 2 for item in a]
>>> [2, 4, 6]
						

Pra quem gosta de coisas "funcionais", é o mesmo que map.


>>> a = [1, 2, 3]
>>> map(lamba f: f * 2, a)
>>> [2, 4, 6]
						

Comprehensions (contd.)

É possível filtrar elementos com list comprehensions.


>>> a = [1, 2, 3]
>>> [item for item in a if item > 2]
>>> [3]
						

Funcionalmente, é o mesmo que filter.


>>> a = [1, 2, 3]
>>> filter(lambda f: f > 2, a)
>>> [3]
						

Generators

Enquanto que comprehensions criam novas listas, generators geram elementos sob demanda.


>>> a = [1, 2, 3]
>>> (item * 2 for item in a)
<generator object <genexpr> at 0x7f8673dfc050>
						
Generators (contd.)

>>> [item for item in range(5000000)]
						

vs


>>> (item for item in xrange(5000000))
						
Generators (contd.)

>>> [item for item in range(5000000)][:5]
						

vs


>>> (item for item in xrange(5000000))[:5]
						
Generators (contd.)

>>> def gen(max_value):
>>> 	for value in xrange(max_value):
>>> 		yield value * 2
						

Generator functions não podem ter return!

Context Managers

Context Managers são usados como "marcadores" para entrada e saída de pontos específicos.

Context Managers


>>> class Connection(object):
>>>    def __init__(self):
>>>        self._conn = None
>>>
>>>    def __enter__(self):
>>>        self._conn = self._make_connection()
>>>        return self._conn
>>>
>>>    def __exit__(self, exc_type, exc_value, traceback):
>>>        self._conn.close()
>>>        if exc_type:    # then exc_value and traceback
>>>            print "Exception!", exc_type, exc_value
>>>            print traceback
						

>>> with Connection() as connection:
>>>     connection.request('Value')
						

Context Managers vs Exceptions


>>> try:
>>>     conn = self._make_connection()
>>>     conn.request('value')
>>> finally:
>>>     conn.close()
>>> except Exception as exc:  # Bare exceptions are BAAAADDD!
>>>     print 'Exception!', exc
						

Docstrings

Documentação de funções/classes é feita com uma string logo abaixo da declaração da mesma.


>>> def func(a):
>>>     """Função mágica"""
>>>     return a

>>> print func.__doc__
Função mágica

>>> help(func)
                        

Sphinx

Sphinx é o sistema de geração de documentação do Python.

PEP 257

PEP 257 fala sobre formato de documentação de funções.

PEP 257
  • Docstrings devem ter três aspas duplas.
  • Para classes, uma linha em branco antes e uma depois.
  • Para funções, sempre logo depois.
  • Se a documentação passar da coluna 80, as três aspas que fecham a docstring devem ficar em uma linha em separado.

class MyClass(object):

    """This is my class. It is very long and I should break it
    into several lines.
    """

    def __init__(self):
        """Initialization."""
        self.value = None
                        

Sphinx Format

O Sphinx tem um formato específico para documentação.

  • Parametros são documentados com :param [nome]:
  • Tipo do parametro é documetnado com :type [tipo]:
  • Retorno é marcado como :return:
  • Tipo de retorno é definido em :rtype:
  • Exceções podem ser descritas em :raises [exceção]:

Sphinx Format (contd.)


def send(sender, recipient, message_body, priority):
   """Send a message to a recipient

   :param str sender: The person sending the message
   :param str recipient: The recipient of the message
   :param str message_body: The body of the message
   :param priority: The priority of the message, can be a number 1-5
   :type priority: integer or None
   :return: the message id
   :rtype: int
   :raises ValueError: if the message_body exceeds 160 characters
   :raises TypeError: if the message_body is not a basestring
   """
   pass
                        

(como gerar documentação Sphinx não será apresentado por questões de espaço.)

(mas é simples: um arquivo conf com os diretórios a serem pesquisados e um makefile com a opção html para geração de arquivos HTML.)

VirtualEnv