Eu acabei de ler o livro online Byte of Python, de Swaroop C H,
um programador indiano que trabalha na Adobe. Comecei lendo o capítulo sobre
orientação a objetos, pois queria ver qual era a abordagem do Python para
essa questão fundamental. Como muitas coisas em Python, a abordagem OO é
simples e direta, com uma sintaxe muito legal.
Como esse capítulo era muito bem escrito acabei me empolgando e li o restante
do livro, dessa vez seguindo a ordem de capítulos proposta pelo autor.
Posso lhe garantir que valhe a pena, se você está chegando ao mundo
onde a indentação é obrigatória e as declarações “não terminam”.
Se você ainda não se convenceu a ler o livro talvez se interesse pela minha
listagem dos “melhores momentos” de Byte of Python (ela acabou ficando um
pouco extensa, pois foi escrita como uma forma de eu mesmo estudar aquilo
que estava lendo no livro). Ela é feita do meu ponto de vista
e do meu background principal (PHP e Ruby).
A indentação do código é obrigatória
Certas expressões Python impõe restrições
sobre as expressões seguintes. Por exemplo,
uma expressão que termina com : (dois pontos),
impõe que a expressão seguinte seja um
bloco de expressões (e blocos de expressões devem
ser, obrigatoriamente, indentados 1 nível a mais
que a expressão anterior/externa).
Exemplo:
if len('caio') == 4:
print 'OK'
Saída:
OK
Agora se você não indentar a segunda linha teremos
um erro de sintaxe:
File "<stdin>", line 2
print 'OK'
^
IndentationError: expected an indented block
Quando o bloco de expressões possuir apenas um comando,
esse comando pode parecer na mesma linha que inicia
o bloco, por exemplo:
if len('caio') == 4: print 'OK'
O autor do livro não recomenda esse tipo de uso
(mas não justifica essa opinião).
A quebra de linha termina blocos de expressões
Você não verá isto em Python:
class Dog:
def __init__(self, name):
self.name = name
end
def bark(self, times=1):
print 'au' * times
end
end
snoopy = Dog('Snoopy')
snoopy.bark()
Mas isso (note a ausência de todos os end):
class Dog:
def __init__(self, name):
self.name = name
def bark(self, times=1):
print 'au' * times
snoopy = Dog('Snoopy')
snoopy.bark()
No início eu achava essa sintaxe muito alienígena.
Mas quando se trata de escrever menos e obter o
mesmo resultado, por que não?
Isso traz algumas conseqüências interessantes.
Por exemplo, como declarar a mesma classe Dog,
agora sem nenhum daqueles dois métodos? Tente desta
forma:
class Dog:
O interpretador Python vai reclamar dessa declaração.
Ele vai lhe dizer que esperava um “bloco indentado”, e você
deu a ele apenas uma quebra de linha. Ou seja,
uma declaração de classe (class Dog:) possui o caracter :
(dois pontos), e isso indica ao interpretador que em seguida
você vai declarar um conjunto de expressões (ou bloco indentado).
O que você pode fazer, portanto, para dizer ao interpretador que
você não vai declarar um bloco indentado é utilizar a
declaração pass no lugar do conjunto de blocos. Ou seja,
a definição mínima para a classe Dog é a seguinte:
class Dog:
pass
Não há blocos switch
Python adota a seguinte filosofia: “There should be one — and preferably
only one — obvious way to do it” (”Deve haver uma - e de preferência
apenas uma - maneira de se fazer alguma coisa”). Como a construção
switch pode ser traduzida em um bloco if-else, a linguagem
não o suporta.
Suporta herança múltipla
Python suporta a herança múltipla diretamente (ao contrário de
Ruby, por exemplo, que a suporta por meio de mixins). A seguinte
listagem de código Python declara Dog como herdeiro de duas
classes, Animal e Mammal.
class Animal:
pass
class Mammal:
pass
class Dog(Animal, Mammal):
pass
Variáveis globais
Variáveis globais podem ser acessadas dentro de
uma função diretamente (apenas pelo nome da variável)
ou utilizando a palavra-chave global. Há uma diferença
entre utilizar uma variável no escopo da função após
a declaração dela como global e sem sua declaração
como global, como apontado pelo Luciano Ramalho
nos comentários deste post:
a declaração global não é sempre opcional […].
Ela é opcional apenas para se acessar a
variável global. Porém, se a intenção do programador
é atribuir um novo valor ao uma variável global x,
ele é obrigado a declarar global x no escopo local,
do contrário uma atribuição como x=1 vai apenas
criar uma nova variável local, e não alterar o valor
da variável global.
Exemplo:
my_global_var = 5
def some_function():
global my_global_var
my_global_var = 1
print my_global_var
some_function()
print my_global_var
A saída desse programa será:
5
1
Strings suportam o método *
Exemplo:
`'python' * 2
Saída:
'pythonpython'
Em Ruby isso também é possível, em PHP não (na verdade é,
e o resultado é zero, hehe).
Argumentos nomeados são suportados nativamente
Python suporta o conceito de argumentos nomeados
(keyword arguments). Isso é
útil quando não queremos nos preocupar com a ordem na qual
os parâmetros são passados para uma função.
Exemplo:
def my_func(a = 1, b = 5):
print 'a =', a
print 'b =', b
my_func()
my_func(b = 30, a = 100)
Saída:
a = 1
b = 5
a = 100
b = 30
O “nada” é representado por None
Uma função que não retorna valores explicitamente (com o uso
da palavra-chave return) retorna, implicitamente, None, que
em Python corresponde a nada, ou vazio. Exemplo:
def my_func():
pass
print my_func()
A saída do programa será:
None
Para retornar valores de uma função use return
Em Ruby, se uma função não retorna um valor explicitamente,
então o último valor avaliado é retornado. Com Python,
assim como em PHP,
você deve retornar valores explicitamente com return.
Tipos boolean são True e False
Python conta com duas instâncias do tipo boolean: True
e False. Expressões lógicas retornam um desses dois
valores. Exemplos:
print type(True)
print type(False)
print 1 == 1
print 1 == 0
Saída:
<type 'bool'>
<type 'bool'>
True
False
Funções são objetos de primeira classe
Diferentemente de PHP, em Python este código é possível:
def my_func():
print 'hey'
a = my_func
a()
a, neste caso, passou a ser um objeto do tipo function.
Como qualquer outra variável, ela poderá ser passada como
argumento de outras funções. Se você não percebe o benefício
desse recurso pesquise o assunto first class functions no
Google.
DocStrings são documentação e código ao mesmo tempo
O seguinte código:
def my_documented_function():
'''This is a one-liner description of the function.
This is a long description of the function.'''
pass
print my_documented_function()
print my_documented_function.__doc__
help(my_documented_function)
Irá gerar a seguinte saída:
None
This function doesn't do much (this is the docstring header)
But its documentation takes a few lines
O Python trata, como podemos ver, a documentação como uma
propriedade/atributo da função my_documented_function (a
linguagem associa a documentação automaticamente a __doc__). Note,
ainda, que para acessar o atributo de uma função não a invocamos,
pois não estamos utilizando os parênteses após seu nome.
O outro ponto interessante é que help é um atalho que
podemos utilizar para acessar a propriedade __doc__ da função.
Ainda é possível associar docstrings também a classes e módulos.
Listas, Tuplas e Dicionários
Listas são coleções de objetos mutáveis.
Exemplos de manipulação de listas:
a = [1,2,3,4]
print type(a)
a.append(5)
print (a)
del a[0]
print a
Saída:
<type 'list'>
[1, 2, 3, 4, 5]
[2, 3, 4, 5]
Tuplas são coleções de objetos imutáveis. Tuplas
têm a mesma sintaxe de criação das listas, mas
troca-se o [] por (), como em:
a = ('caio', 'moritz')
Dicionários correspondem aos arrays associativos
de PHP ou Hashes de Ruby. Sua sintaxe é bem simples:
lang_creators = {
'Ruby': 'Yukihiro Matsumoto',
'Python': 'Guido van Rossum',
'PHP': 'Rasmus Lerdorf'
}
for lang, creator in lang_creators.items():
print '%s was created by %s' % (lang, creator)
print 'Who created Java?'
if lang_creators.has_key('Java'):
print lang_creators['Java']
else:
print 'I have no idea.'
Saída:
Python was created by Guido van Rossum
PHP was created by Rasmus Lerdorf
Ruby was created by Yukihiro Matsumoto
Who created Java?
I have no idea.
Pontos importantes:
- No loop
for, utilize o método items() do dicionário
para acessar seus itens
- Dicionários não guardam a ordem em que seus elementos
foram declarados
Interpolação de strings
Em PHP interpolamos strings assim:
echo "My favourite programming language is {$lang}";
Em Ruby assim:
puts "My favourite programming language is #{lang}";
Em Python não temos algo diferente, que atinge o mesmo objetivo:
print "My favourite programming language is %s" % lang
Referências
Uma variável referencia um objeto, e não um valor,
conforme o código abaixo:
lang_creators = ['matz', 'guido', 'rasmus']
lc = lang_creators
lc == lang_creators
del(lang_creators[0])
print lc
print lang_creators
Saída:
['guido', 'rasmus']
['guido', 'rasmus']
A segunda linha da listagem faz com que lc e lang_creators
passem a apontar para o mesmo objeto (no caso uma lista).
Por conseqüência, quando a lista é alterada através
da referência lc a mudança também é sentida pela
referência lang_creators.
String.Join ou Array.Join?
Em Ruby utiliza-se Array#join para unir todos
os elementos de um array,
separando-os por um determinado
caracter passado como parâmetro.
Exemplo:
arr = ['this', 'is', 'going', 'to', 'be', 'a', 'string']
arr.join(' ')
Saída:
"this is going to be a string"
Com Python o método join está em String, funcionando
de forma “inversa” (é a String que une o Array, e não o Array
que une seus elementos a partir de uma String):
lst = ['this', 'is', 'going', 'to', 'be', 'a', 'string']
' '.join(lst)
Saída:
'this is going to be a string'
Entrada e saída
Não vou me alongar muito neste tópico: entrada
e saída em Python é muito simples, como em PHP e Ruby.
Nada me supreendeu nem pro bem e nem pro mal.
Destaque para a iteração sobre todas as linhas de um
arquivo, em 2 linhas de código:
for line in file('some_file.txt'):
print line[:-1]
[:-1] evita que se imprima a quebra de linha original.
Namespaces
Python suporta namespaces de um forma muito legal. Um
arquivo externo importado para dentro de um programa
Python tem todo o seu código (variáveis, funções etc.)
carregado dentro de um namespace próprio, igual ao
nome do arquivo importado.
Orientação a Objetos
A orientação a objetos, ao menos até onde o livro
explica, é bastante simples. Não compreendi bem
como fica a questão do encapsulamento, por exemplo.
Gostei da possibilidade da herança múltipla (isso
sempre me fez falta no Java), só não gostei de todos
os métodos de uma classe precisarem declarar self
como o primeiro parâmetro. Sei o que self representa,
mas porquê a linguagem não pode tomar conta
de torná-lo presente no código interno da classe?
Miscelânea
- Strings são imutáveis
- Strings não são Unicode por padrão (confira
os comentários do Luciano Ramalho neste post
para entender como utilizar strings normais
e strings Unicode)
Tópicos importantes não abordados no meu resumo
Tópicos importantes que não abordei no resumo
mas que são tratados mais ou menos profundamente
no livro:
- Tratamento de exceções
- Compreensão de listas (list comprehensions)
- Métodos especiais (como
__str__ e __init__)
- Expressões Lambda
E agora?
Você continuar aprendendo Python lendo o livro
do Swaroop ou seguindo sua recomendação de leituras.