Curso de Pydjango - Python e Django
Curso de Pydjango - Python e Django
Curso de Pydjango - Python e Django
Apresentao
Programador amador
desde 1988
Programador
profissional desde
1991
Programador Python
desde 2000
Programador Django
desde 2008
Introduo e Histria
Python
Surgiu em 1989
Criada por Guido van Rossum
Monty Python and the flying circus
Licena compatvel com Software Livre
Linguagem de altssimo nvel (VHLL)
Tipagem dinmica
Multiparadigma (OO, funcional e
procedural)
Compilada + Interpretada
Verses de Python
Python 2.7
Mantido at que a verso 3 esteja estvel
e com boa parte das bibliotecas portadas
Python 3.2
Evoluo da linguagem
Quebra compatibilidade retroativa
Usaremos Python 2.7
O Django (e muitas outras bibliotecas)
ainda no foi portado para o Python 3.0
Verses de Python
Python 2.7
Mantido at que a verso 3 esteja estvel
e com boa parte das bibliotecas portadas
Python 3.2
Evoluo da linguagem
Quebra compatibilidade retroativa
Usaremos Python 2.7
O Django (e muitas outras bibliotecas)
Trataremos as
diferenas sempre
que necessrio!
Primeiro Programa
#!/usr/bin/env python
# -*- encoding: utf-8 -*# prog1.py - Primeiro programa
"""
Importa o mdulo random e sorteia
um nmero inteiro entre 1 e 100
"""
import random
numero = random.randint(1, 100)
escolha, tentativas = 0, 0
while escolha != numero:
escolha = input("Escolha um nmero entre 1 e 100:")
tentativas += 1
if escolha < numero:
print u"O nmero", escolha, u" menor que o sorteado."
elif escolha > numero:
print u"O nmero", escolha, u" maior que o sorteado."
print u"Parabns! Voc acertou com", tentativas, "tentativas."
Primeiro Programa
#!/usr/bin/env python
# -*- encoding: utf-8 -*# prog1.py - Primeiro programa
"""
Importa o mdulo random e sorteia
um nmero inteiro entre 1 e 100
"""
import random
numero = random.randint(1, 100)
Que interpretador
ser usado?
escolha, tentativas = 0, 0
while escolha != numero:
escolha = input("Escolha um nmero entre 1 e 100:")
tentativas += 1
if escolha < numero:
print u"O nmero", escolha, u" menor que o sorteado."
elif escolha > numero:
print u"O nmero", escolha, u" maior que o sorteado."
print u"Parabns! Voc acertou com", tentativas, "tentativas."
Primeiro Programa
#!/usr/bin/env python
# -*- encoding: utf-8 -*# prog1.py - Primeiro programa
"""
Importa o mdulo random e sorteia
um nmero inteiro entre 1 e 100
"""
import random
numero = random.randint(1, 100)
Qual conjunto de
caracteres (charset)
ser usado no
arquivo?
escolha, tentativas = 0, 0
while escolha != numero:
escolha = input("Escolha um nmero entre 1 e 100:")
tentativas += 1
if escolha < numero:
print u"O nmero", escolha, u" menor que o sorteado."
elif escolha > numero:
print u"O nmero", escolha, u" maior que o sorteado."
print u"Parabns! Voc acertou com", tentativas, "tentativas."
Primeiro Programa
#!/usr/bin/env python
# -*- encoding: utf-8 -*# prog1.py - Primeiro programa
"""
Importa o mdulo random e sorteia
um nmero inteiro entre 1 e 100
"""
import random
numero = random.randint(1, 100)
Usa-se # para
comentrios em Python.
Tambm pode-se usar
docstrings.
escolha, tentativas = 0, 0
while escolha != numero:
escolha = input("Escolha um nmero entre 1 e 100:")
tentativas += 1
if escolha < numero:
print u"O nmero", escolha, u" menor que o sorteado."
elif escolha > numero:
print u"O nmero", escolha, u" maior que o sorteado."
print u"Parabns! Voc acertou com", tentativas, "tentativas."
Primeiro Programa
#!/usr/bin/env python
# -*- encoding: utf-8 -*# prog1.py - Primeiro programa
"""
Importa o mdulo random e sorteia
um nmero inteiro entre 1 e 100
"""
import random
numero = random.randint(1, 100)
escolha, tentativas = 0, 0
while escolha != numero:
escolha = input("Escolha um nmero entre 1 e 100:")
tentativas += 1
if escolha < numero:
print u"O nmero", escolha, u" menor que o sorteado."
elif escolha > numero:
print u"O nmero", escolha, u" maior que o sorteado."
print u"Parabns! Voc acertou com", tentativas, "tentativas."
Demonstrao
Criando e executando nosso primeiro programa...
O interpretador
O interpretador
O intepretador
Parmetros
Parmetro
Descrio
-h
-O / -OO
-c cmds
-u
Desliga o buffer de sada para o terminal. Isso faz com que todo o
contedo impresso no terminal seja exibido imediatamente.
-m
O intepretador
Variveis de ambiente
Varivel
Descrio
PYTHONHOME
PYTHONPATH
PYTHONSTARTUP
A linguagem
Caractersticas
Um comando por linha
Usar ; para mais de uma linha
Usar \ para continuar em outra linha
Bloco de comando por indentao
No misture Tabs e espaos
Comentrios
Caracter # ou """strings multilinhas"""
Diferencia maisculas de minsculas
Nmeros
>>> 2 ** 32, 2 + 5j
(4294967296L, (2+5j)
>>> 5.35 / 2.45
2.1836734693877546
>>> 5 + 2, 5 - 2, 5 * 2, 5 / 2
(7, 3, 10, 2)
>>> 5 / 2.0, 5 // 2.0
(2.5, 2.0)
>>> 5 ** 2, 5 % 2
(25, 1)
>>> 1 & 0, 1 & 1, 1 | 0, 1 | 1
(0, 1, 1, 1)
>>> 1 ^ 0, 1 ^ 1
(1, 0)
>>> ~1, ~0, 1 << 2, 32 >> 2
(-2, -1, 4, 8)
Nmeros
>>> 2 ** 32, 2 + 5j
(4294967296L, (2+5j)
>>> 5.35 / 2.45
2.1836734693877546
>>> 5 + 2, 5 - 2, 5 * 2, 5 / 2
(7, 3, 10, 2)
>>> 5 / 2.0, 5 // 2.0
(2.5, 2.0)
>>> 5 ** 2, 5 % 2
(25, 1)
>>> 1 & 0, 1 & 1, 1 | 0, 1 | 1
(0, 1, 1, 1)
>>> 1 ^ 0, 1 ^ 1
(1, 0)
>>> ~1, ~0, 1 << 2, 32 >> 2
(-2, -1, 4, 8)
Lgica
bool()
True, False
0, 0.0, [], (), {}, "", set(),
None, ... - Falso
==, !=, >, >=, <, <=, is, is
not, and, or, in e not in
Usar "is None" para
comparaes com None
Prefira "if valor: ..." no
lugar de "valor == True"
ou "valor != 0".
Palavras reservadas
and
as
assert
break
class
continue
def
del
elif
else
except
exec
False
finally
for
global
if
import
in
is
lambda
None
nonlocal
not
or
pass
raise
return
True
try
while
with
yield
3.0
Identificadores
Diferena entre maiscula e minsculas
Nome deve iniciar com letra ou "_"
Restante do nome pode conter letras,
nmeros e "_"
No permitido o uso de palavras
Atribuio
>>> a = "a"
>>> b = "b"
>>> c, d = "cd"
>>> e, f = "e", "f"
>>> print a, b, c, d, e, f
a b c d e f
>>> e, f = f, e
>>> print a, b, c, d, e, f
a b c d f e
>>> a += b
>>> print a
ab
>>> a *= 5
>>> print a
ababababab
Referncias
CONTADOR DE
REFERNCIAS
p1 = PessoaFisica()
p1
Referncias
CONTADOR DE
REFERNCIAS
p1 = PessoaFisica()
p2 = p1
p1
p2
Referncias
CONTADOR DE
REFERNCIAS
p1 = PessoaFisica()
p2 = p1
del p1
p2
Referncias
CONTADOR DE
REFERNCIAS
p1 = PessoaFisica()
p2 = p1
del p1
del p2
Referncias
CONTADOR DE
REFERNCIAS
p1 = PessoaFisica()
p2 = p1
del p1
del p2
Destruio do objeto
Comandos
Comando if/elif/else
Comando de deciso.
Executa o bloco de
cdigo em if ou elif caso
a condio for
verdadeira.
Se nenhuma condio
for verdadeira executa o
bloco else
Expresso if usada entre
parnteses
a = input("A:")
b = input("B:")
if a > b:
print "A maior que B"
elif b > a:
print "B maior que A"
else:
print "A e B so iguais"
print ("A maior" if a > b \
else "A no maior")
Comando while
import sys, time
Executa o bloco de
cdigo, em loop,
enquanto a condio for
verdadeira
A clusula 'else' pode ser
usada e ser executada
caso o loop termine
normalmente (sem
break)
Comando for
import sys
a = range(10)
print a
1, 2, 3, 4, 5, 6, 7, 8, 9]
for i in a:
print i,
2 3 4 5 6 7 8 9
Comando continue
import sys
Fora a iterao de um
loop (e a verificao de
uma condio no caso
do loop while)
a = range(10)
print a
1, 2, 3, 4, 5, 6, 7, 8, 9]
for i in a:
print i,
2 3 4 5 6 7 8 9
Comando break
import sys
Interrompe o loop
Um loop interrompido
com break no executa
os comandos da clusula
'else'
a = range(10)
print a
1, 2, 3, 4, 5, 6, 7, 8, 9]
for i in a:
print i,
2 3 4 5 6 7 8 9
Comando print
import sys
Imprime informaes na
tela
Tambm pode enviar
informaes para um
arquivo
Comando pass
No executa nada
Necessrio para definir
blocos de cdigo vazios
j que Python no utiliza
braces para delimitar
blocos.
class Abstrata:
def metodo_abstrato(self):
pass
try:
print 1 / 0
except ZeroDivisionError:
pass # ignora exceo
Comando del
Remove a referncia a
um objeto
Remove elementos de
collections
Quando as referncias
chegam a zero o objeto
entra na fila do garbage
collector que ir liberar
os recursos desse objeto
>>> a = [1,2,3]
>>> b = a
>>> a.append(4)
>>> a, b
([1, 2, 3, 4], [1, 2, 3, 4])
>>> del b
>>> b
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
NameError: name 'b' is not defined
>>> a
[1, 2, 3, 4]
>>> del a
>>> a, b
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
NameError: name 'a' is not defined
Comando exec
Comando assert
python prog_assert.py
== 1? sim.
== 1? 2 != 1
python -O prog_assert.py
== 1? sim.
== 1?
Tipos de dados
Tipos de dados
Em Python, todos os tipos de dados so
objetos
Quando fazemos algo como: int("0")
>>> type(50)
<type 'int'>
>>> type(0xFF) # Hexadecimal
<type 'int'>
>>> type(034) # Octal
<type 'int'>
>>> 50, 0xFF, 034
(50, 255, 28)
>>> type(50L)
<type 'long'>
>>> type(0xFFFFFFFFFF) # Hexadecimal
<type 'long'>
>>> 0xFFFFFFFFFF # Hexadecimal
1099511627775L
>>> 2147483647 + 1
2147483648L
>>> type(50)
<type 'int'>
>>> type(0xFF) # Hexadecimal
<type 'int'>
>>> type(034) # Octal
<type 'int'>
>>> 50, 0xFF, 034
(50, 255, 28)
>>> type(50L)
<type 'long'>
>>> type(0xFFFFFFFFFF) # Hexadecimal
<type 'long'>
>>> 0xFFFFFFFFFF # Hexadecimal
1099511627775L
>>> 2147483647 + 1
2147483648L
Ponto flutuante
float(x)
0.12, 1.25, 2e-5, ...
>>> type(1.5)
<type 'float'>
>>> type(7e-5)
<type 'float'>
>>> type(1e10)
<type 'float'>
>>> 7e-5
6.9999999999999994e-05
>>> print 7e-5 - 7e-3
-0.00693
>>> 7e-5 - 7e-3
-0.0069300000000000004
Complexos complex
Complexos complex(i,
j)
2+2j, 5j
>>> type(5j)
<type 'complex'>
>>> type(2+5j)
<type 'complex'>
>>> (2+3j) * 5
(10+15j)
Booleanos bool
Booleano bool(x)
True e False
Valores Falsos:
(), [], {}, set([]), None,
0, 0.0 e ""
__nonzero__() ou
__len__() retornam 0
>>> type(True)
<type 'bool'>
>>> type(False)
<type 'bool'>
>>> int(True)
1
>>> int(False)
0
Seqncias
So seqncias:
Strings (imutvel)
Listas (mutvel)
Tuplas (imutvel)
Indexveis
Iterveis
"Fativeis" (slice)
List comprehension
Indexao e Slicing
Indice inicial: 0 (zero)
Indexao:
"abcde"[2] 'c' / ['a', 'b', 'c', 'd'][-1] 'd'
Slicing
seq[incio:fim:intervalo]
"abcde"[2:] 'cde' / "abc"[:-1] 'ab'
"abcde"[::2] 'ace' / "abcde"[1:-1:2] 'bd'
"abcde"[:] 'abcde' (cpia 'rasa')
Iterao
for c in "abcde": ... percorre todos os
caracteres
for n in [ 1, 2, 3 ]: ... percorre 1, 2 e 3
for x in "": ... no entra no loop
List Comprehension
seq = [ -1, 3, -5, 4, 8, 9 ]
# loop
res = []
for n in seq:
if n > 0: res.append(n * n)
print res
# list comprehension
print [ x*x for x in seq if x > 0 ]
print [ x*x for x in seq ] # todos
# ['nome: fulano', 'idade: 35']
d = { 'nome': 'fulano',
'idade': '35' }
print [ "%s: %s" % (k,v) for \
k,v in d.items() ]
Descrio
Newline ser ignorado. Usado para continuar string em
outra linha
\\
Caracter de contra-barra
\'
Apstrofo
\"
Aspas
\a
\b
\e
\0
\t
Tab horizontal
\r
\n
Avano de linha
Descrio
%s
%c
Caracter
%d
%i
%o
%x %X
%r
%f %e %g
%%
Para listagem
completa:
Listas list
Lista list(x)
[ 1, 2, 3 ], list('seq')
Mutvel
lista[2] = 'spam'
lista + ['eggs']
Listas list
Para listagem
completa:
Mtodos teis:
dir(list)
String
.count()
funcionam como em
Listas list
>>> ['a', 'b']
['a', 'b']
>>> ['a', 'b'] + ['c', 'd']
['a', 'b', 'c', 'd']
>>> a = ['a', 'b', 'c', 'd']
>>> a
['a', 'b', 'c', 'd']
>>> a[0] = 'X'
>>> a
['X', 'b', 'c', 'd']
>>> a += "efg"
>>> a
['X', 'b', 'c', 'd', 'e', 'f', 'g']
>>> a + "ghi"
TypeError: can only concatenate list
(not "str") to list
>>> ['a', 'b', 'c', 'd'][0]
'a'
>>> ['a', 'b', 'c', 'd'][0:2]
['a', 'b']
Listas list
>>>
>>>
>>>
[1,
>>>
>>>
[1,
>>>
>>>
[1,
>>>
6
>>>
[1,
>>>
>>>
[5,
a = [1, 2, 3]
a.append(4)
a
2, 3, 4]
a.extend([5, 6])
a
2, 3, 4, 5, 6]
a.insert(2, 2.5)
a
2, 2.5, 3, 4, 5, 6]
a.pop()
a
2, 2.5, 3, 4, 5]
a.reverse()
a
4, 3, 2.5, 2, 1]
>>>
>>>
[1,
>>>
2
a.sort()
a
2, 2.5, 3, 4, 5]
['err', 'ok', 'err'].count('err')
Tuplas tuple
Tupla tuple(x)
( 1, 2, 3 ), tuple([1,2,3])
Imutvel
tupla[2] = 'spam' # Erro!
tupla + ['eggs'] # Erro!
Consome menos recursos que list()
Dicionrios dict
Dicionrio dict(x)
{ 'chave1': 'valor1', 'chave2': 'valor2' }
dict(k1=v1, k2=v2)
Mutvel
dicionario['chave1'] = 'valor novo'
dicionario['chave nova'] = 1
Hashmap (no preserva ordem das chaves)
Python usa dicionrios em toda sua
implementao
Dicionrios dict
Para listagem
completa:
Mtodos teis:
dir(dict)
{'a':1}.get('b', 0) 0
{'a':1}.setdefault('b', 0) 0 / d['b'] = 0
{'a': 1, 'b': 2}.items() [('a', 1), ('b', 2)]
{'a': 1, 'b': 2}.keys() ['a', 'b']
{'a': 1, 'b': 2}.values() [1, 2]
{'a': 1}.update({'b': 2}) {'a': 1, 'b': 2} (inline)
{'a': 1, 'b': 2}.pop('a') 1 / del d['a']
'chave' in {'chave': 1} True
Dicionrios dict
>>> d = { "c1": "v1" }
>>> dic["c2"] = "v2"
>>> d
{'c1': 'v1', 'c2': 'v2'}
>>> d[1] = "chave numerica"
>>> d
{'c1': 'v1', 1: 'chave numerica',
'c2': 'v2'}
>>> tuplachave = (1, 2, 3)
>>> d[tuplachave] = "objeto chave"
>>> d
{'c1': 'v1', 1: 'chave numerica',
'c2': 'v2',
(1, 2, 3): 'objeto chave'}
>>> d.update({'c3':'v3', 'c4':'v4'})
>>> d
{1: 'chave numerica', 'c3': 'v3',
Dicionrios dict
>>> 'c1' in d, 'c4' in d
(True, False)
>>> d.get('c4', 'v4')
'v4'
>>> 'c4' in d
False
>>> d.setdefault('c4', 'v4')
'v4'
>>> d['c4']
'v4'
>>> d.setdefault('c4', 'X')
'v4'
>>> d['c4']
'v4'
Dicionrios dict
notas = {
"Graham Chapman": 5.5,
"John Cleese": 7.0,
"Terry Gilliam": 4.5,
"Terry Jones": 4.5,
"Eric Idle": 10,
"Michael Palin": 3.5,
}
print "Conteudo dicionario:", \
notas
print
for aluno in notas:
print aluno
print
for nota in notas.values():
print nota
print
for aluno, nota in notas.items():
print "Aluno: %-20s Nota: %4.1f" % \
(aluno, nota)
Dicionrios dict
Conteudo do dicionario: {'Terry
4.5
10
5.5
3.5
4.5
7.0
Terry Gilliam
Eric Idle
Nota:
4.5
Graham Chapman
Nota: 10.0
Michael Palin
Nota:
5.5
Terry Jones
Nota:
3.5
John Cleese
Nota:
4.5
Nota:
7.0
Arquivos file
Arquivo file(x) / open(x)
file(nome, modo, buffer)
file("/etc/passwd")
Buffer (f.flush() para esvaziar):
0 Sem buffer
1 Com buffer
+N Tamanho do buffer
Itervel linha-a-linha (for linha in arquivo: ...)
Arquivos file
Modos
r
Leitura
Escrita (trunca)
Escrita (adiciona)
b
Binrio
Leitura e Escrita
U
Arquivos file
Para listagem
completa:
Mtodos teis:
dir(file)
Arquivos file
Contedo do arquivo:
linha 1
linha 2
linha 3
>>> f.tell()
5L
>>> f.readlines()
[' 1\n', 'linha 2\n', 'linha 3\n',
'linha 4\n']
>>> f.seek(0)
>>> f.readline()
'linha 1\n'
>>> f.close()
Arquivos file
>>> f = file("arq.txt", "a")
>>> f.write("linha 4\n")
>>> f.close()
>>> f = file("arq.txt")
>>> for l in f: print l,
...
linha 1
linha 2
linha 3
linha 4
>>> f.seek(0)
>>> f.read(5)
'linha'
>>> f.tell()
5L
>>> f.readlines()
[' 1\n', 'linha 2\n', 'linha 3\n',
'linha 4\n']
>>> f.seek(0)
>>> f.readline()
'linha 1\n'
>>> f.close()
Iteradores iter
Iterador iter(x)
iter([1,2,3])
Generators tambm implementam a
interface de iteradores
Generator expressions so iguais uma list
Iteradores iter
>>> a = [1, 2, 3]
>>> i = iter(a)
>>> type(i)
<type 'listiterator'>
>>> i.next(), i.next(), i.next()
(1, 2, 3)
>>> i.next()
Traceback (most recent call last):
StopIteration
>>> def f(n):
...
for i in range(n):
...
yield i
...
>>> r = f(3)
>>> type(r)
<type 'generator'>
Funes builtin
Funes builtin
Funes
Existem 2 tipos de funes:
Funes "convencionais" (def)
Funes annimas (lambdas)
Todo objeto que implementa o mtodo
__call__() executvel.
Funo annima:
lambda p1, p2, p3=1, *ps, **dps: ...
Parmetros
param
param=valor
*params
Demonstrao
>>> def spam(x, y):
...
return x ** y
...
>>> spam(5)
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
TypeError: spam() takes exactly 2 arguments (1 given)
>>> def spam(x, y=2):
...
return x ** y
...
>>> spam(5)
25
>>> spam(5, 3)
125
Demonstrao
>>>
...
...
>>>
1 2
>>>
1 3
>>>
1 2
Demonstrao
>>> def spam(x, y=2, z):
...
pass
...
File "<stdin>", line 1
SyntaxError: non-default argument follows default argument
>>> def spam(x, y=2, *a, **kw):
...
print x, y, a, kw
...
>>> spam(1, z=3)
1 2 () {'z': 3}
>>> spam(1, z=3, 2)
File "<stdin>", line 1
SyntaxError: no-keyword arg after keyword arg
Comando return
def potencia(x, y=2): return x ** y
def sem_return(): pass
Generators
Comando yield
Escopo
Busca-se os identificadores no escopo
Comando global
def l():
x = "L"
print "l:", x # l(): L
def g():
global x
x = "G"
print "g():", x # g(): G
x = "X"
print "X1:", x # X1: X
l()
# l(): L
print "X2:", x # X2: X
g()
# g(): G
print "X3:", x # X3: G
Closures
def fib(n):
if n < 2: return 1
return fib(n-1) + fib(n-2)
def memoize(fn):
memo = {}
def memoizer(key):
if key not in memo:
memo[key] = fn(key)
return memo[key]
return memoizer
print fib(35) # muuuito mais lento
fib = memoize(fib)
print fib(35)
Decorators
Com decorator
Sem decorator
@dec1
def f(x):
return x * 2
def f(x):
return x * 2
f = dec1(f)
@dec1
@dec2
def f(x):
return x * 2
def f(x):
return x * 2
f = dec1(dec2(f))
@dec1(arg)
def f(x):
return x * 2
def f(x):
return x * 2
f = dec1(arg)(f)
def memoize(fn):
memo = {}
def memoizer(key):
if not key in memo:
memo[key] = fn(key)
return memo[key]
return memoizer
@memoize
def fib(n):
if n < 2: return 1
return fib(n-1) + fib(n-2)
print fib(35)
Excees
Excees
Comandos:
try:/except:/else:/finally: tratamento de
excees.
raise
Excees builtin (mais comuns):
,
,
NotImplementedError, UnicodeError e ValueError.
IndexError KeyError KeyboardInterrupt
Excees
Excees
class NotFound(Exception): pass
class InvalidKey(Exception): pass
def find(collection, elem):
try:
return collection[elem]
except (KeyError, IndexError), ex:
raise NotFound("%s not found (%s)" % \
(elem, ex))
except TypeError:
raise InvalidKey("%s is invalid" % \
(elem,))
a, b = [0], {}
try:
find(a, 1) # IndexError
except Exception, ex:
print "Exception: %s" % (ex,)
try:
find(b, 'spam') # KeyError
except Exception, ex:
print "Exception: %s" % (ex,)
try:
find(a, "spam") # TypeError
except Exception, ex:
print "Exception: %s" % (ex,)
try:
find(b, 0) # No error
except Exception, ex:
print "Exception: %s" % (ex,)
else:
print "Element found."
try:
find(a, "spam") # TypeError again
except Exception, ex:
raise # re-raise exception
Excees
def readfile(filename, bytes):
try:
f = file(filename)
except IOError:
return ""
try:
return f.read(bytes)
except IOError:
return ""
finally:
f.close()
print "Nao existe:", readfile("no.txt", 5)
print "Existe:", readfile("yes.txt", 5)
Programao
Orientada a Objetos
Objetos
Instncias
Classe
Classes
Definida com o comando class
Suporte a herana mltipla
Mtodo inicializador ("construtor"):
def __init__(self, ...): ...
Instanciar: identificador = Pessoa()
Classes
Classe
Atributos
Mtodos
Mtodo
esttico
Instncia
class Pessoa(object):
atributo = 1
class Pessoa(object):
def __init__(self, attr):
self.attr = attr
class Pessoa(object):
@classmethod
def metodo(cls, arg):
pass
class Pessoa(object):
def metodo(self, arg):
pass
class Pessoa(object):
@staticmethod
def metodo(arg):
pass
Objetos
class Pessoa(object):
def __init__(self, nome):
self.nome = nome
def valida(self):
raise NotImplementedError()
class PF(Pessoa):
tabela_db = "pessoa_fisica"
def __init__(self, nome, cpf):
super(PF, \
self).__init__(nome)
self.cpf = ''.join(\
d for d in cpf if d.isdigit()
)
def valida(self):
return len(self.cpf) == 11
class PJ(Pessoa):
tabela_db = "pessoa_juridica"
def __init__(self, nome, cnpj):
super(PJ, \
self).__init__(nome)
self.cnpj = ''.join(\
d for d in cnpj if d.isdigit()
)
def valida(self):
return len(self.cnpj) == 14
f = PF("Fulano", "123.456.789-01")
j = PJ("ACME", "12345678/0001-90")
print f.nome, f.cpf, f.valida()
print j.nome, j.cnpj, j.valida()
Segurana
Todos os mtodos e atributos so pblicos
Por conveno os mtodos com nomes
iniciados por "_" so privados
Adicionar "__" (duplo) renomeia o
identificador para "_Classe__metodo"
Propriedades
No necessrio getters e setters
Acesse os atributos diretamente
Para validar as entradas ou calcular as
sadas transforme-o em uma propriedade
Use a funo property()
Propriedades
class Forno(object):
def __init__(self):
self.temp = 0
forno = Forno()
print forno.temp # 0
forno.temp = 120
print forno.temp # 120
forno.temp = 300 # FORNO DERRETIDO!
Propriedades
class Forno(object):
def __init__(self):
self._temp = 0
def _get_temp(self):
return self._temp
forno = Forno()
print forno.temp # 0
forno.temp = 120
print forno.temp # 120
forno.temp = 300 # ValueError
Mdulos, pacotes e
bibliotecas
Pacotes e Mdulos
Importar um mdulo ou pacote:
import pacote.modulo [as nome]
Importa para o namespace do mdulo
from pacote.modulo import ident|*
Importa para o namespace corrente
Mdulo "executado" ao ser importado
Se necessrio mdulo recompilado para
bytecode (.pyc/.pyo)
Pacotes e Mdulos
Pacotes: diretrios com arquivo __init__.py
Mdulo:
.py mdulo fonte
.pyc / .pyo bytecode (com e sem asserts)
.pyd / .so extenso em C para
Windows e Unix/Linux
Mdulos e pacotes so procurados no
PYTHONPATH:
import sys; print sys.path
Pacotes e Mdulos
Atributos do mdulo:
__name__ nome do mdulo ou
__main__ se for executado diretamente
__file__ caminho completo para o
arquivo importado
Para proteger cdigo da execuo durante
o import:
if __name__ == "__main__": ...
Biblioteca padro
anydbm
bz2
calendar
cgi
CGIHTTPServer
cgitb
cmd
codecs
collections
ConfigParser
copy
csv
ctypes
datetime
decimal
distutils
doctest
DocXMLRPCServer
ftplib
gc
getpass
glob
gzip
hashlib
htmlentitydefs
HTMLParser
httplib
imaplib
locale/gettext
logging
mailbox
math
mmap
optparse
os
os.path
pickle/cPickle
platform
poplib
pprint
Queue
random
re
shelve
shlex
shutil
signal
SimpleHTTPServer
SimpleXMLRPCServer
smtpd
smtplib
socket
SocketServer
sqlite3
ssl
stat
string
StringIO/cStringIO
struct
subprocess
sys
tarfile
tempfile
thread
threading
time
traceback
unittest
urllib
urllib2
urlparse
uuid
weakref
webbrowser
wsgiref
xml.dom.minidom
xml.etree
xml.sax
xmlrpclib
zipfile
zlib
PIP
Utilitrio de instalao de bibliotecas
Baixa e instala pacotes do PyPI (Python
Package Index)
Comando: pip install nome_do_pacote
Necessrio permisso de administrador
No vem instalado por padro, necessrio
baixar em:
http://pypi.python.org/pypi/pip
Virtualenv
Cria ambientes Python isolados
Permite modificao de um ambiente sem
causar interferncia em outros
Instalar virtualenv
sudo pip install virtualenv
Criar um ambiente virtual
virtualenv nome_do_ambiente
Ativando um ambiente
source nome_do_ambiente/bin/activate
Django
Django
Criado por Jacob Kaplan-Moss, Adrian
Holovaty e Simon Willison em 2003
Criado como um CMS para a World Online
Componentes
Object-relation Mapper mapeador objetorelacional
Template System linguagem de templates
URL dispatcher processador de URLs
Admin Interface Interface de administrao
Internacionalizao regionalizao
Outros: gerador de formulrio, serializador
de dados, autenticao e segurana, ...
Instalao
Criar ambiente com virtualenv e ativ-lo
Instalando "na mo":
Baixar do site:
http://djangoproject.com/download/
Descompactar
No diretrio do Django rodar:
python setup.py install
Ou usando o setuptools com o comando:
pip install django
Instalao
$ su # apt-get install python-pip
# pip install virtualenv
# exit
$ virtualenv aula --no-site-packages
$ cd aula
aula $ source bin/activate
(aula) aula$ pip install django
O Projeto de aula
Agenda de eventos
Cadastro de evento com data/hora e
lugar
Cadastro dos usurios que participaro
do evento
Sistema de autenticao de usurio
Iniciando o projeto
Utilitrio django-admin.py
Diversos comandos de gerenciamento
django-admin.py help para list-los
django-admin.py help <comando> para
ajuda detalhada de cada comando
Criando um projeto 'gerenciador':
django-admin.py startproject gerenciador
Iniciando o projeto
$ cd aula
aula$ source bin/activate
(aula) aula$ django-admin.py startproject gerenciador
(aula) aula$ cd gerenciador
(aula) aula/gerenciador$ ls
__init__.py manage.py
settings.py urls.py
(aula) aula/gerenciador$ chmod +x manage.py
(aula) aula/gerenciador$ ls
__init__.py manage.py*
settings.py urls.py
Os arquivos do projeto
__init__.py arquivo vazio
manage.py utilitrio parecido com o
Testando
Django vem com um servidor Web prprio
para ser usado durante o desenvolvimento
Recarga automtica de mdulos
NO usar este servidor em produo
O comando para iniciar o servidor :
./manage.py runserver
Aponte o navegador para o endereo:
http://localhost:8000/
Testando
Gerenciando
manage.py
Comando
Descrio
syncdb
loaddata fixture
shell
runserver [endereo:porta]
startapp [aplicao]
Aplicaes
Aplicaes Django so mdulos com
Aplicaes
Aplicaes
Aplicaes
Dizendo "Ol"
from django.conf.urls.defaults import *
# Uncomment the next two lines to enable the admin:
# from django.contrib import admin
# admin.autodiscover()
urlpatterns = patterns('',
# Example:
# (r'^gerenciador/', include('gerenciador.foo.urls')),
Arquivo:
urls.py
(r'^$', 'agenda.views.index'),
# Uncomment the admin/doc line below and add 'django.contrib.admindocs'
# to INSTALLED_APPS to enable admin documentation:
# (r'^admin/doc/', include('django.contrib.admindocs.urls')),
# Uncomment the next line to enable the admin:
# (r'^admin/', include(admin.site.urls)),
)
Dizendo "Ol"
Arquivo:
agenda/
views.py
Dizendo "Ol"
(aula) aula/gerenciador $ ./manage.py runserver
Validating models...
0 errors found
Django, using settings 'gerenciador.settings'
Development server is running at http://127.0.0.1:8000/
Quit the server with CONTROL-C.
Dizendo "Ol"
Models
Arquivo:
agenda/
models.py
from django.db import models
class ItemAgenda(models.Model):
data = models.DateField()
hora = models.TimeField()
titulo = models.CharField(max_length=100)
descricao = models.TextField()
ItemAgenda
data: Date
hora: Time
titulo: Varchar(100)
descricao: Text
Models
Tipos de campos
BooleanField
EmailField
IntegerField
SmallIntegerField
URLField
CharField
FileField
IPAddressField
PositiveSmallIntegerField
XMLField
DateField
FilePathField
TextField
CommaSeparatedIntegerField
TimeField
DateTimeField
FloatField
SlugField
NullBooleanField
AutoField
DecimalField
ImageField
PositiveIntegerField
db_index
help_text
unique_for_month
blank
db_tablespace
primary_key
unique_for_year
choices
default
unique
verbose_name
db_column
editable
unique_for_date
Configurando
Arquivo de configurao: settings.py
J vem preenchido
Arquivo auto-explicativo
Repleto de comentrios e configuraes
default mais comuns
Informaes sobre todas as opes em:
http://docs.djangoproject.com/en/dev/ref/settings/#ref-settings
Configurando
Configurao da conexo com o Banco de
Dados para o ORM
At o momento suporta os bancos:
SQLite3, MySQL, PostgreSQL ou Oracle.
Usaremos o SQLite3
Recomendado usar outro banco de dados
em ambientes de produo
Configurando
# Caso 2: SQLite3
DATABASES = {
'default': {
'ENGINE': 'django.db.backends.sqlite3',
'NAME': 'gerenciador.db',
'USER': '', 'PASSWORD': '',
'HOST': '', 'PORT': '',
}
}
# Caso 1: MySQL
DATABASES = {
'default': {
'ENGINE': 'django.db.backends.mysql',
'NAME': 'gerenciadordb',
'USER': 'user_name', 'PASSWORD': 'secreta123',
'HOST': '', 'PORT': '',
}
}
Configurando
# Caso 2: SQLite3
DATABASES = {
'default': {
'ENGINE': 'django.db.backends.sqlite3',
'NAME': 'gerenciador.db',
'USER': '', 'PASSWORD': '',
'HOST': '', 'PORT': '',
}
}
# Caso 1: MySQL
DATABASES = {
'default': {
'ENGINE': 'django.db.backends.mysql',
'NAME': 'gerenciadordb',
'USER': 'user_name', 'PASSWORD': 'secreta123',
'HOST': '', 'PORT': '',
}
}
Arquivo:
settings.py
Configurando
Arquivo:
settings.py
INSTALLED_APPS = (
'django.contrib.auth',
'django.contrib.contenttypes',
'django.contrib.sessions',
'django.contrib.sites',
'agenda',
)
Gerenciando dados
(aula) aula/gerenciador$ ./manage.py syncdb
:
Creating table agenda_itemagenda
You just installed Django's auth system, which means you don't have any
superusers defined.
Would you like to create one now? (yes/no): yes
Username (Leave blank to use 'osantana'): dead_parrot
E-mail address: dead_parrot@montypython.co.uk
Password: XXXXX
Password (again): XXXXX
Superuser created successfully.
:
Installing index for agenda.ItemAgenda model
Gerenciando dados
(aula) aula/gerenciador$ sqlite3 gerenciador.db
SQLite
Enter ".help" for instructions
sqlite> .schema agenda_itemagenda
CREATE TABLE "agenda_itemagenda" (
"id" integer NOT NULL PRIMARY KEY,
"data" date NOT NULL,
"hora" time NOT NULL,
"titulo" varchar(100) NOT NULL,
"descricao" text NOT NULL,
);
Mostrando Dados
Mostrando dados
Linguagem de Template
A linguagem de templates do Django
composta de tags e filtros
As tags (com comandos) devem ficar entre
as marcaes {% e %}
Para imprimir o resultado de uma
expresso podemos usar os marcadores
{{ e }}
Linguagem de Template
A linguagem propositadamente
Mostrando dados
Arquivo:
templates/
base.html
Mostrando dados
{% extends 'base.html' %}
{% block corpo %}
<a href="/adiciona/">Adicionar novo item</a>
<ul>
{% for item in lista_itens %}
<li>
<a href="/item/{{ item.id }}">
{{ item.data|date:'d/m/Y' }} - {{ item.titulo }}</a>
</li>
{% empty %}
<li>Sem itens na lista</li>
{% endfor %}
</ul>
{% endblock %}
Arquivo:
templates/
lista.html
View
Mostrando dados
Arquivo:
agenda/
views.py
# -*- encoding: utf-8 -*from django.shortcuts import render
from models import ItemAgenda
def index(request):
lista_itens = ItemAgenda.objects.all()
return render(request, "lista.html",
{'lista_itens': lista_itens})
Mostrando dados
Criando objetos
Formulrios
Uma das formas mais usadas para interao
dos usurios com a aplicao
Django oferece classes geradoras de
formulrios: forms.Form e
forms.ModelForm
A impresso dos campos de um formulrio
podem ser feitas com uma instruo no
template: form.as_p, form.as_li ou
form.as_table
Formulrios
Campos
Fazem o mapeamento do formulrio
Formulrios
Tipos de campos
BooleanField
DateTimeField
FloatField
NullBooleanField
CharField
DecimalField
ImageField
RegexField
ChoiceField
EmailField
IntegerField
TimeField
TypedChoiceField
FileField
IPAddressField
URLField
DateField
FilePathField
MultipleChoiceField
Tipos de widgets
TextInput
DateInput
Select
MultiWidget
PasswordInput
DateTimeInput
NullBooleanSelect
SplitDateTimeWidget
HiddenInput
TimeInput
SelectMultiple
SelectDateWidget
MultipleHiddenInput
Textarea
RadioSelect
FileInput
CheckboxInput CheckboxSelectMultiple
ModelForm
Arquivo:
agenda/
forms.py
from django import forms
from agenda.models import ItemAgenda
class FormItemAgenda(forms.ModelForm):
data = forms.DateField(
widget=forms.DateInput(format='%d/%m/%Y'),
input_formats=['%d/%m/%Y', '%d/%m/%y'])
class Meta:
model = ItemAgenda
fields = ('titulo', 'data', 'hora', 'descricao')
Formulrios
Arquivo:
templates/
adiciona.html
{% extends 'base.html' %}
{% block corpo %}
<form action="" method="post">
{% csrf_token %}
{{ form.as_p }}
<button type="submit">Adicionar</button>
</form>
{% endblock %}
Formulrios
Arquivo:
urls.py
from django.conf.urls.defaults import *
# Uncomment the next two lines to enable the admin:
# from django.contrib import admin
# admin.autodiscover()
urlpatterns = patterns('',
# Example:
# (r'^gerenciador/', include('gerenciador.foo.urls')),
(r'^$', 'agenda.views.index'),
(r'^adiciona/$', 'agenda.views.adiciona'),
)
ModelForm
Arquivo:
agenda/
views.py
ModelForm
Arquivo:
agenda/
views.py
from django.shortcuts import render, redirect
from forms import FormItemAgenda
def adiciona(request):
form = FormItemAgenda(request.POST or None, request.FILES or None)
if form.is_valid():
form.save()
return redirect("/")
return render(request, "adiciona.html", {'form': form})
Verso
aprimorada
Formulrios
Formulrios
Mensagens em portugus:
Mudar opo
LANGUAGE_CODE
para
'pt-br'
no arquivo
settings.py
Editando objetos
http://localhost:8000/item/1
Exibindo objetos
Arquivo:
urls.py
ModelForm
Arquivo:
agenda/
views.py
def item(request, nr_item):
item = get_object_or_404(ItemAgenda, pk=nr_item)
form = FormItemAgenda(request.POST or None,
request.FILES or None, instance=item)
if form.is_valid():
form.save()
return redirect("/")
return render(request, "item.html", {'form': form})
ModelForm
Arquivo:
templates/
item.html
{% extends 'base.html' %}
{% block corpo %}
<form action="" method="post">
{% csrf_token %}
{{ form.as_p }}
<button type="submit">Atualizar</button>
</form>
{% endblock %}
ModelForm
Atividades
Adicionar a
funcionalidade de
remoo
de item com
confirmao
Agenda Multiusurio
Segurana
Aplicao: django.contrib.auth
Requer aplicaes:
django.contrib.sessions
django.contrib.contenttypes
Models:
User
Group
Permission
Sistema de perfil de usurio
Segurana
Arquivo:
agenda/
models.py
from django.db import models
from django.contrib.auth.models import User
class ItemAgenda(models.Model):
data = models.DateField()
hora = models.TimeField()
titulo = models.CharField(max_length=100)
descricao = models.TextField()
usuario = models.ForeignKey(User)
User
ItemAgenda
data: Date
hora: Time
titulo: Varchar(100)
descricao: Blob
(FK)usuario: User
Segurana
(aula) gerenciador$ rm gerenciador.db
(aula) gerenciador$ ./manage.py syncdb
Creating table auth_permission
Creating table auth_group
Creating table auth_user
Creating table auth_message
Creating table django_content_type
Creating table django_session
:
:
Would you like to create one now? (yes/no): yes
Username (Leave blank to use 'osantana'): dead_parrot
E-mail address: dead_parrot@montypython.co.uk
Password: XXXXX
Password (again): XXXXX
Superuser created successfully.
:
Segurana
Arquivo:
agenda/
views.py
Segurana
Arquivo:
agenda/
views.py
@login_required
def adiciona(request):
form = FormItemAgenda(request.POST or None,
request.FILES or None)
if form.is_valid():
item = form.save(commit=False)
item.usuario = request.user
item.save()
return redirect("/")
return render(request, "adiciona.html", {'form': form})
Segurana
Arquivo:
agenda/
views.py
@login_required
def item(request, nr_item):
item = get_object_or_404(ItemAgenda, usuario=request.user, pk=nr_item)
form = FormItemAgenda(request.POST or None,
request.FILES or None, instance=item)
if form.is_valid():
form.save()
return redirect("/")
return render(request, "item.html", {'form': form})
Segurana
Arquivo:
agenda/
views.py
@login_required
def remove(request, nr_item):
item = get_object_or_404(ItemAgenda, usuario=request.user, pk=nr_item)
if request.method == "POST":
item.delete()
return redirect("/")
return render(request, "remove.html", {'item': item})
Segurana
Arquivo:
settings.py
LOGIN_URL = "/login/"
LOGOUT_URL = "/logout/"
LOGIN_REDIRECT_URL = "/"
Segurana
:
urlpatterns = patterns('',
# Example:
# (r'^gerenciador/', include('gerenciador.foo.urls')),
(r'^$', 'agenda.views.lista'),
(r'^adiciona/$', 'agenda.views.adiciona'),
(r'^item/(?P<nr_item>\d+)/$', 'agenda.views.item'),
(r'^remove/(?P<nr_item>\d+)/$', 'agenda.views.remove'),
(r'^login/$', 'django.contrib.auth.views.login',
{'template_name': 'login.html' }),
(r'^logout/$', 'django.contrib.auth.views.logout_then_login',
{'login_url': '/login/'}),
Arquivo:
urls.py
Segurana
{% extends "base.html" %}
{% block corpo %}
Arquivo:
templates/
login.html
{% if form.errors %}
<p>Seu usurio e senha no conferem. Tente novamente.</p>
{% endif %}
<form action="" method="post">
{% csrf_token %}
<table>
<tr><td>{{form.username.label_tag}}</td><td>{{form.username}}</td></tr>
<tr><td>{{form.password.label_tag}}</td><td>{{form.password}}</td></tr>
<tr><td colspan="2"><button type="submit">Login</button></td></tr>
</table>
</form>
{% endblock %}
Segurana
{% extends 'base.html' %}
{% block corpo %}
<p><a href="/logout/">logout</a></p>
<p><a href="/adiciona/">Adicionar novo item</a></p>
<ul>
{% for item in lista_itens %}
<li>
<a href="/remove/{{item.id}}">remover</a>
<a href="/item/{{item.id}}">
{{ item.data|date:"d/m/Y" }} - {{ item.titulo }}
</a>
</li>
{% empty %}
<li>Sem itens na lista</li>
{% endfor %}
</ul>
{% endblock %}
Arquivo:
templates/
lista.html
Segurana
Admin
na opo
Adicione
do
Remova comentrios relacionados a admin
do
Rode novamente
contendo...
Crie um arquivo
Desabilite a aplicao staticfiles em
'django.contrib.admin'
INSTALLED_APPS
settings.py
urls.py
./manage.py syncdb
agenda/admin.py
INSTALLED_APPS
Admin
Arquivo:
agenda/
admin.py
Admin
Admin
Admin
Admin
Admin
Arquivo:
agenda/
admin.py
Admin
Django ORM
Relacionamentos
Arquivo:
agenda/
models.py
Relacionamentos
from django import forms
from django.contrib.auth.models import User
from agenda.models import ItemAgenda
Arquivo:
agenda/
forms.py
class ModelMultipleUserField(forms.ModelMultipleChoiceField):
def label_from_instance(self, user):
return u"%s (%s)" % (user.get_full_name(), user)
class FormItemAgenda(forms.ModelForm):
data = forms.DateField(
widget=forms.DateInput(format='%d/%m/%Y'),
input_formats=['%d/%m/%Y', '%d/%m/%y'])
participantes = ModelMultipleUserField(queryset=User.objects.all())
class Meta:
model = ItemAgenda
fields = ('titulo', 'data', 'hora', 'descricao', 'participantes')
Relacionamentos
Manipulando Queries
$ ./manage.py shell
Python 2.5.1 (r251:54863, Feb 6 2009, 19:02:12)
[GCC 4.0.1 (Apple Inc. build 5465)] on darwin
Type "help", "copyright", "credits" or "license" for more information.
(InteractiveConsole)
>>> from agenda.models import ItemAgenda as ia
>>> print ia.objects.all()
[<ItemAgenda: ItemAgenda object>, <ItemAgenda: ItemAgenda object>,
<ItemAgenda: ItemAgenda object>]
>>> print ia.objects.get(pk=1)
ItemAgenda object
>>> print ia.objects.filter(data__month=8)
[<ItemAgenda: ItemAgenda object>]
>>> print ia.objects.order_by('-data')
[<ItemAgenda: ItemAgenda object>, <ItemAgenda: ItemAgenda object>,
<ItemAgenda: ItemAgenda object>]
Executando SQL
>>> from django.db import connection, transaction
>>> cursor = connection.cursor()
>>> c = cursor.execute("SELECT * FROM agenda_itemagenda")
>>> cursor.fetchall()
[(1, datetime.date(2009, 4, 25), datetime.time(12, 0), u'foo', u'bar', 1),
(2, datetime.date(2009, 8, 20), datetime.time(23, 41, 17), u'bla', u'blek',
1), (3, datetime.date(2000, 1, 10), datetime.time(20, 0), u'6', u'foo', 1)]
>>> c = cursor.execute("UPDATE agenda_itemagenda SET titulo='novo titulo'
WHERE id=1")
>>> transaction.commit_unless_managed()
>>> c = cursor.execute("SELECT * FROM agenda_itemagenda")
>>> cursor.fetchall()
[(1, datetime.date(2009, 4, 25), datetime.time(12, 0), u'novo titulo',
u'bar', 1), (2, datetime.date(2009, 8, 20), datetime.time(23, 41, 17),
u'bla', u'blek', 1), (3, datetime.date(2000, 1, 10), datetime.time(20, 0),
u'6', u'foo', 1)]
Signals
Arquivo:
agenda/
models.py
def envia_email(**kwargs):
print "Implementar..."
models.signals.post_save.connect(envia_email,
sender=ItemAgenda,
dispatch_uid="agenda.models.ItemAgenda")
E-mail
from datetime import datetime
def envia_email(**kwargs):
try:
item = kwargs['instance']
except KeyError:
return
for participante in item.participantes.all():
if participante.email:
dados = (item.titulo,
datetime.strftime(item.data, "%d/%m/%Y"),
item.hora)
participante.email_user(
subject="[evento] %s dia %s as %s" % dados,
message="Evento: %s\nDia: %s\nHora: %s" % dados,
from_email=item.usuario.email
)
Arquivo:
agenda/
models.py
E-mail
Arquivo:
settings.py
DEFAULT_FROM_EMAIL = "eventos@empresa.com"
EMAIL_HOST='smtp.empresa.com'
EMAIL_PORT=587
EMAIL_HOST_USER="eventos"
EMAIL_HOST_PASSWORD="senha123"
EMAIL_USE_TLS=True
Outras funes
Arquivos estticos e de mdia
GeoDjango (aplicaes GIS)
Internacionalizao e Localizao
Funes locais (ex. CPF, CNPJ, CEP, )
Aplicaes de comentrios, moderao,
Servios de segurana: signed cookies,
CSRF Protection, Clickjacking protection
Cache multi-backend
Pginas planas, multisite, etc.
Referncias
Python
http://www.python.org/
http://www.python.org.br/
Django
http://www.djangoproject.com/
http://www.djangobook.com/
http://www.djangobrasil.org/
Geral
http://www.pythonologia.org/
http://triveos.ludeos.com.br/
http://db.tt/j2wwFvX3
Cursos online
http://www.ludeos.com.br