Python3 02 – “Variáveis”

Este artigo é a parte 2 de 10 na série Python3

Neste texto, vou dissertar um pouco mais a fundo sobre variáveis em Python, explorando mais sobre a tipagem dinâmica, identificadores e operadores, além de aproveitar para explorar algumas particularidades da linguagem.

1. Variáveis no Python

Não há declarações de variáveis no Python, podendo, em qualquer momento, uma variável ser apontada para uma string, um inteiro ou qualquer outro tipo de dado. Para acompanhar a discussão, é aconselhável iniciar o interpretador e acompanhar os exemplos:

[xterm color=’true’ py=’true’]
$ python3
Python 3.6.1 (default, Jul 28 2017, 17:35:34)
[GCC 7.1.0] on linux
Type “help”, “copyright”, “credits” or “license” for more information.
>>> idade = 29
>>> pot = 16069380442589902755419620923411626025222029
>>> nome = ‘Sara’
>>> sobrenome = “Bertoluci”
>>> salário = 3600.52
>>> Salário = “€3.600,52”
[/xterm]

As duas primeiras variáveis declaradas são inteiros, sendo pot o valor de 2^200. O Python não possui um barreira para o tamanho de um inteiro, sendo este limitado apenas pela memória da máquina. Na sequência, são criadas duas strings, nome e sobrenome. Uma string pode ser delimitada tanto por aspas simples ou dupla, a gosto do programador. Uma novidade no Python 3 é o total suporte a Unicode, suportando qualquer caractere tanto no nome das grandezas como no conteúdo das variáveis sem qualquer restrição, como ocorre na maioria das linguagens que se limitam ao um subconjunto dos caracteres da tabela ASCII.

Os nomes das variáveis também são case sensitive (sensível à caixa das letras), de forma que salário é uma variável tipo float (ponto flutuante), enquanto Salário é uma string com o valor do salário em euros.

[xterm color=’true’ py=’true’]
>>> salário
3600.52
>>> type(salário)
<class ‘float’>
>>> Salário
‘€3.600,52’
>>> type(Salário)
<class ‘str’>
[/xterm]

A função type() retorna a tipagem da variável.

1.1. Identificadores

Nomear variáveis em Python segue algumas poucas regras, não muito diferentes do que se faz em outras linguagens:

  • 1º) o primeiro caractere deve ser qualquer letra unicode (inclusive os caracteres acentuados, legras gregas, ou qualquer outro símbolo que pode ser considerado uma letra em alguma linguagem) e também o underscore (“_”);
  • 2º) nenhum identificador de variável poder ser igual a uma das 32 palavras reservadas da linguagem, apresentadas na Tab 01.

[TABLE=58]

O Python ainda possui um conjunto de métodos/atributos padrões que, embora possam ser utilizados como identificadores de variáveis, não é uma prática recomendada. Os métodos/atributos padrões do Python são listados no __builtins__ da biblioteca padrão, que pode ser listado pelo comando dir():

[xterm color=’true’ py=’true’]
>>> dir()
[‘__annotations__’, ‘__builtins__’, ‘__doc__’, ‘__loader__’, ‘__name__’,
‘__package__’, ‘__spec__’, ‘name’, ‘str’]
>>> dir(__builtins__)
[‘ArithmeticError’, ‘AssertionError’, ‘AttributeError’, ‘BaseException’,
‘BlockingIOError’, ‘BrokenPipeError’, ‘BufferError’, ‘BytesWarning’,
‘ChildProcessError’, ‘ConnectionAbortedError’, ‘ConnectionError’,
‘ConnectionRefusedError’, ‘ConnectionResetError’, ‘DeprecationWarning’,
‘EOFError’, ‘Ellipsis’, ‘EnvironmentError’…
‘max’, ‘memoryview’, ‘min’, ‘next’, ‘object’, ‘oct’, ‘open’, ‘ord’, ‘pow’,
‘print’, ‘property’, ‘quit’, ‘range’, ‘repr’, ‘reversed’, ‘round’, ‘set’,
‘setattr’, ‘slice’, ‘sorted’, ‘staticmethod’, ‘str’, ‘sum’, ‘super’,
‘tuple’, ‘type’, ‘vars’, ‘zip’]
[/xterm]

Observe que alguns deles são funções para conversão de tipo como str, float, int, complex, list, dict, tupla e list, bem como outras funções que serão apresentadas futuramente. Tente os comandos a seguir no modo interativo do Python:

[xterm color=’true’ py=’true’]
>>> str(45)
’45’
>>> str = ‘Alberto’
>>> float(‘12.465’)
12.465
>>> float = ‘Maria’
>>> str, float
(‘Alberto’, ‘Maria’)
[/xterm]

Como pode observar, str e float funcionam sem problemas com identificadores de variáveis, mas esta é uma prática fortemente desaconselhada, pois torna o código confuso, visto que str e float são funções de conversão de tipo muito bem conhecidas e empregadas em scripts Python. A adição de outras bibliotecas ao programa Python irá aumentar consideravelmente esta lista, portanto alguma atenção a estas adições é bem aconselhável.

Esta possibilidade de redefinir funções, objetos, operadores, métodos e atributos no Python vem em acordo com a grande flexibilidade da linguagem em criar sobrecarga nestes, mas isto deve ser feito com alguns critérios para não gerar códigos e funcionalidades pouco intuitivas. Cabe ao programador empregar sabiamente as superposições que achar conveniente para evitar ambiguidades visuais na leitura do código.

No momento, não há a necessidade de se preocupar com isto e, à medida que for usando o Python, se torna natural evitar a sobrecarga dos identificadores mais comuns. O Python também é bastante flexível para permitir importar apenas as partes interessadas de cada módulo, sem sobrecarregar seu ambiente com funções desnecessárias. Seguem alguns exemplos de nomes de variáveis válidos e alguns inválidos:

[xterm color=’true’ py=’true’]
>>> from math import pi
>>> π = pi
>>> raio = 12
>>> Área = π*raio**2
>>> _ = ‘roberta’
>>> seu_nome = ‘alberto’
>>> 2be = 2
File “<stdin>”, line 1
2be = 2
^
SyntaxError: invalid syntax
>>> meu-nome = ‘carlos’
File “<stdin>”, line 1
SyntaxError: can’t assign to operator
>>> c++ = ‘linguagem’
File “<stdin>”, line 1
c++ = ‘linguagem’
^
SyntaxError: invalid syntax
[/xterm]

O primeiro comando, from math import pi, importa do módulo math (módulo padrão para as funções matemáticas) apenas o pi, cujo valor é apontado pelo identificador π na linha seguinte, um unicode perfeitamente válido como identificador no Python.

Operadores matemáticos, obviamente, também não são permitidos nos identificadores de variáveis.

1.2. Imutável e Referência

Tipos numéricos e strings no Python são imutáveis e, por isto, não podem ter seu valor alterado uma vez definido. Ao invés de alterar o valor da variável numérica, o Python instancia um novo objeto e aponta a variável para o objeto criado.

O “instanciação” de um objeto é uma consequência de que todas estruturas de dados no Python são objetos criados a partir de alguma classe, mas isto será abordado em textos futuros. No momento, se atenha ao ‘imutável’ comentado anteriormente. Observe o exemplo a seguir:

[xterm color=’true’ py=’true’]
>>> a = 3
>>> b = a
>>> a, b
(3, 3)
>>> a = 8
>>> a, b
(8, 3)
[/xterm]

Agora, veja como o Python processa estes comandos:

  1. primeiro é instanciado um objeto inteiro com conteúdo 3 e, em seguida, a referência a este objeto é passada a variável “a“, Figura 02-a;
  2. b” recebe uma cópia da referência de “a“, Figura 02-b;
  3. imprime os conteúdos apontados nas referências de “a” e “b“;
  4. em seguida, é instanciado um novo objeto inteiro com o conteúdo 8 e sua referência é passada para “a“, Figura 02-c;
  5. por fim, imprime os conteúdos apontados pelas referências em “a” e “b“.
Referenciando objetos no Python

Após “a” ter seu valor referido ao novo objeto “8”, uma consulta a “b” continua retornando o inteiro 3, pois a variável “b” continha a referência ao objeto inteiro 3. Para o programador, tudo se passa como em outras linguagens de programação tradicionais, como se a variável b tivesse seu valor sobrescrito pelo conteúdo de a, na linha b = a.

As aspas duplas empregadas nas variáveis “a” e “b” foram adicionadas com o intuito de salientar que, no Python, estas “variáveis” são, na verdade, apontamentos para objetos, no caso os inteiros 3 e 8, como fazem os ponteiros na linguagem de programação C e não variáveis como na forma tradicional.

Estas escolhas do Python possuem como objetivos a performance do interpretador, bem como atender a algumas especificidades da linguagem. De forma transparente, o interpretador irá liberar a memória sempre que um objeto se tornar órfão e instanciar novos objetos sempre que for necessário.

Uma aplicação interessante e simples desta peculiaridade do Python é o swap de valores entre duas “variáveis”. Veja as linhas a seguir:

[xterm color=’true’ py=’true’]
>>> a = 12
>>> b = ‘alvo’
>>> a, b = b, a
>>> a, b
(‘alvo’, 12)
[/xterm]

Como a e b são apenas apontadores para os conteúdos inteiro 12 e string 'alvo', a linha a, b = b, a apenas atribui o apontador de a para b e o de b para a. Portanto ocorre um swap de endereços nos apontadores e não de conteúdos.

1.3. Variáveis Complexas, Orientação a Objetos e Documentação

Um breve momento para dar um pouco de atenção ao suporte de variáveis complexas no Python. Desde a primeira versão, o Python traz nativamente o suporte a variáveis complexas. Sua definição é bem simples: basta adicionar uma letra j a um número que ele é tipado como complexo.

[xterm color=’true’ py=’true’]
>>> z = 2 + 5j
>>> type(z)
<class ‘complex’>
[/xterm]

Esta também é uma oportunidade para explorar um pouco a classe complex, que possui os dois atributos real e imag, e o método conjugate(), para gerar seu complexo conjugado. Veja-os em ação:

[xterm color=’true’ py=’true’]
>>> z.real
2.0
>>> z.imag
5.0
>>> z.conjugate()
(2-5j)
>>> zc = z.conjugate()
>>> z*zc
(29+0j)
[/xterm]

No Python, todas as variáveis são instâncias de alguma classe, estando a orientação a objetos por toda parte. Apesar disto, é perfeitamente possível programar em Python sem tocar em orientação a objetos.

Em alguns momentos, será necessário acessar algum atributo ou método da classe para utilizar alguns características dos objetos, mas nada que uma breve consulta do comando dir() e a documentação padrão não resolva.

[xterm color=’true’ py=’true’]
>>> dir(z)
[‘__abs__’, ‘__add__’, ‘__bool__’, ‘__class__’, ‘__delattr__’, ‘__dir__’,
‘__divmod__’, ‘__doc__’, ‘__eq__’, ‘__float__’, ‘__floordiv__’, ‘__format__’,
‘__ge__’, ‘__getattribute__’, ‘__getnewargs__’, ‘__gt__’, ‘__hash__’,
‘__init__’, ‘__init_subclass__’, ‘__int__’, ‘__le__’, ‘__lt__’, ‘__mod__’,
‘__mul__’, ‘__ne__’, ‘__neg__’, ‘__new__’, ‘__pos__’, ‘__pow__’, ‘__radd__’,
‘__rdivmod__’, ‘__reduce__’, ‘__reduce_ex__’, ‘__repr__’, ‘__rfloordiv__’,
‘__rmod__’, ‘__rmul__’, ‘__rpow__’, ‘__rsub__’, ‘__rtruediv__’, ‘__setattr__’,
‘__sizeof__’, ‘__str__’, ‘__sub__’, ‘__subclasshook__’, ‘__truediv__’,
‘conjugate’, ‘imag’, ‘real’]
[/xterm]

Neste momento, atenha-se apenas aos atributos/métodos que não iniciam com “__“. No caso do tipo complexo, são apenas os três últimos: conjugate, imag e real.

Para saber como utilizá-los, basta consultar o atributo __doc__ de cada atributo/método com um comando print(). Veja a seguir:

[xterm color=’true’ py=’true’]
>>> print(z.conjugate.__doc__)
complex.conjugate() -> complex

Return the complex conjugate of its argument. (3-4j).conjugate() == 3+4j.
[/xterm]

Mas isto é útil somente se a propriedade consultada for um método da classe. Caso seja um atributo, ele deve retornar algo como:

[xterm color=’true’ py=’true’]
>>> print(z.real.__doc__)
float(x) -> floating point number

Convert a string or number to a floating point number, if possible.
[/xterm]

O que aconteceu, na verdade, é que você acessou o .__doc__ do atributo, que no caso é um float, recebendo o conteúdo da documentação do número de ponto flutuante.

[xterm color=’true’ py=’true’]
>>> type(z.real)
<class ‘float’>
>>> type(z.imag)
<class ‘float’>
[/xterm]

Em geral, os atributos em Python costumam ficar mascarados atrás de métodos, não expostos como no tipo complexo.

Todos os objetos e funções do Python possuem alguma documentação embutida dentro de um atributo tipo string, nomeado __doc__. Esta documentação pode ser acessada diretamente por um comando print(função/variável.__doc__)

[xterm color=’true’ py=’true’]
>>> max(2, 8, 5, 1)
8
>>> print(max.__doc__)
max(iterable, *[, default=obj, key=func]) -> value
max(arg1, arg2, *args, *[, key=func]) -> value

With a single iterable argument, return its biggest item. The
default keyword-only argument specifies an object to return if
the provided iterable is empty.
With two or more arguments, return the largest argument.
[/xterm]

Outra forma de acessar esta documentação é pelo comando help(). Um comando help(z.conjugate) no interpretador Python deve gerar uma tela semelhante a que se segue:

[xterm color=’true’ py=’true’]
Help on built-in function conjugate:

conjugate(…) method of builtins.complex instance
complex.conjugate() -> complex

Return the complex conjugate of its argument. (3-4j).conjugate() == 3+4j.

— MOST: *stdin* (1,1) 100%
Press `Q’ to quit, `H’ for help, and SPACE to scroll.
[/xterm]

O comando help() apenas consulta a string __doc__ do seu argumento, neste caso o z.conjugate, e a transfere para um paginador para ser apresentado. Observe que o conteúdo é o mesmo do print(z.conjugate.__doc__) realizado anteriormente.

2. Operadores

Como em outras linguagens, o Python também possui um conjunto de operadores matemáticos e lógicos. Não tem muita novidade aqui, mas tem algumas peculiaridades pythônicas que podem ser levantadas.

2.1. Operadores Matemáticos

Os operadores matemáticos no Python são bem semelhantes aos usados em outras linguagens. A tabela a seguir apresenta os operadores matemáticos.

[TABLE=59]

A precedência das operações seguem as mesmas das operações matemáticas, com a adição de parênteses para forçar a ordem desejada. Seguem alguns exemplos:

[xterm color=’true’ py=’true’]
>>> a = 13
>>> b = 7
>>> a + b*2
27
>>> a/b
1.8571428571428572
>>> a//b
1
>>> a%b
6
>>> b**2
49
>>> (a + b)*(a % b)
120
[/xterm]

O Python aceita os operadores compostos apresentados na Tab 03, mas não possui os tradicionais operadores ++ e -- da linguagem C.

[TABLE=60]

Seguem alguns exemplos:

[xterm color=’true’ py=’true’]
>>> a += 2
>>> b -= 2
>>> a /= 5
>>> a, b
(3.0, 5)
[/xterm]

O Python ainda suporta outros operadores compostos que serão apresentados em outros contextos adiante.

2.2. Comparadores

Python também possui um conjunto completo de comparadores lógicos: < (menor que); > (maior que); >= (maior ou igual a); <= (menor ou igual a); == (igual); e != (diferente de).

[xterm color=’true’ py=’true’]
>>> a, b = 13, 7
>>> a >= b
True
>>> a < b
False
>>> a == b
False
>>> a != b
True
[/xterm]

A primeira linha de comando carrega a com 13 e b com 7, resgatando os valores anteriores. O Python também suporta comparações em cadeia.

[xterm color=’true’ py=’true’]
>>> 0 < b < a
True
[/xterm]

2.3. Operadores Lógicos

Os comparadores ainda podem ser associados com os três operadores lógicos: and, or e not.

[xterm color=’true’ py=’true’]
>>> (b > 0) and (b < a)
True
>>> (b > 0) or (b > a)
True
>>> not(0 < b < a)
False
[/xterm]

2.4. Operadores in e is

O operador in é o operador matemático “está contido”, o qual retorna verdadeiro se um elemento está contido em uma sequência.

[xterm color=’true’ py=’true’]
>>> c = (1, ‘algas’, 12j, 7, 18.2)
>>> c
(1, ‘algas’, 12j, 7, 18.2)
>>> a in c
False
>>> b in c
True
>>> ‘a’ in c
False
>>> a, b
(13, 7)
>>> d = ‘Alberto’
>>> ‘r’ in d
True
>>> ‘a’ in d
False
[/xterm]

Neste caso, c é uma tupla, uma estrutura de dados que será apresentada em um texto futuro. Por agora, pense nele apenas como a sequência de elementos (1, 'algas', 12j, 7, 18.2). A comparação retorna verdadeira sempre que o elemento comparado for um dos elementos da sequência. Observe que a terceira comparação, 'a' in c, retorna falso. Mesmo que o caractere ‘a’ esteja contido no segundo elemento da sequência c, ela não é um elemento de c. Uma string também é uma sequência e, por isto, pode ser usado com o comparador in.

Já o operador is retorna verdadeiro se os dois identificadores apontarem para o mesmo objeto.

[xterm color=’true’ py=’true’]
>>> f = ‘albert segundo’
>>> g = ‘albert segundo’
>>> h = f
>>> f is g
False
>>> h is f
True
>>> f == g
True
[/xterm]

No exemplo, embora f e g possuam o mesmo conteúdo, certificado pela comparação f == g, eles não são o mesmo objeto. Por isto, o f is g retorna falso. Já o identificador h é uma cópia do apontador do identificador f, portanto h is f retorna verdadeiro, já que apontam para o mesmo objeto.

2.5. Operadores Binários

Os operadores binários são os tradicionais operadores empregados para executar operações sobre bits, tal como os operadores em circuitos lógicos.

[TABLE=61]

Se alguns dos inteiros forem empregados com os operadores binários, estes serão considerados em complemento de 2. Seguem alguns exemplos:

[xterm color=’true’ py=’true’]
>>> a = 1
>>> b = 2
>>> a | b
3
>>> c = a << 2
>>> d = b << 2
>>> a, b, c, d
(1, 2, 4, 8)
>>> bin(a), bin(b), bin(c), bin(d)
(‘0b1’, ‘0b10’, ‘0b100’, ‘0b1000’)
[/xterm]

3. Considerações

Embora o texto tenha se estendido muito além do que esperava, esta foi apenas uma arranhada nas variáveis básicas e operadores. Nos textos seguintes, devo adentrar um pouco mais em orientação a objetos, explorando mais as funções embutidas em cada “variável” do Python. “Variáveis” estas que não são mais que apontadores para objetos instanciados das classes fundamentais.

Para melhorar o aproveitamento do conteúdo do Python, reorganizei meus textos e adicionei o próximo, onde discuto sobre as estruturas de controle de fluxo de programa, os tradicionais if, for e while, além da construção de funções. Posteriormente retorno a discutir variáveis, focando a atenção às variáveis do tipo sequência, bastante importantes e versáteis quando bem empregadas.

Deixe um comentário

Esse site utiliza o Akismet para reduzir spam. Aprenda como seus dados de comentários são processados.