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
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
)(... que é o nome bonito para coisas tipo if
, for
...)
Antes de mais nada...
Em Python, uma identação define um bloco.
Não tem {
/ }
, não tem end
, nada. Só blocos.
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 ]
.
(Na verdade, o objeto tem que ter um generator;
para acesar elementos diretamente, o objeto tem que implementar a função
__getitem__
.)
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
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 (como o
__getitem__
comentado anteriormente), 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'
>>> 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.
BORING!
A parte legal dos stars não é usar para criar funções que aceitam qualquer parâmetro (embora isso seja legal em alguns casos).
A parte legal é fazer chamadas de funções com dicionários.
>>> def funcao(a, b, c):
>>> return (a + b) / c
>>>
>>> params = {'b': 2, 'c': 3, 'a':10}
>>> funcao(**params)
>>> 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
.