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.
Correto:
MinhaClasse
minha_funcao()
Errado:
minhaClasse
minhaFuncao()
CONSTANTES
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.
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.
Correto:
(1 + 2)
array[1]
Errado:
(1+2)
( 1 + 2 )
array [ 1 ]
Correto:
funcao(param=1)
Errado:
funcao(param = 1)
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
.
$ 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.
>>>
Python 2 (todas as versões), deixarão de ser suportadas em 2020!
Executando scripts Python:
python meuscript.py
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):
a = 1L
)a = lambda a: a + 2
)r'string\s'
, usadas para regex)(... que é o nome bonito para coisas tipo if
, for
...)
if [condição]
>>> if a == 1:
... b = 2
>>> c = 3
while [condição]
>>> a = 1
>>> while True:
... a += 1
... if a > 10:
... break
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
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.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
Um objeto "iterável" é aquele que pode ter elementos
acessados usando [
e ]
.
Tipos iteráveis:
a[2]
)a[2]
)a['Python']
)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.
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
= 0end
= 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.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
>>> def funcao(a, b):
... def inner(b):
... return b * 2 if b > 0 else b * -2
... return inner(a) + b
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.
(Existe, mas ninguém usa.)
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
Propriedades podem ser injetadas a qualquer momento.
>>> class A(object):
... def __init__(self):
... self.value = 10
>>>
>>> a = A()
>>> a.name = 'Julio'
Inserção de propriedades pode ser
barrada com o uso da variável mágica
__slots__
, mas raramente é usado.
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__()
.
super()
é mais usado para resolução
hierárquia com herança múltipla.
>>> class Adao(object): pass
>>> class Eva(object): pass
>>> class AvoPaterno(Adao, Eva): pass
>>> class AvohPaterna(Adao, Eva): pass
>>> class AvoMaterno(Adao, Eva): pass
>>> class AvohMaterna(Adao, Eva): pass
>>> class Pai(AvoPaterno, AvohPaterna): pass
>>> class Mae(AvoMaterno, AvohMaterna): pass
>>> class Filho(Pai, Mae): pass
>>> help(Filho)
Help on class Filho in module __main__:
class Filho(Pai, Mae)
| Method resolution order:
| Filho
| Pai
| AvoPaterno
| AvohPaterna
| Mae
| AvoMaterno
| AvohMaterna
| Adao
| Eva
| __builtin__.object
A ordem de resolução pode ser usada para inserir mocks sem o uso de mocks.
... embora só sirva para mockar objetos "é um" e não "contém um".
>>> class Robo(object):
... def pegar(self, ferramenta):
... print 'Pegando', ferramenta
... def pra_frente(self):
... print 'Movendo pra frente'
... def pra_tras(self):
... print 'Voltando'
... def largar(self):
... print 'Largando ferramenta'
>>> class RoboDeLimpeza(Robo):
... def clean(self, repeticoes=10):
... super(Robo, self).pegar('vassoura')
... for _ in xrange(repeticoes):
... super(Robo, self).pra_frente()
... super(Robo, self).pra_tras()
... super(Robo, self).largar()
>>> class MockRobo(Robo):
... def __init__(self):
... self.acoes = []
... def pegar(self, ferramenta):
... self.acoes.append('Pegar {}'.format(ferramenta))
... def pra_frente(self):
... self.acoes.append('frente')
... def pra_tras(self):
... self.acoes.append('tras')
... def largar(self):
... self.acoes.append('largar')
>>> class MockRoboDeLimpeza(RoboDeLimpeza, MockRobo):
... pass
Help on class MockRoboDeLimpeza in module __main__:
class MockRoboDeLimpeza(RoboDeLimpeza, MockRobo)
| Method resolution order:
| MockRoboDeLimpeza
| RoboDeLimpeza
| MockRobo
| Robo
| __builtin__.object
Mais informações no vídeo de Raymond Hettinger: Super Considered super!
>>> class Outer(object):
... class Inner(object):
... def __init__(self):
... self.value = 1
... def __init__(self):
... self.value = Inner()
Existem dois tipos de módulos:
modulo.py
def funcao(a, b):
return a * b
>>> import modulo
>>> modulo.funcao(1, 2)
No caso, o "namespace" do módulo (modulo) foi importando para o namespace atual; as funções permanecem dentro do namespace original.
>>> from modulo import funcao
>>> funcao(1, 2)
Importado apenas uma função do módulo modulo
;
a função é adicionada no namespace atual.
Para um diretório ser reconhecido como módulo, é preciso
que exista um arquivo __init__.py
dentro dele.
... apenas para Python 2; Python 3 removeu esse requisito.
utils/modulo.py
def funcao(a, b):
return a * b
Sem o arquivo __init__.py
, o import falha
(no Python 2)
>>> import utils.module
Traceback (most recent call last):
File "", line 1, in
ImportError: No module named utils.module
Com o arquivo __init__.py
, o import funciona.
>>> import utils.module
>>> utils.module.funcao(1, 2)
Importando o módulo sem o nome do arquivo, é importado o conteúdo
do próprio __init__.py
:
__init__.py
def mult(a, b):
return a * b
>>> import utils
>>> utils.mult(1, 2)
>>> a = 'string 1'
>>> b = 'string 2'
>>> c = a + ' ' + b
Forma correta de concatenar strings:
>>> a = 'string 1'
>>> b = 'string 2'
>>> c = ' '.join([a, b])
>>> 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" 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.
for
tem else
>>> for i in xrange(10):
... if i == 1:
... break
... else:
... print 'Oh no'
WAT
for
tem else
else
é chamado se a execução do loop
chegar ao final.
>>> for record in all_content:
... if record.name = search:
... found = record
... break
... else:
... found = None
try
tem else
>>> try:
... func_that_raises_exception()
... else:
... print 'Yay!'
else
é chamado quando
exceções não ocorrerem.
finally
SEMPRE é chamado.
>>> 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()
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.
>>> 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
@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))
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]
É 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]
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>
>>> [item for item in range(5000000)]
vs
>>> (item for item in xrange(5000000))
>>> [item for item in range(5000000)][:5]
vs
>>> (item for item in xrange(5000000))[:5]
>>> def gen(max_value):
... for value in xrange(max_value):
... yield value * 2
Generator functions não podem ter return
!
Context Managers são usados como "marcadores" para entrada e saída de pontos específicos.
>>> 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')
>>> try:
... conn = self._make_connection()
... conn.request('value')
... finally:
... conn.close()
... except Exception as exc: # Bare exceptions are BAAAADDD!
... print 'Exception!', exc
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 é o sistema de geração de documentação do Python.
PEP 257 fala sobre formato de documentação de funções.
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
O Sphinx tem um formato específico para documentação.
:param [nome]:
:type [tipo]:
:return:
:rtype:
:raises [exceção]:
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.)