Chapter 2: A linguagem Python - TODO
A linguagem Python
Sobre Python
Python é uma linguagem de programação de alto nível e de propósito geral. Sua filosofia de design enfatiza a produtividade do programador e a legibilidade do código. Tem uma sintaxe de núcleo minimalista com muito poucos comandos básicos e semântica simples, porém também conta com uma vasta e compreensível biblioteca padrão, incluindo uma Interface de Programação de Aplicação (API)
list
), tuplas (tuple
), tabelas hash (dict
), e inteiros abitrariamente longos (long
).Python suporta múltiplos paradigmas de programação, incluindo orientação a objetos (class
), imperativa (def
), e programação funcional (lambda
). Python tem um sistema de tipagem dinâmica e gerenciamento de memória automático que usa contagem de referências (similar à Perl, Ruby, e Scheme).
Python foi lançado inicialmente por Guido van Rossum em 1991. A linguagem possui um modelo de desenvolvimento aberto e baseado na comunidade, gerenciado pela fundação, sem fins lucrativos, Python Software Foundation. Existem muitos interpretadores e compiladores que implementam a linguagem Python, incluindo um em Java (Jython), mas, nesta breve revisão, nos referimos à implementação da referência C criada por Guido.
Você pode encontrar muitos tutoriais, a documentação oficial e as referências bibliográficas da linguagem no site oficial do Python.[python]
Para referências adicionais sobre Python, podemos recomendar os livros em ref.[guido] e ref.[lutz] .
Você pode ignorar este capítulo se você já está familiarizado com a linguagem Python.
Iniciando
As distribuições binárias do web2py para o Microsoft Windows ou Apple OS X são fornecidas com o interpretador Python incorporado no próprio arquivo de distribuição.
Você pode iniciá-lo no Windows com o seguinte comando (digite no prompt do DOS):
web2py.exe -S welcome
No Apple OS X, digite o seguinte comando em uma janela do Terminal (supondo que você esteja na mesma pasta que web2py.app):
./web2py.app/Contents/MacOS/web2py -S welcome
Em um Linux ou outro ambiente Unix, é provável que você já tenha o Python instalado. Em caso afirmativo, em um prompt do shell, digite:
python web2py.py -S welcome
Se você não possui o Python 2.5 (ou posterior 2.x) já instalado, você terá que fazer o download e instalá-lo antes de executar o web2py.
A opção de linha de comando -S welcome
instrui o web2py a executar o shell interativo como se os comandos fossem executados em um controlador para a aplicação ** welcome **, a aplicação scaffolding web2py. Isso expõe quase todas as classes, objetos e funções do web2py. Esta é a única diferença entre a linha de comando interativa web2py e a linha de comando normal do Python.
A interface administrativa também fornece um shell baseado na web para cada aplicação. Você pode acessar a aplicação "welcome" em.
http://127.0.0.1:8000/admin/shell/index/welcome
Você pode experimentar todos os exemplos neste capítulo usando o shell normal ou o shell baseado na web.
help, dir
A linguagem Python fornece dois comandos para obter documentação sobre objetos definidos no escopo atual, ambos integrados e definidos pelo usuário.
Podemos pedir ajuda (help
) sobre um objeto, por exemplo "1":
>>> help(1)
Help on int object:
class int(object)
| int(x[, base]) -> integer
|
| Convert a string or number to an integer, if possible. A floating point
| argument will be truncated towards zero (this does not include a string
| representation of a floating point number!) When converting a string, use
| the optional base. It is an error to supply a base when converting a
| non-string. If the argument is outside the integer range a long object
| will be returned instead.
|
| Methods defined here:
|
| __abs__(...)
| x.__abs__() <==> abs(x)
...
e, como "1" é um número inteiro, obtemos uma descrição sobre a classe int
e todos os seus métodos. Aqui, a saída foi truncada porque é muito longa e detalhada.
Da mesma forma, podemos obter uma lista de métodos do objeto "1" com o comando dir
:
>>> dir(1)
['__abs__', ..., '__xor__']
Tipos
Python é uma linguagem dinamicamente tipada, o que significa que as variáveis não têm um tipo e, portanto, não precisam ser declaradas. Os valores, por outro lado, têm um tipo. Você pode consultar uma variável para o tipo de valor que contém:
>>> a = 3
>>> print type(a)
<type 'int'>
>>> a = 3.14
>>> print type(a)
<type 'float'>
>>> a = 'hello python'
>>> print type(a)
<type 'str'>
O Python também inclui, nativamente, estruturas de dados como listas e dicionários.
str
Python suporta o uso de dois tipos diferentes de strings: strings ASCII e strings Unicode. As strings ASCII são delimitadas por '...', "..." or by '..' or """...""". Aspas triplas delimitam strings multilinhas. Strings Unicode são iniciadas com um u
seguido por uma string contendo caracteres Unicode. Uma string Unicode pode ser convertida em uma string ASCII, escolhendo-se uma codificação, por exemplo:
>>> a = 'esta é uma string ASCII'
>>> b = u'Esta é uma string Unicode'
>>> a = b.encode('utf8')
Depois de executar esses três comandos, a resultante a
é uma string ASCII que armazena caracteres codificados em UTF8. Por design, o web2py usa internamente strings codificadas em UTF8.
Também é possível escrever variáveis em strings de várias maneiras:
>>> print 'número é ' + str(3)
número é 3
>>> print 'número é %s' % (3)
número é 3
>>> print 'número é %(number)s' % dict(number=3)
número é 3
A última notação é mais explícita e menos propensa a erros, e é preferível.
Muitos objetos Python, por exemplo números, podem ser serializados em strings usando str
or repr
. Esses dois comandos são muito semelhantes, mas produzem saídas ligeiramente diferentes. Por exemplo:
>>> for i in [3, 'olá']:
print str(i), repr(i)
3 3
olá 'olá'
Para classes definidas pelo usuário, str
and repr
podem ser definidos/redefinidos usando os operadores especiais __str__
and __repr__
. Estes são brevemente descritos mais tarde; Para mais, consulte a documentação oficial do Python[pydocs] . repr
sempre tem um valor padrão.
Outra característica importante de uma string Python é que, como uma lista, ela é um objeto iterável.
>>> for i in 'olá':
print i
o
l
á
list
Os principais métodos de uma lista Python são append, insert, e delete:
>>> a = [1, 2, 3]
>>> print type(a)
<type 'list'>
>>> a.append(8)
>>> a.insert(2, 7)
>>> del a[0]
>>> print a
[2, 7, 3, 8]
>>> print len(a)
4
Listas podem ser fatiadas:
>>> print a[:3]
[2, 7, 3]
>>> print a[1:]
[7, 3, 8]
>>> print a[-2:]
[3, 8]
e concatenadas:
>>> a = [2, 3]
>>> b = [5, 6]
>>> print a + b
[2, 3, 5, 6]
Uma lista é iterável; você pode fazer um loop por ela:
>>> a = [1, 2, 3]
>>> for i in a:
print i
1
2
3
Os elementos de uma lista não precisam ser do mesmo tipo; eles podem ser qualquer tipo de objeto Python.
Existe uma situação muito comum na qual uma "list comprehension" pode ser usada. Considere o seguinte código:
>>> a = [1,2,3,4,5]
>>> b = []
>>> for x in a:
if x % 2 == 0:
b.append(x * 3)
>>> b
[6, 12]
Este código processa claramente uma lista de itens, seleciona e modifica um subconjunto da lista de entrada e cria uma nova lista de resultados, e esse código pode ser completamente substituído pela seguinte list comprehension:
>>> a = [1,2,3,4,5]
>>> b = [x * 3 for x in a if x % 2 == 0]
>>> b
[6, 12]
tuple
Uma tupla é como uma lista, mas seu tamanho e elementos são imutáveis, enquanto que em uma lista são mutáveis. Se um elemento de tupla é um objeto, os atributos do objeto são mutáveis. Uma tupla é delimitada por parênteses.
>>> a = (1, 2, 3)
Então, enquanto isso funciona para uma lista:
>>> a = [1, 2, 3]
>>> a[1] = 5
>>> print a
[1, 5, 3]
a atribuição de elementos não funciona para uma tupla:
>>> a = (1, 2, 3)
>>> print a[1]
2
>>> a[1] = 5
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
TypeError: 'tuple' object does not support item assignment
Uma tupla, como uma lista, é um objeto iterável. Observe que uma tupla constituída por um único elemento deve incluir uma vírgula posterior, como mostrado abaixo:
>>> a = (1)
>>> print type(a)
<type 'int'>
>>> a = (1,)
>>> print type(a)
<type 'tuple'>
Tuplas são muito úteis para a embalagem eficiente de objetos devido à sua imutabilidade, e os parênteses geralmente são opcionais:
>>> a = 2, 3, 'olá'
>>> x, y, z = a
>>> print x
2
>>> print z
olá
dict
Um dicionário Python (dict
) é uma tabela hash que mapeia um ojeto chave a um objeto valor. Por exemplo:
>>> a = {'k':'v', 'k2':3}
>>> a['k']
v
>>> a['k2']
3
>>> a.has_key('k')
True
>>> a.has_key('v')
False
As chaves podem ser de qualquer tipo hasheável (int, string ou qualquer objeto cuja classe implemente o método __hash__
). Valores podem ser de qualquer tipo. Diferentes chaves e valores no mesmo dicionário não precisam ser do mesmo tipo. Se as chaves são caracteres alfanuméricos, um dicionário também pode ser declarado com a sintaxe alternativa:
>>> a = dict(k='v', h2=3)
>>> a['k']
v
>>> print a
{'k':'v', 'h2':3}
Métodos úteis são has_key
, keys
, values
e items
:
>>> a = dict(k='v', k2=3)
>>> print a.keys()
['k', 'k2']
>>> print a.values()
['v', 3]
>>> print a.items()
[('k', 'v'), ('k2', 3)]
O método items
produz uma lista de tuplas, cada uma contendo uma chave e seu valor associado.
Os elementos de um dicionário e os elementos de uma lista podem ser excluídos com o comando del
:
>>> a = [1, 2, 3]
>>> del a[1]
>>> print a
[1, 3]
>>> a = dict(k='v', h2=3)
>>> del a['h2']
>>> print a
{'k':'v'}
Internamente, Python usa o operador hash
para converter objetos em inteiros e usa esse inteiro para determinar onde armazenar o valor.
>>> hash("olá mundo")
-1500746465
Sobre indentação
Python usa indentação para delimitar blocos de código. Um bloco começa com uma linha que termina em dois pontos, e continua por todas as linhas que têm uma indentação semelhante ou maior que a próxima linha. Por exemplo:
>>> i = 0
>>> while i < 3:
>>> print i
>>> i = i + 1
>>>
0
1
2
É comum usar quatro espaços para cada nível de indentação. É uma boa política não misturar tabulações com espaços, o que pode resultar em confusão (invisível).
for...in
Em Python, você pode percorrer objetos iteráveis usando laços:
>>> a = [0, 1, 'olá', 'python']
>>> for i in a:
print i
0
1
olá
python
Um atalho comum é xrange
, que gera um intervalo iterável sem armazenar toda a lista de elementos.
>>> for i in xrange(0, 4):
print i
0
1
2
3
Isso é equivalente à sintaxe C/C++/C#/Java:
for(int i=0; i<4; i=i+1) { print(i); }
Outro comando útil é enumerate
, que conta enquanto percorre o laço:
>>> a = [0, 1, 'olá', 'python']
>>> for i, j in enumerate(a):
print i, j
0 0
1 1
2 olá
3 python
Há também a palavra-chave range(a, b, c)
que retorna uma lista de inteiros começando com o valor a
, incrementando em c
e terminando com o último valor menor do que b
, o valor padrão para a
é 0 e para c
é 1. xrange
é semelhante, mas na verdade não gera a lista, apenas um iterador sobre a lista; assim, é melhor para laços.
Você pode sair de um laço usando break
>>> for i in [1, 2, 3]:
print i
break
1
Você pode pular para a próxima iteração do laço sem executar todo o bloco de código com continue
>>> for i in [1, 2, 3]:
print i
continue
print 'teste'
1
2
3
while
O laço while
em Python funciona muito como em várias outras linguagens de programação, fazendo um laço de um número indefinido de vezes e testando uma condição antes de cada iteração. Se a condição for False
, o laço termina.
>>> i = 0
>>> while i < 10:
i = i + 1
>>> print i
10
Não há a estrutura loop...until
em Python.
if...elif...else
>>> for i in range(3):
>>> if i == 0:
>>> print 'zero'
>>> elif i == 1:
>>> print 'um'
>>> else:
>>> print 'outro'
zero
um
outro
"elif" significa "else if". Ambas as cláusulas elif
e else
são opcionais. Pode haver mais de uma cláusula elif
mas apenas uma cláusula else
. Condições complexas podem ser criadas usando os operadores not
, and
e or
.
>>> for i in range(3):
>>> if i == 0 or (i == 1 and i + 1 == 2):
>>> print '0 or 1'
try...except...else...finally
>>> try:
>>> a = 1 / 0
>>> except Exception, e:
>>> print 'oops: %s' % e
>>> else:
>>> print 'nenhum problema aqui'
>>> finally:
>>> print 'feito'
oops: integer division or modulo by zero
feito
Se a exceção for levantada, ela é capturada pela cláusula except
, que é executada, enquanto a cláusula else
não é. Se nenhuma exceção for levantada, a cláusula except
não será executada, mas o else
o será. A cláusula finally
sempre é executada.
Pode haver várias cláusulas except
para diferentes exceções possíveis:
>>> try:
>>> raise SyntaxError
>>> except ValueError:
>>> print 'erro de valor'
>>> except SyntaxError:
>>> print 'erro de sintaxe'
erro de sintaxe
As cláusulas else
e finally
são opcionais.
Aqui está uma lista de exceções embutidas de Python + HTTP (definida pelo web2py)
BaseException
+-- HTTP (definida pelo web2py)
+-- SystemExit
+-- KeyboardInterrupt
+-- Exception
+-- GeneratorExit
+-- StopIteration
+-- StandardError
| +-- ArithmeticError
| | +-- FloatingPointError
| | +-- OverflowError
| | +-- ZeroDivisionError
| +-- AssertionError
| +-- AttributeError
| +-- EnvironmentError
| | +-- IOError
| | +-- OSError
| | +-- WindowsError (Windows)
| | +-- VMSError (VMS)
| +-- EOFError
| +-- ImportError
| +-- LookupError
| | +-- IndexError
| | +-- KeyError
| +-- MemoryError
| +-- NameError
| | +-- UnboundLocalError
| +-- ReferenceError
| +-- RuntimeError
| | +-- NotImplementedError
| +-- SyntaxError
| | +-- IndentationError
| | +-- TabError
| +-- SystemError
| +-- TypeError
| +-- ValueError
| | +-- UnicodeError
| | +-- UnicodeDecodeError
| | +-- UnicodeEncodeError
| | +-- UnicodeTranslateError
+-- Warning
+-- DeprecationWarning
+-- PendingDeprecationWarning
+-- RuntimeWarning
+-- SyntaxWarning
+-- UserWarning
+-- FutureWarning
+-- ImportWarning
+-- UnicodeWarning
Para uma descrição detalhada de cada um delas, consulte a documentação oficial do Python.
web2py expõe apenas uma nova exceção, chamada HTTP
. Quando levantada, faz com que o programa devolva uma página de erro HTTP (para mais informações, consulte o Capítulo 4).
Qualquer objeto pode ser levantado como uma exceção, mas é uma boa prática levantar objetos que estendam uma das classes de exceções embutidas.
def...return
Funções são declaradas usando def
. Aqui está uma função típica de Python:
>>> def f(a, b):
return a + b
>>> print f(4, 2)
6
Não há necessidade (ou forma) de especificar tipos de argumentos ou o(s) tipo(s) de retorno. Neste exemplo, uma função f
, que pode levar dois argumentos, é definida.
Funções são o primeiro recurso de sintaxe de código descrito neste capítulo para introduzir o conceito de escopo ou namespace. No exemplo acima, os identificadores a
e b
estão indefinidos fora do escopo da função f
:
>>> def f(a):
return a + 1
>>> print f(1)
2
>>> print a
Traceback (most recent call last):
File "<pyshell#22>", line 1, in <module>
print a
NameError: name 'a' is not defined
Identificadores definidos fora do escopo da função são acessíveis dentro da função; observe como o identificador a
é tratado no seguinte código:
>>> a = 1
>>> def f(b):
return a + b
>>> print f(1)
2
>>> a = 2
>>> print f(1) # o novo valor de a é usado
3
>>> a = 1 # reinicia a
>>> def g(b):
a = 2 # cria um novo a local
return a + b
>>> print g(2)
4
>>> print a # a global não é modificado
1
Se a
for modificado, as chamadas de função subseqüentes usarão o novo valor do a
global porque a definição da função vincula o local de armazenamento do identificador a
, não o valor do próprio a
no momento da declaração da função; no entanto, se a
for atribuído dentro da função g
, o a
global não é afetado porque o novo a
local esconde o valor global. A referência de escopo externo pode ser usada na criação de closures:
>>> def f(x):
def g(y):
return x * y
return g
>>> duplicador = f(2) # duplicador é uma nova função
>>> triplicador = f(3) # triplicador é uma nova função
>>> quadruplicador = f(4) # quadruplicador é uma nova função
>>> print duplicador(5)
10
>>> print triplicador(5)
15
>>> print quadruplicador(5)
20
A função f
cria novas funções; e note que o escopo do nome g
é inteiramente interno de f
. Closures são extremamente poderosas.
Argumentos de funções podem ter valores padrão, e pode retornar vários resultados:
>>> def f(a, b=2):
return a + b, a - b
>>> x, y = f(5)
>>> print x
7
>>> print y
3
Argumentos de funções podem ser passados explicitamente por nome e isso significa que a ordem dos argumentos especificados na chamada pode ser diferente da ordem dos argumentos com os quais a função foi definida:
>>> def f(a, b=2):
return a + b, a - b
>>> x, y = f(b=5, a=2)
>>> print x
7
>>> print y
-3
Funções também podem ter um número variável de argumentos em tempo de execução:
>>> def f(*a, **b):
return a, b
>>> x, y = f(3, 'olá', c=4, test='mundo')
>>> print x
(3, 'olá')
>>> print y
{'c':4, 'test':'mundo'}
Aqui os argumentos não passados pelo nome (3, 'olá') são armazenados na tupla a
, e os argumentos passados pelo nome (c
e test
) são armazenados no dicionário b
.
No caso oposto, uma lista ou tupla pode ser passada para uma função que requer argumentos posicionais individuais, descompactando-os:
>>> def f(a, b):
return a + b
>>> c = (1, 2)
>>> print f(*c)
3
e um dicionário pode ser descompactado para entregar argumentos nomeados:
>>> def f(a, b):
return a + b
>>> c = {'a':1, 'b':2}
>>> print f(**c)
3
lambda
lambda
fornece uma maneira de criar uma função muito curta e sem nome muito facilmente:
>>> a = lambda b: b + 2
>>> print a(3)
5
A expressão "lambda
[a]: [b]" literalmente lê-se como "uma função com argumentos [a] que retorna [b]". A expressão lambda
não é nomeada, mas a função adquire um nome sendo atribuída ao identificador a
. As regras de escopo para def
aplicam-se igualmente a lambda
e, de fato, o código acima, em relação a a
, é idêntico à declaração de função usando def
:
>>> def a(b):
return b + 2
>>> print a(3)
5
O único benefício de lambda
é a brevidade; No entanto, a brevidade pode ser muito conveniente em certas situações. Considere uma função chamada map
que aplica uma função a todos os itens de uma lista, criando uma nova lista:
>>> a = [1, 7, 2, 5, 4, 8]
>>> map(lambda x: x + 2, a)
[3, 9, 4, 7, 6, 10]
Este código teria duplicado em tamanho se def
tivesse sido usado em vez de lambda
. A principal desvantagem de lambda
é que (na implementação do Python) a sintaxe permite apenas uma única expressão; no entanto, para funções mais longas, def
pode ser usado e o custo extra de fornecer um nome de função diminui à medida que o comprimento da função cresce. Assim como def
, lambda
pode ser usado para funções "curry": novas funções podem ser criadas adicionando-se um invólucro para funções existentes, de modo que a nova função tenha um conjunto diferente de argumentos:
>>> def f(a, b): return a + b
>>> g = lambda a: f(a, 3)
>>> g(2)
5
Há muitas situações em que o currying é útil, mas uma delas é diretamente útil no web2py: cache. Suponha que você tenha uma função cara que verifique se seu argumento é primo:
def isprime(number):
for p in range(2, number):
if (number % p) == 0:
return False
return True
Esta função é, obviamente, demorada.
Suponha que você tenha uma função de cache cache.ram
que leva três argumentos: uma chave, uma função e um número de segundos.
value = cache.ram('key', f, 60)
A primeira vez que é chamada, ela chama a função f()
, armazena a saída em um dicionário na memória (digamos "d") e o retorna de forma que esse valor seja:
value = d['key']=f()
A segunda vez que é chamada, se a chave está no dicionário e não é mais antiga que o número de segundos especificado (60), ela retorna o valor correspondente sem executar a chamada de função.
value = d['key']
Como você armazenaria em cache a saída da função isprime para qualquer entrada? Aqui está como:
>>> number = 7
>>> seconds = 60
>>> print cache.ram(str(number), lambda: isprime(number), seconds)
True
>>> print cache.ram(str(number), lambda: isprime(number), seconds)
True
O resultado é sempre o mesmo, mas na primeira vez que cache.ram
é chamada, isprime
é chamada; Na segunda vez não é.
Funções Python, criadas com
def
oulambda
permitem refatorar funções existentes em termos de um conjunto diferente de argumentos.cache.ram
ecache.disk
são funções de cache do web2py.
class
Como Python é dinamicamente tipada, as classes e objetos Python podem parecer estranhos. Na verdade, você não precisa definir as variáveis de membros (atributos) ao declarar uma classe, e diferentes instâncias da mesma classe podem ter atributos diferentes. Os atributos geralmente são associados à instância, não à classe (exceto quando declarado como "atributos de classe", que é o mesmo que "variáveis de membros estáticos" em C++/Java).
Eis um exemplo:
>>> class MyClass(object): pass
>>> myinstance = MyClass()
>>> myinstance.myvariable = 3
>>> print myinstance.myvariable
3
Note que pass
é um comando nada-faça. Neste caso, ele é usado para definir uma classe MyClass
que não contém nada. MyClass()
chama o construtor da classe (neste caso o construtor padrão) e retorna um objeto, uma instância da classe. O (object)
na definição da classe indica que nossa classe expande a classe incorporada object
. Isso não é necessário, mas é uma boa prática.
Eis uma classe mais complexa:
>>> class MyClass(object):
>>> z = 2
>>> def __init__(self, a, b):
>>> self.x = a
>>> self.y = b
>>> def add(self):
>>> return self.x + self.y + self.z
>>> myinstance = MyClass(3, 4)
>>> print myinstance.add()
9
Funções declaradas dentro da classe são métodos. Alguns métodos possuem nomes especiais reservados. Por exemplo, __init__
é o construtor. Todas as variáveis são variáveis locais do método, exceto as variáveis declaradas fora dos métodos. Por exemplo, z
é uma variável de classe, equivalente a uma "variável de membro estático" C++ que contém o mesmo valor para todas as instâncias da classe.
Observe que __init__
leva 3 argumentos e add
leva um, e ainda assim as chamamos com 2 e 0 argumentos, respectivamente. O primeiro argumento representa, por convenção, o nome local usado dentro do método para se referir ao objeto atual. Aqui, usamos self
para nos referirmos ao objeto atual, mas poderíamos ter usado qualquer outro nome. self
desempenha o mesmo papel que *this
em C++ ou this
em Java, mas self
não é uma palavra-chave reservada.
Essa sintaxe é necessária para evitar ambigüidade ao declarar classes aninhadas, como uma classe que é local para um método dentro de outra classe.
Atributos, métodos e operadores especiais
Os atributos de classe, métodos e operadores que começam com um sublinhado duplo geralmente se destinam a ser privados (isto é, ser usado internamente, mas não exposto fora da classe), embora esta seja uma convenção que não seja aplicada pelo interpretador.
Alguns deles são palavras-chave reservadas e têm um significado especial.
Aqui, como exemplo, estão três delas:
__len__
__getitem__
__setitem__
Elas podem ser usadas, por exemplo, para criar um objeto contêiner que age como uma lista:
>>> class MyList(object):
>>> def __init__(self, *a): self.a = list(a)
>>> def __len__(self): return len(self.a)
>>> def __getitem__(self, i): return self.a[i]
>>> def __setitem__(self, i, j): self.a[i] = j
>>> b = MyList(3, 4, 5)
>>> print b[1]
4
>>> b.a[1] = 7
>>> print b.a
[3, 7, 5]
Outros operadores especiais incluem __getattr__
e __setattr__
, que definem os atributos get e set para a classe, e __sum__
e __sub__
, que sobrecarregam operadores aritméticos. Para o uso desses operadores, encaminhamos o leitor para livros mais avançados sobre esse assunto. Nós já mencionamos os operadores especiais __str__
e __repr__
.
Entrada/Saída em arquivos
Em Python você pode abrir e escrever em um arquivo com:
>>> file = open('myfile.txt', 'w')
>>> file.write('olá mundo')
>>> file.close()
Da mesma forma, você pode ler de volta do arquivo com:
>>> file = open('myfile.txt', 'r')
>>> print file.read()
olá mundo
Alternativamente, você pode ler em modo binário com "rb", escrever em modo binário com "wb" e abrir o arquivo no modo de anexação "a", usando a notação C padrão.
O comando read
leva um argumento opcional, que é o número de bytes. Você também pode saltar para qualquer local em um arquivo usando seek
.
Você pode ler de volta do arquivo com read
>>> print file.seek(4)
>>> print file.read()
mundo
e você pode fechar o arquivo com:
>>> file.close()
Na distribuição padrão de Python, que é conhecida como CPython, as variáveis têm suas referências contadas, incluindo aquelas contendo manipulações de arquivos, então CPython sabe que, quando a contagem das referências de um identificador de arquivo aberto diminui para zero, o arquivo pode ser fechado e a variável descartada. No entanto, em outras implementações do Python, como o PyPy, a coleta de lixo é usada em vez da contagem de referência, e isso significa que é possível que haja o acúmulo de muitas manipulações de arquivos abertas ao mesmo tempo, resultando em um erro antes que gc (garbage-colector) tenha a chance de fechar e descartar todas elas. Portanto, é melhor fechar explicitamente as manipulações de arquivos quando elas não são mais necessárias. web2py'' fornece duas funções auxiliares, read_file()
e write_file()
dentro do namespace gluon.fileutils
que encapsulam o acesso ao arquivo e garantem que as manipulações de arquivo usadas sejam corretamente fechadas.
Ao usar o web2py, você não sabe onde está o diretório atual, porque isso depende de como o web2py está configurado. A variável
request.folder
contém o caminho para o aplicativo atual. Os caminhos podem ser concatenados com o comandoos.path.join
, discutido abaixo.
exec
, eval
Ao contrário de Java, Python é uma linguagem verdadeiramente interpretada. Isso significa que ela tem a capacidade de executar instruções Python armazenadas em strings. Por exemplo:
>>> a = "print 'olá mundo'"
>>> exec(a)
'olá mundo'
O que acabou de acontecer? A função exec
informa ao interpretador para se chamar e executar o conteúdo da string passada como argumento. Também é possível executar o conteúdo de uma string dentro de um contexto definido pelos símbolos em um dicionário:
>>> a = "print b"
>>> c = dict(b=3)
>>> exec(a, {}, c)
3
Aqui, o interpretador, ao executar a string a
, vê os símbolos definidos em c
(b
no exemplo), mas não vê c
ou a
. Isso é diferente de um ambiente restrito, uma vez que exec
não limita o que o código interno pode fazer; apenas define o conjunto de variáveis visíveis para o código.
Uma função relacionada é eval
, que funciona muito como exec
, exceto que ela espera que o argumento seja computado como um valor e retorna esse valor.
>>> a = "3*4"
>>> b = eval(a)
>>> print b
12
import
Por exemplo, se você precisar usar um gerador de números aleatórios, você pode fazer:
>>> import random
>>> print random.randint(0, 9)
5
Isso imprime um número inteiro aleatório entre 0 e 9 (incluindo o 9), 5 no exemplo. A função randint
é definida no módulo random
. Também é possível importar um objeto de um módulo para o namespace atual:
>>> from random import randint
>>> print randint(0, 9)
ou importar todos os objetos a partir de um módulo para o namespace atual:
>>> from random import *
>>> print randint(0, 9)
ou importar tudo em um namespace recém-definido:
>>> import random as myrand
>>> print myrand.randint(0, 9)
No restante deste livro, usaremos principalmente objetos definidos nos módulos os
, sys
, datetime
, time
e cPickle
.
Todos os objetos web2py são acessíveis através de um módulo chamado
gluon
, e esse é o assunto de capítulos posteriores. Internamente, o web2py usa muitos módulos Python (por exemplothread
), mas raramente você precisa acessá-los diretamente.
Nas subseções a seguir, consideramos os módulos mais úteis.
os
Este módulo fornece uma interface para a API do sistema operacional. Por exemplo:
>>> import os
>>> os.chdir('..')
>>> os.unlink('nome_do_arquivo_a_ser_apagado')
Algumas das funções de
os
, comochdir
, NÃO DEVEM ser usadas no web2py porque elas não são thread-safe.
os.path.join
é muito útil; ela permite a concatenação de caminhos de forma independente do sistema operacional:
>>> import os
>>> a = os.path.join('path', 'sub_path')
>>> print a
path/sub_path
As variáveis de ambiente do sistema podem ser acessadas através de:
>>> print os.environ
que é um dicionário somente leitura.
sys
O módulo sys
contém muitas variáveis e funções, mas o que mais usamos é sys.path
. Ele contém uma lista de caminhos onde Python procura por módulos. Quando tentamos importar um módulo, Python procura por todas as pastas listadas em sys.path
. Se você instalar módulos adicionais em algum local e quiser que Python os encontre, você deve anexar o caminho para essa localização a sys.path
.
>>> import sys
>>> sys.path.append('caminho/para/meus/modulos')
Ao executar o web2py, Python permanece residente na memória, e existe apenas um sys.path
, enquanto há muitas threads atendendo os pedidos HTTP. Para evitar um vazamento de memória, é melhor verificar se um caminho já está presente antes de adicionar:
>>> path = 'caminho/para/meus/modulos'
>>> if not path in sys.path:
sys.path.append(path)
datetime
O uso do módulo datetime é melhor ilustrado por alguns exemplos:
>>> import datetime
>>> print datetime.datetime.today()
2008-07-04 14:03:90
>>> print datetime.date.today()
2008-07-04
Ocasionalmente, você pode precisar carimbar dados com base no tempo UTC em oposição à hora local. Neste caso, você pode usar a seguinte função:
>>> import datetime
>>> print datetime.datetime.utcnow()
2008-07-04 14:03:90
O módulo datetime contém várias classes: date, datetime, time e timedelta. A diferença entre dois objetos date ou dois objetos datetime ou dois objetos time é um timedelta:
>>> a = datetime.datetime(2008, 1, 1, 20, 30)
>>> b = datetime.datetime(2008, 1, 2, 20, 30)
>>> c = b - a
>>> print c.days
1
Em web2py, date e datetime são usados para armazenar os tipos SQL correspondentes quando passados ou retornados do banco de dados.
time
O módulo time difere de date
e datetime
pois representa o tempo como os segundos acumulados da época (começando em 1970).
>>> import time
>>> t = time.time()
1215138737.571
Consulte a documentação Python para as funções de conversão de time para segundos e time para datetime
.
cPickle
Este é um módulo muito poderoso. Ele fornece funções que podem serializar quase qualquer objeto Python, incluindo objetos auto-referenciais. Por exemplo, vamos construir um objeto estranho:
>>> class MyClass(object): pass
>>> myinstance = MyClass()
>>> myinstance.x = 'algo'
>>> a = [1 ,2, {'olá':'mundo'}, [3, 4, [myinstance]]]
e agora:
>>> import cPickle
>>> b = cPickle.dumps(a)
>>> c = cPickle.loads(b)
Neste exemplo, b
é uma representação string de a
, e c
é uma cópia de a
gerada de-serializando b
.
cPickle também pode serializar de e de-serializar a partir de um arquivo:
>>> cPickle.dump(a, open('myfile.pickle', 'wb'))
>>> c = cPickle.load(open('myfile.pickle', 'rb'))