AspectosFormaisDaLinguagemPython

Aspectos Formais da Linguagem Python

Análise Léxica e Modelo de Dados

Texto apresentado em palestra do Grupo de Interesse em Linguagens de Programação da Fundação Universidade Federal do Rio Grande (GRULING), 1998.

Resumo

Este artigo tem por objetivo fazer uma descrição da linguagem Python do ponto de vista teórico. Este texto não tem a pretensão de ser um tutorial, mas sim de esclarecer uma pessoa já usuária da linguagem ou que tenha noções em linguagens de programação de como é a estrutura interna desta linguagem. O artigo faz inicialmente uma análize léxica da linguagem, passando, em seguida, pelos modelos de dados e de execução, expressões, senteças e componentes Toplevel.

O presente texto procura resumir os temas abordados em 1 incorporando exemplos mais práticos e fazendo refrências as demais referências bibliográficas.

Introdução

A linguagem Python é uma linguagem de alto nível, interpretada, orientada a objetos com uma semântica dinâmica. Suas estruturas de alto nível, combinadas com sua tipagem de amarração dinâmica a faz muito atrativa para desenvolvimento de largos aplicativos assim como para uso como linguagem de script ou de colagem.

A sintaxe simples do Python encoraja a reutilização de código simplificando a manutenção e a normalização de dados em módulos e pacotes distintos.

Esta linguagem, bem como seu código fonte, se encontram gratuitamente disponível na Internet podendo ser vasculhados por qualquer interessado sem ônus algum. Apesar disto, a linguagem Python é marca registrada do Stichting Mathematisch Centrum de Amsterdam. Todos os direitos reservados.

A linguagem Python foi desenvolvida pelo holandês Guido Van Rossun, no final de 1990. Segundo Van Rossum, a linguagem surgiu enquanto este passava o tempo entre o Natal de 1990 e o ano novo de 1991, mexendo na linguagem ABC, a qual ele havia participado do grupo que a desenvolvera. Já no começo de 1991, Van Rossum já havia feito uma especificação bastante próxima a esta feita neste texto.

Hoje a linguagem Python vem sendo utilizada por milhares de pessoas ao redor do mundo, sendo sustentada por uma fundação, a CNRI, e por um sem número de voluntários ao redor do mundo, unidos via Internet, formando a Python Software Activity (PSA). Neste momento, está sendo proposta a criação de um movimento chamado Python Consortium que tem por objetivo passar o fomento da linguagem para um pool de empresas e instituições de ensino e pesquisa. Esta proposta está sendo apresentada na Sétima conferência Internacional da Linguagem Python que aconteceu agora em novembro, em Houston, Texas.

De volta ao artigo, vamos, a seguir, iniciar o trabalho de análise léxica da linguagem. Você observará que o texto não será baseado em metodologias formais de especificação de linguagens, como a BNF, dando portanto, margens para ambigüidades. Mas, como o texto é de caráter ilustrativo, o autor escolheu a linguagem natural como o melhor formato para apresentá-lo.

Análise Léxica

O interpretador Python interpreta senteças recebidas de algum dispositivo de entrada (console ou arquivo), Esta entrada é lida pelo analizador léxico, que divide o código recebido em tokens, repassando-o para o parser que interpreta o programa.

O Python é totalmente definido utilizando a tabela ASCII de 7 bits. 2

Linhas

Um programa em Python é dividido em linhas lógicas. Estas linhas lógicas são separadas pelo token NEWLINE e podem ser constituídas de uma ou mais linhas físicas. Estas linhas físicas são trechos de programas divididos pelo caractere ENTER.

Uma linha lógica não pode ultrapassar uma linha física, com exceção de dois casos especiais. O primeiro caso é o ajuntamento explícito de linhas quando um caractere de barra invertida é posto no final da linha.

   1 soma = a + b + c + d + e + f + g \
   2        h + i + j + k + l + m

O segundo é o ajuntamento implícito de linhas que ocorre dentro de expressões delimitadas por colchetes, parênteses ou chaves, como no exemplo abaixo.

   1 dias_da_semana = ['dom','seg','ter',
   2                   'qua','qui','sex','sab']

Os comentários são identificados pelo símbolo hash (#). todo o conteúdo de uma linha que estiver a direita deste símbolo é desconsiderado. O comentário só vale na linha que contém o hash. As linhas totalmente em branco são disconsideradas pelo interpretador.

Indentação

Uma peculiaridade do Python é a de não possuir terminadores de blocos explícitos, como begin e end no Pascal, ou { e } no C. Ao invés disto, o Python utiliza a própria indentação do programa como referência para que o analizador léxico determine o escopo dos blocos. Neste conceito, cada linha lógica possui um valor chamado de nível de indentação que é o número da primeira coluna não branca da linha. Os delimitadores de blocos são chamados de INDENT e DEDENT, e são encontrados através de uma pilha, como descrito abaixo.

Antes que a primeira linha do arquivo seja lida, um zero é posto numa pilha, este zero não mais sairá da pilha. Os números postos na pilha irão sempre crescer do fundo até o topo da pilha, ou seja, cada novo valor na pilha é sempre maior que o valor anterior. No começo de cada linha lógica, o nível de indentação é comparado como o número do topo da pilha. Se este número for igual, a pilha permanece inalterada, Se o número for maior, a pilha recebe o valor do nível de indentação da linha e um delimitador INDENT é adicionado. Se o nível de indentação for menor que o valor da pilha, a pilha é sucessivamente desempilhada, até que se encontre na pilha o valor do nível da linha. Se este valor não for encontrado o interpretador acusa um erro de sintaxe. Para cada valor que sai da pilha, é adicionado um identificador DEDENT.

A seguir, um programa onde são mostrados os delimitadores de linhas e blocos.

   1         def perm(1):                        NEWLINE
   2         # Compute the list for all permutations of 1
   3 INDENT      if len(l)<=1:                   NEWLINE
   4 INDENT         return[1]                    NEWLINE
   5 DEDENT      r=[]                            NEWLINE
   6             for i in range(len(l)):         NEWLINE
   7 INDENT          s=l[:i]+ \
   8                 l[i+1:]                     NEWLINE
   9                 p=perm(s)                   NEWLINE
  10                 for x in p:                 NEWLINE
  11 INDENT             r.append (l[i:i+1]+x)    NEWLINE
  12 DEDENT
  13 DEDENT      return r
  14 DEDENT

Vamos agora a um exemplo com vários erros de indentação:

   1    def perm(l):         # primeira linha indentada
   2 for i in range (len(l)):  # não-indentada
   3    s=l[:i]+l[i+1:]
   4        p = perm (s)     # identação inesperada
   5   for x in p:
   6        r.append(l[i:i+1]+x)
   7      return r             # ''dedentação'' inconsistente

Outros tokens

Identificadores: uma letra seguido quantas letras ou números, ou barrasbaixas se desejar. Palavras-chave: as palavras-chave são as seguntes

and    assert  break class continue
def    del     elif  else  except
exec   finally for   from  global
if     import  in    is    lambda
not    or      pass  print raise
return try     while yeld

Classes Reservadas de Identificadores: alguns identificadores com significados especiais. Eles são reconhecíveis pelas seguintes máscaras: _* (não importável), __*__ (nome definido pelo sistema), __* (nome privado de uma classe). Strings: uma string é uma cadeia de caracteres delimitada por aspas simples ou aspas duplas. Também são válidos os caracteres de escape ao estilo da linguagem C. As strings precedidas por um "r" maiúsculo ou minúsculo consideram os caracteres de escape como caracteres comuns. Números inteiros: uma sequência de dígitos decimais, sucedidos de "X" se hexadecimal, ou precedido de "0" (zero) se octal. Ou seja, 123 é um número inteiro decimal, já 0123 é um número inteiro octal. Inteiros longos: são números inteiros sucedidos da letra "L', maiúscula ou minúscula. Números de ponto flutuante: é um número inteiro seguido por um separador decimal "." e uma parte fracionária. Pode-se usar a notação de base dez com a letra "E". Numeros imaginários: é um número de ponto flutuante seguido da letra "J". Operadores:

+  -  *  ** /  %
<< >> &  |  ^  ~
<  >  <= >= == != <>

Delimitadores:

( ) [ ] { }
, : . ' = ;

Caracteres com significados dependentes do contexto:

' `` # \

Caracteres totalmente inválidos: fora de strings, estes caracteres geram erro sintático imediato.

@ $ ?

Modelo de Dados

Objetos são a abstração do Python para todas as estruturas de dados. Todo programa em Python é composto por objetos ou por relação entre objetos.

Todo objeto tem uma identidade, que lhe é atribuída na sua criação e não pode ser mudada. O tipo do objeto também é imutável. Este tipo define as operações que podem ser efetuadas neste objeto. Os objetos também possuem um valor que pode ser de tipo mutável (listas, tuplas, etc.) ou imutável (números e strings).

Os objetos de Python, uma vez criados, não podem ser destruídos. O que ocorre normalmente é que o interpretador possui um serviço de limpeza automática de memória que finaliza todo o objeto que não é mais alcançável. Por exemplo: suponha um objeto que representa uma grande estrutura de dados. Se a este objeto for atribuído um objeto de tamanho pequeno, como por exemplo um inteiro, toda aquela estrutura de dados a ele associada anteriormente é finalizada e o espaço de memória é liberado. Esta função é extensível a tipos mais complexos, como arquivos, apesar de que sua utilização não é aconselhada pelos próprios criadores da linguagem, devido ao baixo nível de abstração que oferece.

Veremos a seguir os tipos padrão da linguagem Python. Devido a característica aberta da linguagem, estes tipos podem ser estendidos com a linguagem em C.

None: é o tipo nulo. pode ser invocado pelo identificador None e indica ausência de sentença. Seu valor lógico é falso.

Ellipsis: representa um objeto único identificado apenas por este nome. Serve para indicar a presença de elipse ("...") em uma seqüencia. Seu valor lógico é verdadeiro.

Números

São criados por literais numéricos ou são retornados por funções ou operações aritméticas. Objetos numéricos são imutáveis.

Números inteiros: são objetos que representam o conjunto matemático dos números inteiros. Inteiros simples: são inteiros de 32 bits de precisão finita. Englobam a faixa de -2147483648 até 2147483647, mas podem ser maiores, dependendo do processador e do sistema operacional. Um resultado fora desta faixa retorna um erro de Overflow. Não ocorre a perda do bit mais significativo fazendo com que o valor caia sempre dentro desta faixa. A representação binária do inteiro simples é em forma de complemento de 2.

...-1000,-1001,...,-3,-2,-1,0,1,2,3,...,1000,1001,...
0100 # octal
0x12 # hexadecimal

Interios longos: é uma outra classe de inteiros de abrangência ilimitada, ou limitada apenas pela memória da máquina. Uma operação bit-a-bit válida para os inteiros simples também é válida para os inteiros longos.

767468641767451361273676485352135L
fatorial (10000L) # acredite, eh BEM maior do que todo este texto.

Números de ponto flutuante: representam valores reais de precisão dupla (64 bits). os numeros de precisão simples não são utilizados porque, devido à estrutura de dados internamente complexa do Python, o projetista da linguagem concluiu que a economia de alguns bits é completamente sem propósito.

3.1415
1. # o ponto no numero inteiro serve para identificar um objeto como ponto flutuante
0.00000000231

Números complexos: são representados por um par de números de ponto flutuante, os quais podem ser extraídos independentemente pelos métodos real e imag.

complex (1,2)
(1+2j)
3 + 1j*3
(3+3j)

Sequências

São objetos que representam seqüências ordenadas e finita de outros objetos. A função len retorna o número de elementos de uma determinada seqüência. As seqüências são indexadas e podem ser recuperadas por um esquema de índices por exemplo:

   1 a = [i:j]

retorna todos os elementos cujos índices estão compreendidos no intervalo i <= k < j.

Uma explanação sobre as sequências mutáveis

Pode haver confusão em declarar que uma string é uma estrutura imutável posto que são válidas operações do tipo

a = "Rolling " a = a + "Stones"

Quando fazemos uma atribuição a uma string estamos redefinindo o seu valor, ou seja, na primeira linha, o objeto a recebe a string " Rolling ", que por sua vez é uma constante (ou um objeto imutável na terminologia adotada pelo Python). Na segunda expressão, o objeto recebe o valor de "a", ou seja, a constante "Rolling ", concatenada a constante "Stones", que é, na verdade, um novo objeto. Desta forma o objeto string não é mutável!

Na sentença a seguir, ocorre uma opreração completamente diferente.

   1 lista = [1,2]
   2 lista.append( 2 )
   3 lista[ 0 ] = 123
   4 
   5 texto = "teste"
   6 texto[ 0 ] = "T" # Falha! Pois txt é imutável.
   7 
   8 tupla = ( 1, 2 )
   9 tupla[ 1 ] = 3 # Falha! Pois tupla é imutável.

Neste caso o objeto lista é mudado com lista.append(2), o qual irá adicionar um elemento ao fim da lista e posteriormente o valor do primeiro elemento é mudado para 123.

Já em texto e tupla isto não é possível, pois eles são imutáveis. Não se pode alterar o valor dos mesmos. Existem várias razões para a existência destes tipos, algumas delas: alocação de espaço: o python pode alocar (o malloc() em C) espaço exato para o tamanho da str e da tuple e também acessar com O(1) os elementos, pois não precisa de uma estrutura de lista ligada para possibilitar o crescimento desta. chaves: algumas estruturas de dados assumem que as chaves não mudam, como no caso de um dict e ter tipos imutáveis é essencial para o preenchimento destes pré-requisitos.

Um cuidado que tem-se que tomar com tipos imutáveis é a concatenação dos mesmos, como no caso de str. Toda vez que somamos duas str o Python faz uma alocação de len(str1) + len(str2), isto pode trazer impacto de performance e consumo de memória significativos! Programas que necessitem montar uma string aos poucos devem usar a estrutura de lista e depois usar o "join" para juntá-las, é muito mais eficiente:

   1 def monta_pagina():
   2    retorno = []
   3    retorno.append( pega_cabecalho() )
   4    # ...
   5    # funcoes diversas que usam retorno.append()
   6    # ...
   7    retorno.append( pega_rodape() )
   8    return "".join( retorno )   

Para criar objetos imutáveis você deve fazer uma classe que proíba atribuições:

   1 class Imutavel( object ):
   2    """Define um objeto imutável."""
   3    def __setattr__( self, attr, val ):
   4       raise AttributeError( "'Imutavel' object has no attribute '%s'"% attr )

Se quiser um objeto que não seja possível usar hash e portanto não seja possível usá-lo como chave de um dicionário, sobrescreva o __hash__() do mesmo:

   1 class Mutavel( object ):
   2    """Define um objeto mutável e que não deve ser utilizado como chave de 'mappings'."""
   3    def __hash__( self ):
   4       raise TypeError( "Mutavel objects are unhashable" )

Funções

Existem dois tipos de funções:

Funções definidas pelo usuário: é um objeto criado por uma definição de função. Observe o código a seguir: A primeira função retorna o fatorial do número dado, a segunda retorna a potência de base x e expoente n, observe os exemplos de utilização das funções.

   1 def fatorial (n):
   2     'retorna o fatorial de n'
   3     if n == 0:
   4         return 1
   5     return n*fat (n-1)
   6 
   7 def pot (x, n=2)
   8     'retorna a potencia com base x e expoente n \
   9     se n for ocultado eh assumido n = 2'
  10     return x**n
  11 
  12 print fat (10)
  13 print fat (5000L) # inteiro longo
  14 print pot (3,5)
  15 print pot (8)    # equivale a 8**2

Funções pré-definidas (Built-in): são funções que não são obtidas de uma operação import ou declaração, ou seja, já estavam presentes quando da inicialização do interpretador. O usuário pode escrever uma função built-in, escrevendo-a na linguagem C e incorporando-a ao próprio interpretador. A linguagem Python oferece vários recursos de expansão neste sentido.

Classes

Um objeto classe é definido por uma definição de classe. Quando uma classe é atribuída a outro objeto, este objeto passa a ser uma instância desta classe. Uma classe é composta por atributos que representam o seu estado interno ou funções que fazem operações sobre estes estado (métodos). Vejamos o exemplo abaixo:

   1 from Tkinter import *
   2 
   3 class App:
   4     def __init__ (self):
   5         self.i = 0
   6         self.root = Tk()
   7 
   8         self.button = Button (self.root, text = 'Hello', command = self.say_hi)
   9         self.button.pack (side=LEFT)
  10 
  11         self.root.mainloop()
  12 
  13     def say_hi (self):
  14         print 'Ola todos!'
  15         self.i = self.i + 1
  16 
  17 a = App()

O exemplo acima mostra a classe App. Esta classe cria uma janela com um botão escrito 'Hello', que, quando pressionado, imprime a frase 'Ola todos!'. Esta classe é composta dos métodos __init__ e say_hi, e dos objetos root (a janela), button (o botão) e i (literal que conta o número de vezes que o botão foi pressionado). Os métodos definidos pelo usuário dentro do objeto são as implementações das operações válidas nos objetos desta classe. Estes objetos, por sua vez, representam o estado atual da classe.

Na última linha, observamos a atribuição de App() a a. Neste ponto a tornou-se uma instância de App. Uma instância de uma Classe é obtida pela chamada desta classe como uma função. Quando isto ocorre, automaticamente é chamado o método __init__. Isto é um padrão da linguagem.

Diferença entre classe e instância

Uma classe é um objeto, porém é um objeto dito não amarrado (unbound), ou seja, não foi mapeado em memória um endereço para os métodos e os demais objetos internos a esta classe. Por outro lado, uma instância de uma classe é uma amarração (binding) de uma classe, ou seja, quando se instancia uma classe, atribuindo-a a um objeto, está se alocando um espaço da memória para os elementos desta classe. No exemplo acima, são válidos as seguintes expressões no nível da instância a.

   1 a.say_hi ()
   2 
   3 print a.i #imprime o número de vezes que o botao foi pressionado
   4 
   5 a.root.title ('Exemplo 1') # muda o título da janela

Isto não significa que os métodos de uma classe não sejam objetos executáveis, por exemplo, a expressão a.say_hi() é o mesmo do que App.say_hi(a). Aliás, a primeira expressão geralmente é conhecida como açucar sintático da segunda, o que quer dizer que é uma forma resumida e mais simples de fazê-la.

Características internas das classes

Em Python, as classes são construídas através de uma estrutura de dicionário no qual uma strings com os identificadores dos objetos são as chaves e os objetos em sí (métodos e atributos em geral) são os valores indexados por estas chaves.

O exemplo acima é representado pelo seguinte dicionário (os números são aleatórios e seria substituídos pelo endereço de memória dos objetos referidos pelos campos chave):

   1 App = {'__init__':63233, 'say_hi':67542, 'root':63462, 'button':68382, 'i':68112}

Nomes Especiais de Métodos

Aqui é descrito como expressões definidas pelo usuário podem customizar o comportamento de um determinado objeto. Vamos estudar alguns nomes padrões de métodos.

__init__ este método é invocado quando uma instância da classe é criada. Os argumetos da função são aqueles passados ao construtor de classe. Quando uma classe é derivada de outra, esta nova classe deve chamar explicitamente a função __init__ da classe herdada. Veja o exemplo:

   1 class MyClass (OldClass):
   2     def __init__ (self, data1, data2):
   3         self.data1 = data1
   4         self.data2 = data2
   5         OldClass.__init__ (self, 0, 0)
   6 
   7     def Op1 (self):
   8         pass
   9 
  10     def Op2 (self):
  11         pass
  12 
  13 X = MyClass (x, y)

__del__ este método é invocado quando a instância está para ser destruída, por exemplo:

   1 class arquivo:
   2     def __init__ (self, nomearq):
   3         self.arq = open (nomearq, 'rw')
   4 
   5         ..........
   6 
   7     def __del__ (self):
   8         self.arq.close()

__repr__ retorna a representação em Python do objeto, ou seja, retorna o código-fonte do objeto. Serve para recriar objetos com o mesmo valor.

__call__ é invocada quando a instância é "invocada" como uma função. Quando este argumento existe, x(arg1, arg2,..) é uma forma compacta para x.__call__(arg1, arg2, ...). Como, em Python, a abstração genérica de dados é o objeto, ou seja, a classe instanciada, todas as funções defindas são também objetos que contém este método que é invocado quando a própria função é invocada. Observe o exemplo:

   1 def riemann (expr, a, b):
   2     x = a
   3     somat = 0
   4     norma = 0.0001
   5     while x <= b:
   6         somat = somat + eval (expr)* norma
   7         x = x + norma
   8     return somat

É estritamente a mesma coisa do que:

   1 class riemann:
   2     def __call__ (self, expr, a, b):
   3         x = a
   4         somat = 0
   5         norma = 0.0001
   6         while x <= b:
   7             somat = somat + eval (expr)* norma
   8             x = x + norma
   9         return somat

Outros objetos

Módulos: são arquivos fonte de programa em Python que são importados por uma chamada de import. Também são tratadas como as classes e representadas por um dicionário. Arquivos: um objeto arquivo representa um arquivo aberto e é criado pelas funções open, popen, fdopen, ou pelo método makefile do objeto socket. Código objeto: um dos objetos mais impressionantes do Python é o seu código objeto. Como já foi dito o Python é uma linguagem interpretada mas sua execução não é feita linha-a-linha. Na realidade o interpretador gera um bytecode para as senteças recebidas. Este bytecode é a chave da portabilidade e do polimorfismo pois neste são válidas operações do tipo recuperar os nomes dos objetos que o compoem ou mesmo o código fonte e que o gerou. Isto é chamado introspecção, a capacidade de um objeto de descrever-se. Frame: é um dicionário que conta todos os tipos daquele nível de execução. Neste dicionário estão descritos todos os objetos acessíveis naquele ponto do programa. Veja o exemplo:

   1 x = 1
   2 y = 2
   3 nome = 'Mariazinha'
   4 
   5 def troca (x, y):
   6    'funcao que nao serve pra nada'
   7    aux = x
   8    x = y
   9    y = aux
  10 
  11 # frame global
  12 #{'x'=1,'y'=2, 'nome'='Mariazinha, 'troca' = funcao}
  13 
  14 # frame na funcao troca
  15 #{'x'=??, 'y'=??, aux=??, 'nome'='Mariazinha'}

Traceback: um objeto traceback é uma pilha que é criada quando ocorreu uma exceção. Quando acontece uma exceção, o interpretador procura por um bloco de tratamento de exceções para isto ele começa a desempilhara stack, ou a pilha de execução. Á cada nível que é desempilhado o traceback recebe um elemento. Desta forma, pode-se saber em quantos níveis abaixo do tratador é que houve a exceção.

Sobre este texto

Este texto foi escrito no LyX, um editor WYSIWYG para o formato !LaTeX, rodando no sistema operacional Linux. Foi Também utilizado o programa Ghostview, para visualizar os arquivos Postscript gerados pelo !LaTeX.

LyX é marca registrada de Matthias Ettrich, LaTeX é marca registrada de Leslie Lamport, Linux é marca registrada de Linus Torvalds e Ghostview é marca registrada de Alladin Software. Todos estes programas são distribuídos livremente nos termos da GNU Public License.

A linguagem Python é marca registrada do Stichting Matematisch Centrum e também é distribuída livremente.

Referências

  1. Van Rossum, G., Python Tutorial, Corporation for National Research Initiatives (CNRI), Reston, VA, USA, 1997.
  2. Van Rossum, G., Python Library Reference, Corporation for National Research Initiatives (CNRI), Reston, VA, USA, 1997.
  3. Lutz, M., Programming Python, O'Reily & Associates, Inc., New York, NY, 1996.

  4. Laird, C., Soraiz, K., Getting Started with Python, SunWorld On-Line, outubro/1997, http://www.sun.com/sunworldonline.

  5. Van Rossum, G., Glue it all together with Python, OMG-DARPA-MMC Workshop on Compositional Software Architecture, Monterrey, CA, 1998.
  6. Lundhl, F., An introduction to Tkinter - take two, Pythonware, http://www.pythonware.com, 1998.

  7. Waters, A., Van Rossum, G., et. al., Internet Programming with Python, M&T Books, MIS:Press, Inc., New York, NY, 1996.


Corrigi alguns de erros de português :-) -- PedroDeMedeiros

Obs: a descrição de mapeamentos carece de correção. Listas não podem ser considerados mapeamentos da forma como foi escrita a definição, pois listas não usam chaves arbitrárias e são ordenados (mapeamentos não são ordenados e usam chaves arbitrárias). Aconselho tirar a comparação de listas com mapeamentos do texto -- PedroDeMedeiros

A função de riemann e a classe de riemann não são estritamente iguais. Enquanto que o método __call__ é atribuido ao objeto instanciado (transformando este em objeto executável), a função de riemann já é executável sem precisar de qualquer instanciação. Então, o objeto precisa antes ser instanciado -- PedroDeMedeiros

Eu troquei "endentação" por "indentação", pq podia jurar que a primeira forma era incorreta. Mas depois dei uma olhada no google e vi que as duas formas são usadas, perdão. De qualquer forma, eu dei uma checada no Aurélio e "indentar" parece mais apropriado: [Do ingl. (to) indent < fr. endenter (v. endentar).] V. t. d. Espacejar linha, parágrafo, etc. da margem esquerda para a direita em (folha a ser impressa, dactilografada, etc.). -- RodrigoVieira


MarceloPereiraNunes

  1. Van Rossum, G., Python Reference Manual, Corporation for National Research Initiatives (CNRI), Reston, VA, USA, 1997. (1)

  2. Nota do 'editor': à partir da versão 2.3 a linguagem Python aceita caracteres Unicode (2)

AspectosFormaisDaLinguagemPython (editada pela última vez em 2008-09-26 14:07:25 por localhost)