Python: Classes, métodos especiais


Métodos especiais, ou métodos mágicos, em Python são métodos predefinidos em todos os objetos, com invocação automática sob circunstâncias especiais. Eles normalmente não são chamados diretamente pelo usuário mas podem ser overloaded (sobrescritos e alterados). Seus nomes começam e terminam com sublinhados duplos chamados de dunder (uma expressão derivada de double underscore). As operações abaixo são exemplos de métodos mágicos e como acioná-los, com o operador + e a função len().

» x, y = 34, 45
» x+y
↳ 79
» x.__add__(y)
↳ 79
» 
» l = [4,67,78]
» len(l)
↳ 3
» l.__len__()
↳ 3

Vemos que somar dois números usando o operador + aciona o método __add __ e calcular o comprimento de um objeto usando a função len() equivale a usar seu método __len__().

Uma das principais vantagens de usar métodos mágicos é a possibilidade de elaborar classes com comportamentos similares ou iguais aos de tipos internos.

Atributos especiais das classes

Vimos na seção anterior sobre Classes no Python que a função dir() exibe todos os atributos de uma classe. Muitos deles são built-in, herdados da classe object que é a base de todas as classes, portanto de todos os objetos. Em outras palavras object é uma superclasse para todos os demais objetos.

Podemos listar esses atributos criando uma classe T sem qualquer atributo e examinando o resultado de print(dir(T)).

» class T:
»     pass

» print(dir(T))
↳ ['__class__', '__delattr__', '__dict__', '__dir__', '__doc__', '__eq__', '__format__',
↳  '__ge__', '__getattribute__', '__gt__', '__hash__', '__init__', '__init_subclass__',
↳  '__le__', '__lt__', '__module__', '__ne__', '__new__', '__reduce__', '__reduce_ex__',
↳  '__repr__', '__setattr__', '__sizeof__', '__str__', '__subclasshook__', '__weakref__']

Já vimos e usamos os métodos __init__(), para inicializar objetos, e __str__(), para retornar uma representação de string do objeto, acessada por dir(objeto). Também fizemos o overloading de __eq__() e __add__().

Também usamos a possibilidade de sobrescrever os métodos __len__, que ativa a função len(), __str__, associado às chamadas de print() e __eq__ usado em comparações com ==. Para lembrar esse processo de overload observe os métodos na classe abaixo.

» class Comprimento:
»     def __init__(self, fim = 0):
»         self.minhaLista = list(range(fim))
»     def __len__(self):
»         return len(self.minhaLista)
»     def __eq__(self, outro):
»         return self.minhaLista == outro.minhaLista
»     def __str__(self):
»         return '%s \nlen = %d' % (str(self.minhaLista), len(self.minhaLista))

» comp1 = Comprimento(fim=9)
» comp2 = Comprimento(fim=9)

» print(comp1)
↳ [0, 1, 2, 3, 4, 5, 6, 7, 8] 

» len = 9
» comp1==comp2
↳ True

O método len() se refere à contagem de elementos discretos e só pode ser definido para retornar um número inteiro.

Em outro exemplo definimos na classe Ponto os operadores de comparação __gt__ e __ne__, respectivamente > e != da seguinte forma: consideramos, para esse nosso caso, que um ponto é “maior” que outro se estiver mais afastado da origem de coordenadas, o ponto (0, 0). Para isso definimos o método distancia() que calcula essa distância. O operador __ne__ retorna True se uma ou ambas coordenadas dos dois pontos testados forem diferentes. Para usar a função sqrt(), (raiz quadrada) temos que importar o módulo math.

» from math import sqrt
» class Ponto:
»     def __init__(self, x, y):
»         self.x, self.y = x, y
» 
»     def distancia(self):
»         return sqrt(self.x**2 + self.y**2)
»     
»     def __gt__(self, other):
»         return self.distancia() > other.distancia()
»     
»     def __ne__(self, other):
»         x, y, w, z = self.x, self.y, other.x, other.y
»         return x != w or y != z
»     
»     def __str__(self):
»         return 'Ponto com coordenadas (%d, %d)' % (self.x, self.y)

» p1 = Ponto(4,5)
» p2 = Ponto(1,2)

» p1 != p2
↳ True

» p1 > p2
↳ True

De forma análoga podemos fazer o overload e utilizar os aperadores __eq__, ==, __ge__, >=, __gt__, >, __le__, <=, __lt__, < e __ne__, !=.

Todas as comparações se baseiam no método __cmp __(self, other) que deve retornar um inteiro negativo se self < other, zero se self == other e um inteiro positivo se self > other.

Geralmente é melhor definir cada uma das comparações que serão utilizadas. Mesmo assim a definição do método __cmp__ pode ser uma boa maneira de economizar repetição e melhorar a clareza quando você precisa que todas as comparações sejam implementadas com critérios semelhantes.

Método __init__()

Já vimos na seção anterior o funcionamento do método __init__, e extendemos aqui a descrição de sua funcionalidade. Sabemos que podemos inicializar um objeto sem qualquer referência às propriedades de que ele necessita e inserir mais tarde, dinamicamente, essas propriedades.

» class Area:
»     ''' Área de um retângulo '''
» 
»     def area(self):
»         return self.altura * self.largura
»
» a = Area()
» a.altura = 25
» a.largura = 75
» a.area()
↳ 1875

Uma classe definida dessa forma não deixa claro quais são as propriedades que ele deve usar. Considerando que o método __init__() é acionado internamente, podemos tirar vantagem desse método fazendo seu overload e inicializando as propriedades explicitamente.

» class Area:
»     def __init__(self, altura, largura):
»         self.altura = altura
»         self.largura = largura
» 
»     def area(self):
»         return self.altura * self.largura
» 
» b = Area(123, 90)
» b.area()
↳ 11070

A segunda definição é considerada um melhor design uma vez que torna mais clara a leitura do código e seu uso durante a construção de um aplicativo. A própria definição da classe informa quais são os parâmetros usados pelos objetos dela derivados.

Para o próximo exemplo suponha um jogo do tipo RPG onde os jogadores são criados com uma determinada quantidade de energia e inteligência e que esses valores são incrementados ou decrementados de acordo com as escolhas feitas pelo jogador. A partir desses valores se calcula vida e poder, significando quanto tempo a personagem tem de vida e quanto poder de destruição ela possui em seus golpes.

Para representar as dois tipos possíveis de personagens no jogo definimos a superclasse Personagem com duas variáveis de classe (energia e inteligência) e dois atributos calculados: vida e poder. Duas subclasses Cientista e Estudante herdam da primeira, todas as variáveis e métodos, inclusive __init__(), mas sobreescrevem os métodos _estado(), usado na inicialização, e __str()__.

» class Personagem:
»     def __init__(self, energia, inteligencia):
»         self.energia = energia
»         self.inteligencia = inteligencia
»         self.vida, self.poder = self._estado()
» 
»     def _estado(self):
»         ''' Retorna uma tupla '''
»         return int(self.energia + self.inteligencia), int(self.energia * 10)
»     
»     def __str__(self):
»         return 'Vida = %d Poder = %d' % (self.energia , self.inteligencia)
» 
» class Cientista(Personagem):
»     def _estado(self):
»         return  int(self.energia + self.inteligencia *10), int(self.energia * 5)
»      
»     def __str__(self):
»         return 'Cientista: Vida = %d Poder = %d' %  (self.vida , self.poder)
» 
» class Estudante(Personagem):
»     def _estado(self):
»         return  int(self.energia + self.inteligencia * 5), int(self.energia * 10)
» 
»     def __str__(self):
»         return 'Estudante: Vida = %d Poder = %d' % (self.vida , self.poder)
» 
» p = Personagem(10,10)
» c = Cientista(10,10)
» e = Estudante(10,10)
» 
» print(p)
↳ Vida = 10 Poder = 10
» 
» print(c)
↳ Cientista: Vida = 110 Poder = 50
» 
» print(e)
↳ Estudante: Vida = 60 Poder = 100

Esse é um exemplo de polimorfismo pois cada subclasse possui seu próprio método _estado(). É importante lembrar que __init__() sempre retorna None pois não possui (nem admite) o comando return. A notação com sublinhado em _estado() sugere que o método é de uso interno na classe e seus objetos.

Exibindo um objeto, __str__, __repr__, __format__

Temos usado o método __str__ que retorna uma string com dados sobre o objeto que é exibida com print(objeto). Usando esse método esperamos obter uma descrição amigável e legível do objeto, contendo todos os dados ou que julgamos mais relevantes.

Outro método, __repr__, também retorna uma representação de string, mais técnica e em geral usando uma expressão completa que pode ser usada para reconstruir o objeto. Ele é acionado pela função repr(objeto). Essa representação, se passada como argumento para a função eval(), retorna um objeto com as mesmas características do original. A função eval() interpreta a string passada como argumento e a interpreta como código, executando esse código como uma linha de programação python. Outros exemplos são dados a seguir.

» # __repr__
» class Bicho:
»     def __init__(self, especie, habitat):
»         self.especie = especie
»         self.habitat = habitat
» 
»     def __repr__(self):
»         return 'Bicho("%s", "%s")' % (self.especie, self.habitat)
» 
»     def __str__(self):
»         return 'O bicho %s com habitat: %s' % (self.especie, self.habitat)
»     
» peixe = Bicho('peixe', 'rios')
» 
» # usando o método __repr__
» print(repr(peixe))
↳ Bicho("peixe", "rios")
» 
» # usando o método __str__
» print(peixe)
↳ O bicho peixe com habitat: rios
» 
» # usando eval()
» bagre = eval(repr(peixe))
» print(bagre)
↳ O bicho peixe com habitat: rios

O retorno de repr(peixe) pode ser usado para reconstruir o objeto peixe. Caso o método __str__ não tenha sido sobrescrito sua execução chama o método __repr__ e a saída é idêntica. A expressão bagre = eval(repr(peixe)) é idêntica à bagre = Bicho("peixe", "rios").

» # outros exemplos de uso de eval()
» txt = 'x+x**x'.replace('x','3')
» eval(txt)
↳ 30
» 
» txt = "'casa da mãe joana'.title()"
» eval(txt)
↳ Casa Da Mãe Joana
» 
» for t in [a + ' * ' + b for a in '67' for b in '89']:
»     print(t, '=', eval(t))
↳ 6 * 8 = 48
↳ 6 * 9 = 54
↳ 7 * 8 = 56
↳ 7 * 9 = 63


A função eval() não deve ser aplicada diretamente a dados digitados pelo usuário devido ao risco de que se digite uma expressão que delete dados ou cause qualquer outro dano ao computador ou rede onde o código é executado.

Função e método format

Já vimos o método de formatação de strings usando format(), que é uma funcionalidade mais moderna e poderosa que a notação de % para inserir campos. Recapitulando e expandindo um pouco vamos listas mais alguns exemplos desse método.

» # marcadores nomeados são alimentados por format
» txt1 = '1. Eu me chamo {nome} e tenho {idade} anos.'.format(nome = 'João', idade = 36)
» 
» # os campos podem ser marcados numericamente
» txt2 = '2. Moro em {0}, {1} há {2} anos.'.format('Brasília', 'DF', 20)
» txt3 = '3. Há {2} anos moro em {0}, {1}.'.format('Brasília', 'DF', 20)
» 
» # ou marcados apenas por sua ordem de aparecimento
» txt4 = "4. {} são convertidos em {}. Exemplo: {}.".format('Dígitos', 'strings', 100)
» 
» # controle do número de casas decimais
» txt5 = '5. Esse livro custa R$ {preco:.2f} com desconto!'.format(preco = 49.8)
» 
» print(txt1)
↳ 1. Eu me chamo João e tenho 36 anos.
» 
» print(txt2)
↳ 2. Moro em Brasília, DF há 20 anos.
» 
» print(txt3)
↳ 3. Há 20 anos moro em Brasília, DF.
» 
» print(txt4)
↳ 4. Dígitos são convertidos em strings. Exemplo: 100.
» 
» print(txt5)
↳ 5. Esse livro custa R$ 49.80 com desconto!
» 
» # argumento de format é "unpacking" de sequência
» print('{3}{2}{1}{0}-{3}{0}{1}{2}-{1}{2}{3}{0}-{0}{3}{2} '.format(*'amor'))
↳ roma-ramo-mora-aro
» 
» #indices podem ser repetidos
» print('{0}{1}{0}'.format('abra', 'cad'))
↳ abracadabra

padrao.format(objeto) pode conter um objeto com propriedades que serão lidas em {objeto.propriedade}. Por ex., o módulo sys contém os atributos sys.platform e sys.version.

» import sys
» # sys possui atibutos sys.platform e sys.version
» # no caso abaixo sys é o parâmetro 0
» print ('Platform: {0.platform}\nPython version: {0.version}'.format(sys))
↳ Platform: linux
↳ Python version: 3.8.5 (default, Sep  4 2020, 07:30:14) 
↳ [GCC 7.3.0]

O parâmetro pode ser um objeto construído pelo programador.

» class Nome:
»     nome='Silveirinha'
»     profissao='contador' 
» 
» n = Nome()
» idade = 45
» print('Empregado: {0.nome}\nProfissão: {0.profissao}\nIdade: {1} anos'.format(n, idade))
↳ Empregado: Silveirinha
↳ Profissão: contador
↳ Idade: 45 anos

Uma especificação de formato mais precisa pode ser incluída cim a sintaxe de vírgula seguida da especificação do formato.
{0:30} significa, campo 0 preenchido com 30 espaços, e {1:>4} campo 1 com 4 espaços, alinhado à direita. O alinhamento à esquerda é default.

» # Campo 0: justificado à esquerda (default), preenchendo o campo com 30 caracteres
» # Campo 1: justificado à direita preenchendo o campo com 4 caracteres
» linha = '{0:30} R${1:>4},00'
» 
» print(linha.format('Preço para alunos', 35))
» print(linha.format('Preço para professores', 115))
↳ Preço para alunos              R$  35,00
↳ Preço para professores         R$ 115,00

Os campos usados em format() podem ser aninhados (um campo dentro do outro). No caso abaixo a largura do texto é passada como campo 1, que está dentro do campo 0, como comprimento.

» # campos aninhados. campo 0 é o texto a ser formatado pelo padrão. Campo 1 é a largura do texto
» padrao = '|{0:{1}}|'
» largura = 30
» txt ='conteúdo de uma célula'
» print(padrao.format(txt, largura))
↳ |conteúdo de uma célula        |

Esse processo pode ser muito útil quando se monta um texto com formatação, como em html. No preencimento de texto delimitado por tags um padrão pode incluir trechos de início e fim. Na construção de um tabela, por ex., as tags de abertura e fechamento de uma célula de uma tabela são <td></td>.

» padrao = '{inicio}{0:{1}}{fim}'
» txt ='conteúdo da célula'
» larg = len(txt)
» print(padrao.format(txt, larg, inicio = '', fim = ''))
↳ conteúdo da célula

Lembrando: os parâmetros posicionais 0, 1 devem vir antes dos nomeados.

Outro exemplo é a montagem de uma tabela com valores organizados por colunas. O padrão abaixo estabelece que cada número deve ocupar 6 espaços. Observe que ‘{:6d} {:6d} {:6d} {:6d}’ é o mesmo que ‘{0:6d} {1:6d} {2:6d} {3:6d}’.

» for i in range (3, 8):
»     padrao = '{:6d} {:6d} {:6d} {:6d}'
»     print(padrao.format(i, i ** 2, i ** 3, i ** 4))
↳      3      9     27     81
↳      4     16     64    256
↳      5     25    125    625
↳      6     36    216   1296
↳      7     49    343   2401

Os seguintes sinais podem são usados para alinhamento:

Especificadores de alinhamento
< alinhado à esquerda (default),
> alinhado à direita,
^ centralizado,
= para tipos numéricos, preenchimento após o sinal.

Os seguintes sinais são usados para especificação de formato:

Especificadores de formato
b Binário. Exibe o número na base 2.
c Caracter. Converte inteiros em caractere Unicode.
d Inteiro decimal. Exibe o número na base 10.
o Octal. Exibe o número na base 8.
x Hexadecimal. Exibe número na base 16, usando letras minúsculas para os dígitos acima de 9.
e Expoente. Exibe o número em notação científica usando a letra ‘e’ para o expoente.
g Formato geral. Número com ponto fixo, exceto para números grandes, quando muda para a notação de expoente ‘e’.
n Número. É o mesmo que ‘g’ (para ponto flutuantes) ou ‘d’ (para inteiros), exceto que usa a configuração local para inserir separadores numéricos.
% Porcentagem. Multiplica número por 100 e exibe no formato fixo (‘f’), seguido de sinal %.

A função format() aciona o método __format__() interno ao objeto. Uma classe do programador pode ter esse método overloaded e customizado. Ele deve ser chamado como __format__(self, format_spec) onde format_spec são as especificações das opções de formatação.

As seguintes expressões são equivalentes:
format(obj,format_spec) <=> obj.__format__(obj,format_spec) <=> "{:format_spec}".format(obj)

No próximo exemplo construímos uma classe para representar datas com os atributos inteiros dia, mês e ano. O método __format() recebe um padrão e retorna a string de data formatada de acordo com esse padrão. Por exemplo, se padrao = 'dma' ela retorna '{d.dia}/{d.mes}/{d.ano}'.format(d=self). O método __str__() monta uma string diferente usando um dicionário que associa o número ao nome do meses.

» class Data:
»     def __init__(self, dia, mes, ano):
»         self.dia, self.mes, self.ano = dia, mes, ano
»     
»     def __format__(self, padrao):
»         p = '/'.join('{d.dia}' if t=='d' else ('{d.mes}' if t=='m' else '{d.ano}') for t in padrao)
»         return p.format(d=self)
»
»     def __str__(self):
»         mes = {1:'janeiro', 2:'fevereiro', 3:'março', 4:'abril', 5:'maio', 6:'junho',
»          7:'julho', 8:'agosto', 9:'setembro', 10:'outubro', 11:'novembro', 12:'dezembro'}
»         m = mes[self.mes]
»         return '{0} de {1} de {2}'. format(self.dia, m, self.ano)
» 
» d1, d2, d3 = Data(31,12,2019), Data(20,7,2021), Data(1,7,2047)
» 
» print('Usando função: format(objeto, especificacao)')
» print(format(d1,'dma'))
» print(format(d2,'mda'))
» print(format(d3,'amd'))
↳ Usando função: format(objeto, especificacao)
↳ 31/12/2019
↳ 7/20/2021
↳ 2047/7/1
»
» print('\nUsando formatação de string: padrao.format(objeto)')
» print('dia 1 = {:dma}'.format(d1))
» print('dia 2 = {:mda}'.format(d2))
» print('dia 3 = {:amd}'.format(d3))
↳ Usando formatação de string: padrao.format(objeto)
↳ dia 1 = 31/12/2019
↳ dia 2 = 7/20/2021
↳ dia 3 = 2047/7/1
» 
» print('\nUsando método __str__()')
» print(d3)
↳ Usando método __str__()
↳ 1 de julho de 2047

Função property() e decorador @property

Vimos anteriormente que pode ser útil isolar uma propriedade em uma classe e tratá-la como privada. Isso exige a existência de getters e setters, tratados na seção anterior.

No código abaixo definimos uma classe simples com métodos getter, setter e outro para apagar uma propriedade.

» class Pessoa:
»     def __init__(self):
»         self._nome = ''
»     def setNome(self,nome):
»         self._nome = nome.title()
»     def getNome(self):
»         return 'Não fornecido' if not self._nome else self._nome
»     def delNome(self):
»         self._nome = ''
»    
» # inicializamos um objeto da classe e atribuimos um nome
» p1 = Pessoa()
» p1.setNome('juma jurema')
» 
» print(p1.getNome())
↳ Juma Jurema
» 
» # apagando o nome
» p1.delNome()
» print(p1.getNome())
↳ Não fornecido

A função property facilita o uso desse conjunto de métodos. Ela tem a seguinte forma:

property(fget=None, fset=None, fdel=None, doc=None)

onde todos os parâmetros são opcionais. São esses os parâmetros:

  • fget representa o método de leitura,
  • fset método de atribuição,
  • fdel método de apagamento e
  • doc de uma docstring para o atributo. Se doc não for fornecido a função lê o docstring da função getter.

Ela é usada da seguinte forma:

» class Pessoa:
»     def __init__(self, nome=''):
»         self._nome = nome.title()
»
»     def setNome(self, nome):
»         self._nome = nome.title()
»
»     def getNome(self):
»         return 'Não informado' if self._nome=='' else self._nome
»
»     def delNome(self):
»         self._nome = ''
» 
»     nome = property(getNome, setNome, delNome, 'Propriedade de nome')
 
» p = Pessoa('juma jurema')
» print(p.nome)
↳ Juma Jurema
 
» p.nome = 'japira jaciara'
» print(p.nome)
↳ Japira Jaciara

» del p.nome
» print(p.nome)
↳ Não informado

Com a linha nome = property(getNome, setNome, delNome, 'Propriedade de nome') se adiciona um novo atributo nome associada aos métodos getNome, setNome, delNome. Fazendo isso os seguintes comandos são equivalentes:

  • p1.nomep1.getNome(),
  • p1.nome = 'novo nome'p1.setNome('novo nome') e
  • del p1.nomep1.delNome().

Ao invés de usar essa sintaxe também podemos usar o decorador @property.

» class Pessoa:
»     def __init__(self, nome):
»         self._nome = nome.title()
» 
»     @property
»     def nome(self):
»         return 'Não informado' if self._nome=='' else self._nome
» 
»     @nome.setter
»     def nome(self, nome):
»         self._nome = nome.title()
» 
»     @nome.deleter
»     def nome(self):
»         self._nome = ''

» p = Pessoa('adão adâmico')
» print(p.nome)
↳ Adão Adâmico

» p.nome = 'arthur artemis'
» print(p.nome)
↳ Arthur Artemis

» del p.nome
» print(p.nome)
↳ Não informado

Observer que o decorador @property define a propriedade nome e portanto deve aparecer antes de nome.setter e nome.deleter.

Se mais de um atributo deve ser decorado o processo deve ser repetido para cada atributo.

» class Pessoa:
»     def __init__(self):
»         self._nome = ''
»         self._cpf = ''
» 
»     @property
»     def nome(self):
»         return self._nome
» 
»     @property
»     def cpf(self):
»         return self._cpf
»  
»     @nome.setter
»     def nome(self, nome):
»         self._nome = nome
»         
»     @cpf.setter
»     def cpf(self, cpf):
»         self._cpf = cpf
        
» p = Pessoa()
» p.nome = 'Albert'
» p.nome
↳ 'Albert'

» p.cpf = '133.551.052-12'
» p.cpf
↳ '133.551.052-12'

O decorador @property permite que um método seja acessado como se fosse um atributo. Ele é particularmente útil quando já existe código usando um acesso direto à propriedade na forma de objeto.atributo. Se a classe é modificada para usar getters e setters o uso de @property dispensa que todo o restante do código seja alterado..

Método __getattr__

A função interna getattr é usada para ler o valor de um atributo dentro de um objeto. Além de realizar essa leitura ela permite que se retorne um valor especificado caso o atributo não exista. Ela tem a seguinte sintaxe:

getattr(objeto, atributo[, default])

onde os parâmetros são:

  • objeto (obrigatório), um objeto qualquer;
  • atributo (obrigatório), um atributo do objeto;
  • default (opcional), o valor a retornar caso o atributo não exista.

Ela retorna o valor de objeto.atributo.
Com funcionalidades associadas temos as seguintes funções, exemplificadas abaixo:

  • hasattr(objeto, atributo), que verifica se existe o atributo,
  • setattr(objeto, atributo), que insere um valor nesse atributo,
  • delattr(objeto, atributo), que remove o atributo desse objeto.
» class Pessoa:
»     nome = 'Einar Tandberg-Hanssen'
»     idade = 91
»     pais = 'Norway'

» p = Pessoa
» p.pais='Noruega'
» print(getattr(p, 'nome'))
↳ Einar Tandberg-Hanssen

» print(getattr(p, 'idade'))
↳ 91

» print(getattr(p, 'pais'))
↳ Noruega

» print(getattr(p,'profissao','não encontrado'))
↳ não encontrado

» # funções hasattr, setattr e delattr
» hasattr(p,'pais')
↳ True

» hasattr(p,'profissao')
↳ False

» setattr(p, 'idade', 28)
» p.idade
↳ 28

» delattr(p,'idade')
» hasattr(p,'idade')
↳ False

O método __getattr__ permite um overload da função getattr. Ele é particularmente útil quando se deseja retornar muitos atributos derivados dos dados fornecidos, seja por cálculo, por composição ou modificação.

» class Pessoa:
»     def __init__(self, nome, sobrenome):
»         self._nome = nome
»         self._sobrenome = sobrenome
» 
»     def __getattr__(self, atributo):
»         if atributo=='nomecompleto':
»             return '%s %s' % (self._nome, self._sobrenome)
»         elif atributo=='nomeinvertido':
»             return '%s, %s' % (self._sobrenome, self._nome)
»         elif atributo=='comprimento':
»             return len(self._nome) + len(self._sobrenome)
»         else:
»             return 'Não definido'

» p = Pessoa('Albert','Einsten')

» p.nomecompleto
↳ 'Albert Einsten'

» p.nomeinvertido
↳ 'Einsten, Albert'

» p.comprimento
↳ 13

» p.idade
↳ 'Não definido'

Função e método hash

O método __hash__ é invocado quando se aciona a função hash().

chave = hash(objeto)

Hash usa um algoritmo que transforma o objeto em um número inteiro único que identifica o objeto. Esses inteiros são gerados de forma aleatória (tanto quanto possível) de forma que diversos objetos no código não repitam o mesmo código. O hash é mantido até o fim da execução do código (ou se o objeto for reinicializado). Só existem hashes de objetos imutáveis, como inteiros, booleanos, strings e tuplas e essa propriedade pode ser usada para testar se um objeto é ou não imutável.

Essa chave serve como índice que agiliza a localização de objetos nas coleções e é usada internamente pelo Python em dicionários e conjuntos.

» # o hash de um inteiro é o  próprio inteiro
» hash(12345)
↳ 12345

» hash('12345')
↳ -918245046130431123

» # listas não possuem hashes
» hash([1,2,3,4,5])
↳ TypeError: unhashable type: 'list'

» # o hash de uma função
» def func(fim):
»     for t in range(fim):
»         print(t, end='')
» a = func
» print(hash(a))
↳ 8743614090882

» # todos os elementos de uma tupla devem ser imutáveis
» print(hash((1, 2, [1, 2])))
↳ TypeError: unhashable type: 'list'

Em uma classe podemos customizar __hash__ e __eq__ de forma a alterar o teste de igualdade, ou seja, definimos nosso próprio critério de comparação.

As implementações default de __eq__ e __hash__ nas classes usam id() para fazer comparações e calcular valores de hash, respectivamente. A regra principal para a implementação customizada de métodos __hash__ é que dois objetos iguais devem ter o mesmo valor de hash. Por isso se __eq__ for alterada para um teste diferente de igualdade, o que pode ser o caso dependendo de sua aplicação, o método __hash__ também deve ser alterado para ficar em acordo com essa escolha.

Métodos id, hash e operadores == e is

Três conceitos são necessários para entender id, hash e os operadores == e is que são, respectivamente: identidade, valor do objeto e valor de hash. Nem todos os objetos possuem os três.

Objetos têm uma identidade única, retornada pela função id(). Se dois objetos têm o mesmo id eles são duas referências ao mesmo objeto. O operador is compara itens por identidade: a is b é equivalente a id(a) == id(b).

Objetos também têm um valor: dois objetos a e b têm o mesmo valor se a igualdade pode ser testada e a == b. Objetos container (como listas) têm valor definido por seu conteúdo. Objetos do usuário tem valores baseados em seus atributos. Objetos de diferentes tipos podem ter os mesmos valores, como acontece com os números: 0 == 0.0 == 0j == decimal.Decimal ("0") == fraction.Fraction(0) == False.

Se o método __eq__ não estiver definido em uma classe (para implementar o operador ==), seus objetos herdarão da superclasse padrão e a comparação será feita entre seus ids.

Objetos distintos podem ter o mesmo hash mas objetos iguais devem ter o mesmo hash. Armazenar objetos com o mesmo hash em um dicionário é muito menos eficiente do que armazenar objetos com hashes distintos pois a colisão de hashes exige processamento extra. Objetos do usuário são “hashable” por padrão pois seu hash é seu id. Se um método __eq__ for overloaded em uma classe personalizada, o hash padrão será desativado e deve também ser overloaded se existe intenção de manter a classe imutável. Por outro lado se você deseja forçar uma classe a gerar objetos mutáveis (sem valor de hash) você pode definir seu método __hash__ para retornar None.

Exibiremos dois exemplos para demonstrar esses conceitos. Primeiro definimos uma classe com sua implementação default de __equal__ e __hash__. Em seguida alteramos hash e __eq__ fazendo o teste de igualdade concordar com o teste de hash. Observe que em nenhum caso dois objetos diferentes satisfazem b1 is b2 já que o id é fixo e não pode ser alterado pelo usuário.

» # ----------- Exemplo 1 ---------------    
» class Bar1:
»     def __init__(self, numero, lista):
»         self.numero = numero
»         self.lista = lista

» b1 = Bar1(123, [1,2,3])
» b2 = Bar1(123, [1,2,3])
» b3 = b1

» b1 == b2                      # (o mesmo que b1 is b2)
↳ False
» b1 == b3                      # (o mesmo que b1 is b3)
↳ True
» id(b1) == id(b2)
↳ False
» hash(b1) == hash(b2)
↳ False

» # ----------- Exemplo 2 ---------------
» class Bar2:
»     def __init__(self, numero, lista):
»         self.numero = numero
»         self.lista = lista
»     def __hash__(self):
»         return hash(self.numero)
»     def __eq__(self, other):
»         return self.numero == other.numero and self.lista == other.lista
        
» b1 = Bar2(123, [1,2,3])
» b2 = Bar2(123, [1,2,3])
» b3 = Bar2(123, [4,5,6])

» b1 == b2
↳ True
» id(b1) == id(b2)
↳ False
» hash(b1) == hash(b2)
↳ True
» b1 == b3
↳ False
» hash(b1) == hash(b3)
↳ True

No segundo caso a alteração de __hash__ faz com que dois objetos diferentes, de acordo com o teste is, possam ter o mesmo código hash, o que pode ser útil, dependendo da aplicação.

Uma tabela que associa objetos usando o hash de uma coluna como índice, agilizando a busca de cada par é chamada de hashtable. No Python os dicionários são exemplos dessas hashtables.

O processo de hashing é usado em encriptação e verificação de autenticidade. O Python oferece diversos módulos para isso, como hashlib, instalado por padrão, e cryptohash disponível em Pypi.org.

Métodos __new__ e __del__

Vimos que o método __init__ é adicionado automaticamente quando uma classe é instanciada e que podemos passar valores em seus parâmetros para inicializar o objeto. Antes dele outro método é acionado automaticamente, o método __new__, acessado durante a criação do objeto. Ambos são acionados automaticamente.

Além dele o método __del__ é ativado quando o objeto é destruído (quando a última referência é desfeita e o objeto fica disponível para ser coletado pela lixeira).

O parâmetro cls representa a superclasse que será instanciada e seu valor é provido pelo interpretador. Ele não tem acesso a nenhum dos atributos do objeto definidos em seguida.

» class A:
»     def __new__(cls):
»         print("Criando novo objeto") 
»         return super().__new__(cls)
»     def __init__(self): 
»         print("Dentro de __init__ ")
»     def __del__(self):
»         print("Dentro de __del__ ")

» # instanciando um objeto
» a = A()
↳ Criando novo objeto
↳ Dentro de __init__ 

» # finalizando a referência ao objeto
» del a
↳ Dentro de __del__ 

A função super(), que veremos como mais detalhes, é usada para acessar a superclasse de A, que no caso é object, a classe default da qual herdam todos os demais objetos. Nessa linha se retorna o método __new__ da superclasse e, sem ela, não teríamos acesso à A.__init__.

Como não há uma garantia de que o coletor de lixo destruirá o objeto imediatamente após a referência ser cortada (veja a seção sobre o coletor de lixo) método __del__ tem utilidade reduzida. Ele pode ser útil, no entanto e por ex., para desfazer outra referência que pode existir para o mesmo objeto. Também podem ser usadas em conjunto __new__ e __del__ para abir e fechar, respectivamente, uma conexão com um arquivo ou com um banco de dados.

Se __init__ demanda por valores de parâmetros esses valores devem ser passados para __new__. O exemplo abaixo demostra o uso de __new__ para decidir se cria ou não um objeto da classe Aluno. Se a nota < 5 nenhum objeto será criado.

» class Aluno:
»     def __new__(cls, nome, nota):
»         if nota >= 5:
»             return object.__new__(cls)
»         else:
»             return None
» 
»     def __init__(self, nome, nota):
»         self.nome = nome
»         self.nota = nota
» 
»     def getConceito(self):
»         return 'A' if self.nota >= 8.5 else 'B' if self.nota >= 7.5 else 'C'
» 
»     def __str__(self):
»         return 'Classe = %s\nDicionário(%s) Conceito: %s %s' % (self.__class__.__name__,
»                                                    str(self.__dict__),
»                                                    self.getConceito(),
»                                                    '-'*60 )
                                                   
» a1, a2, a3 = Aluno('Isaac Newton', 9), Aluno('Forrest Gump',4), Aluno('Mary Mediana',7)

» print(a1)
» print(a2, '<---- Objeto não criado' + '-'*60)
» print(a3)

↳ Classe = Aluno
↳ Dicionário({'nome': 'Isaac Newton', 'nota': 9}) Conceito: A
↳ ------------------------------------------------------------
↳ None <---- Objeto não criado
↳ ------------------------------------------------------------
↳ Classe = Aluno
↳ Dicionário({'nome': 'Mary Mediana', 'nota': 7}) Conceito: C
↳ ------------------------------------------------------------

A classe Aluno usa no método __str__ dois atributos das classes: __class__ e __dict__. __class__ retorna um objeto descritor, que é de leitura apenas (não pode ser modificado) que contém dados sobre a superclasse, entre eles o atributo __name__, o nome da classe.

» a1.__class__==Aluno
↳ True

» print(a1.__class__.__name__)
↳ Aluno

» a1.__dict__
↳ {'nome': 'Isaac Newton', 'nota': 9}

» print(dir(a1))
↳ ['__class__', '__delattr__', '__dict__', '__dir__', '__doc__', '__eq__', '__format__', '__ge__', '__getattribute__',
↳ '__gt__', '__hash__', '__init__', '__init_subclass__', '__le__', '__lt__', '__module__', '__ne__', '__new__',
↳  '__reduce__', '__reduce_ex__', '__repr__', '__setattr__', '__sizeof__', '__str__', '__subclasshook__',
↳ '__weakref__', 'getConceito', 'nome', 'nota']

A função dir retorna todos os seus métodos e propriedades. Como todos os objetos do Python, a classe Aluno possui o atributo __dict__ que é um dicionário contendo suas propriedades. As propriedades podem ser apagadas, editadas ou inseridas diretamente nesse dicionário.

» # inserindo campo 'nascimento'
» a1.__dict__['nascimento'] = '21/03/2001'
» a1.nascimento
↳ '21/03/2001'

» # apagando campo 'nota'
» del(a1.__dict__)['nota']
» a1.nota
↳ AttributeError: 'Aluno' object has no attribute 'nota

Funções e Classes Fábrica (Factory Classes, Functions)


Na programação orientada a objetos uma fábrica é um objeto usado para a criação de outros objetos. Ela pode ser uma função ou método que retorna objetos derivados de um protótipo ou de uma classe e suas subclasses, à partir de parâmetros que permitem a decisão sobre qual objeto retornar.

Isso é particularmente útil quando um grande número de objetos devem ser criados. Esse construtor generalizado pode ter um processo de decisão sobre que subclasse usar. No exemplo seguinte usamos a classe Personagem e suas subclasses Cientista e Estudante definidas anteriormente para construir um exemplo que gera alguns objetos.

» # uma função "factory"
» def criaPersonagem(qual, energia, inteligencia):
»     if qual == 0:
»         return Personagem(energia, inteligencia)
»     elif qual ==1:
»         return Cientista(energia, inteligencia)
»     elif qual ==2:
»         return Estudante(energia, inteligencia)
»     else:
»         print('Personagem = 0, 1, 2')

» # Constroi 3 personagens de tipos diferentes e os armazena em um dicionário
» personagens = {}
» for t in range(3):
»     personagens[t] = criaPersonagem(t, 100, 100)

» # temos um dicionário com os personagens
» for t in range(3):
»     print(personagens[t])
↳ Vida = 100 Poder = 100
↳ Cientista: Vida = 1100 Poder = 500
↳ Estudante: Vida = 600 Poder = 1000

Um design interessante para evitar um código repleto de ifs e, ao mesmo tempo, forçar uma padronização da informação consiste em usar códigos associados ao dado que se quer informar. Para o exemplo seguinte suponha que estamos interessados em classificar livros em uma biblioteca. Para isso criamos um dicionário que associa um código a cada gênero de livros. Com isso obrigamos o preenchimento de dados a se conformar com a inserção correta do código e evitar erros tais como escrever de modo diferente o mesmo gênero (como Poesias e Poemas).

Digamos que em uma biblioteca existem livros dos seguintes gêneros:

  • Romance
  • Poesia
  • Ficção Científica
  • Divulgação Científica
  • Política
  • Biografias

Claro que em um caso mais realista teríamos muito mais gêneros e subgêneros, tipicamente armazenados por código em um banco de dados.

Iniciamos por criar um dicionário (categoria) usando código do gênero como chave. A definição da classe Livro testa na inicialização se o código fornecido existe e, caso afirmativo, substitui self.genero com o gênero correspondente. Se o código não existe fazemos self.genero = "Não Informado".

» # armazenamos a relação codigo - gênero
» categoria = {1 : 'Romance',
»              2 : 'Poesia',
»              3 : 'Ficção Científica',
»              4 : 'Divulgação Científica',
»              5 : 'Política',
»              6 : 'Biografias'
»              }
» 
» # definimos a classe livro
» class Livro:
»     def __init__(self, codigoGenero, titulo, autor):
»         if codigoGenero in categoria.keys():
»             self.genero = categoria[codigoGenero]
»         else:
»             self.genero = 'Não informado'
»         self.titulo = titulo
»         self.autor = autor
»         
»     def __str__(self):
»         txt = 'Gênero: %s\n' % self.genero
»         txt += 'Título: %s\n' % self.titulo
»         txt += 'Autor: %s\n' % self.autor
»         return txt

» # Inicializamos livro com código existente
» livro1 = Livro(1, 'Anna Karenina','Leo Tolstoy')
» print(livro1)
↳ Gênero: Romance
↳ Título: Anna Karenina
↳ Autor: Leo Tolstoy

» # Inicializamos livro com código inexistente
» livro2 = Livro(9, 'Cosmos','Carl Sagan')
» print(livro2)
↳ Gênero: Não informado
↳ Título: Cosmos
↳ Autor: Carl Sagan

Outra abordagem seria armazenar o próprio código transformando-o em texto apenas no momento de uma consulta ou impressão. Esse tipo de design é particularmente útil quando a quantidade de dados mapeados é grande e a consulta a eles é frequente.

Já vimos que uma subclasse pode fazer poucas modificações na classe base, aproveitando quase todo o seu conteúdo mas customizando alguns atributos.

» class Biografia(Livro):
»     def __init__(self, titulo, autor, biografado):
»         self.biografado = biografado
»         super().__init__(6, titulo, autor)
»     def __str__(self):
»         return '%sBiografado: %s' % (super().__str__(), self.biografado)

» bio1 = Biografia('Um Estranho ao Meu Lado','Ann Rule','Ted Bundy')
» print(bio1)
↳ Gênero: Biografias
↳ Título: Um Estranho ao Meu Lado
↳ Autor: Ann Rule
↳ Biografado: Ted Bundy

Suponha que tenhamos criado subclasses especializadas para cada gênero: Biografia, Politica, Poesia, …, etc. Uma factory de classes pode usar um dicionário que associa diretamente um código à classe. No exemplo temos, além da classe Biografia, criamos outras duas apenas para efeito de demonstração.

» class Politica(Livro):
»     pass
»
» class Poesia(Livro):
»     pass
    
» # uma factory de classe (retorna a classe apropriada)
» class FactoryLivro:
»     def __init__(self, i):
»         classe = {6: Biografia, 5: Politica, 2: Poesia}
»         self.essaClasse = classe.get(i,Livro)
»     def getClasse(self):
»         return self.essaClasse

» # Inicializa com livro do gênero 6 (biografia, único que defimos corretamente)
» fact = FactoryLivro(6)
» # classBio vai receber a classe Biografia
» ClassBio = fact.getClasse()

» # instancia um objeto de ClassBio
» bio2 = ClassBio('Vivendo na Pré-história','Ugah Bugah','Fred Flistone')
» print(bio2)
↳ Gênero: Biografias
↳ Título: Vivendo na Pré-história
↳ Autor: Ugah Bugah
↳ Biografado: Fred Flistone

Relembrando, usamos acima o método de dicionários dict.get(key, default), que retorna o valor correspondente à key se ela existe, ou default, se não existe.

Claro que, ao invés de criar subclasses para cada gênero, também poderíamos ampliar a generalidade da própria superclasse Livro inserindo campos flexíveis capazes de armazenar as especificidades de cada gênero, tal como as propriedades Livro.nomeDoCampo e Livro.valorDoCampo.

Classes servidoras de dados: Podemos definir uma classe que não recebe dados do usuário e apenas é usada para preparar e retornar dados em alguma forma específica. A classe Baralho abaixo representa um baralho construído na inicialização, juntando naipes com números de 2 até 10, adicionados de A, J, Q, K. Um coringa C é adicionado a cada baralho. Um objeto da classe tem acesso ao método Baralho.distribuir(n) que seleciona e retorna aleatoriamente n cartas. Para embaralhar as cartas usamos o método random.shuffle que mistura elementos de uma coleção (pseudo) aleatoriamente.

» import random
» class Baralho:
»     def __init__(self):
»         naipes = ['♣', '♠', '♥','♦']
»         numeros = list(range(1, 14))
»         numeros = ['A' if i==1
»                    else 'K' if i==13
»                    else 'Q' if i==12
»                    else 'J' if i==11
»                    else str(i) for i in numeros
»                   ]
»         deck = [a + b for a in numeros for b in naipes]
»         deck += ['C']
»         random.shuffle(deck)
»         self.deck = deck
» 
»     def distribuir(self, quantas):
»         if quantas > len(self.deck):
»             return 'Só restam %d cartas!' % len(self.deck)
»         mao = ''
»         for t in range(quantas):
»             mao += '[%s]' % self.deck.pop()
»         return mao

» jogo = Baralho()
» player1 = jogo.distribuir(11)
» player2 = jogo.distribuir(11)

» print(player1)
↳ [7♥][5♠][6♥][3♦][6♠][8♣][4♥][3♣][9♠][Q♦][7♣]

» print(player2)
↳ [4♦][A♦][J♣][J♥][8♠][9♦][8♦][10♦][10♥][5♥][3♥]


O método lista.pop() retorna o último elemento da lista, removendo o elemento retornado, como uma carta retirada de um baralho.

Para tornar esse processo ainda mais interessante poderíamos criar a classe Carta que gera objetos contendo separadamente seu naipe e valor para facilitar as interações com outras cartas durante o jogo. Esses objetos poderiam, inclusive, armazenar o endereço de imagens de cada carta do baralho, gerando melhores efeitos visuais. Com isso o método Baralho.distribuir() poderia retornar uma coleção de cartas e não apenas strings com uma represntação simples das cartas do baralho.

🔺Início do artigo

Bibliografia

Consulte a bibliografia no final do primeiro artigo dessa série.

Python: Resumos

Conteúdo

  • Métodos de Conjuntos (sets)
  • Métodos de Dicionários (dictionaries)
  • Métodos de Arquivos (files)
  • Métodos da biblioteca os
  • Exceções
  • Palavras reservadas

    Palavras reservadas (ou keywords) que são nomes que fazem parte da sintaxe da linguagem e não podem ser utilizadas para nomes de variáveis.

    Palavras reservadas do python
    and except lambda with
    as finally nonlocal while
    assert false None yield
    break for not
    class from or
    continue global pass
    def if raise
    del import return
    elif in True
    else is try

    Funções Internas

    Segue uma lista de funções internas ou pré-programadas do Python. Muitas outras podem ser acrescentadas à uma sessão através da importação de módulos externos.

    Função ação (retorna)
    abs() valor absoluto de um número
    all() True se todos os itens em um objeto iterável forem verdadeiros
    any() True se qualquer item em um objeto iterável for verdadeiro
    ascii() uma versão legível de um objeto (trocando caracteres não ascii por caracteres de escape)
    bin() versão binária de um número
    bool() valor booleano do objeto especificado
    bytearray() uma matriz de bytes
    bytes() um objeto bytes
    callable() True se o objeto especificado pode ser chamado, caso contrário, False
    chr() um caractere do código Unicode especificado.
    classmethod() converte um método em um método de classe
    compile() fonte especificada como um objeto, pronto para ser executado
    complex() um número complexo
    delattr() exclui o atributo especificado(propriedade ou método) do objeto
    dict() um dicionário(Array)
    dir() uma lista das propriedades e métodos do objeto
    divmod() quociente e resto quando o argumento1 é dividido pelo argumento2
    enumerate() pega uma coleção(por exemplo, uma tupla) e retorna como um objeto enumerado
    eval() avalia e executa uma expressão
    exec() executa o código(ou objeto) especificado
    filter() usa uma função de filtro para excluir itens em um objeto iterável
    float() um número de ponto flutuante
    format() formata um valor especificado
    frozenset() um objeto frozenset
    getattr() o valor do atributo especificado (propriedade ou método)
    globals() a tabela de símbolos global atual como um dicionário
    hasattr() True se o objeto especificado tem o atributo especificado(propriedade / método)
    hash() o valor hash de um objeto especificado
    help() exibe ajuda embutido
    hex() converte um número em seu valor hexadecimal
    id() o id de um objeto
    input() permite entrada do usuário
    int() um número inteiro
    isinstance() True se o objeto é instância de outro objeto especificado
    issubclass() True se a classe é subclasse de objeto especificado
    iter() um objeto iterador
    len() o comprimento de um objeto
    list() uma lista
    locals() um dicionário atualizado da tabela de símbolos local atual
    map() o iterador com a função especificada aplicada a cada item
    max() o maior item em um iterável
    memoryview() um objeto de visualização de memória
    min() o menor item em um iterável
    next() o próximo item em um iterável
    object() um novo objeto
    oct() converte um número em um octal
    open() abre um arquivo e retorna objeto de arquivo
    ord() converte um inteiro que representa o Unicode do caractere especificado
    pow() o valor de x à potência de y
    print() imprime no dispositivo de saída padrão
    property() obtém, define ou exclui uma propriedade
    range() uma sequência de números, começando em 0 e incrementos em 1 (por padrão)
    repr() uma representação legível de um objeto
    reversed() um iterador reverso
    round() arredonda um número
    set() um objeto de conjunto
    setattr() define um atributo (propriedade/método) de um objeto
    slice() um objeto de fatia
    sorted() uma lista ordenada
    @staticmethod() Converte um método em um método estático
    str() um objeto de string
    sum() soma os itens de um iterador
    super() um objeto que representa a classe pai
    tuple() uma tupla
    type() o tipo de um objeto
    vars() a propriedade __dict__ de um objeto
    zip() um iterador, de dois ou mais iteradores

    Métodos de Strings

    Método descrição
    capitalize() converte 1º caracter em maiúsculo
    casefold() converte string em minúsculas
    center() retorna string centralizada
    count() retorna número de ocorrências de um valor especificado na string
    endswith() retorna True se string termina com valor especificado
    find() busca por valor especificado na string e retorna the posição se encontrado
    format() Formata de acordo com valores especificados
    index() busca por valor especificado na string e retorna the posição se encontrado
    isalnum() retorna True se todos os caracteres são alfa-numéricos
    isalpha() retorna True se todos os caracteres são alfabéticos
    isdecimal() retorna True se todos os caracteres são decimais
    isdigit() retorna True se todos os caracteres são dígitos
    islower() retorna True se todos os caracteres são minúsculos
    isnumeric() retorna True se todos os caracteres são numéricos
    isspace() retorna True se todos os caracteres são espaços
    istitle() retorna True se a string segue regra de títulos
    isupper() retorna True se todos os caracteres são maiúsculos
    join() reune elementos de um iterável no final da string
    ljust() retorna a string justificada à esquerda
    lower() converte a string para minúsculas
    lstrip() retorna a string sem espaços à esquerda
    partition() retorna tuple partindo a string em 3 partes
    replace() substitui trecho da string por outro especificado
    rfind() busca trecho especificado value e retorna última posição
    rindex() busca trecho especificado value e retorna última posição
    rjust() retorna string justificada à direita
    rsplit() quebra a string no separador especificado, retornando lista
    rstrip() retorna a string sem espaços à direita
    split() quebra a string no separador especificado, retornando lista
    splitlines() quebra a string nas quebras de linha, retornando lista
    startswith() retorna True se string começa com valor especificado
    strip() retorna a string sem espaços laterais
    swapcase() inverte minúsculas e maiúsculas
    title() converte em maiúscula o 1º caracter de cada palavra
    upper() converte a string em maiúsculas
    zfill() preencha com número de zeros especificado, no início

    Métodos de Listas (lists)

    Método ação
    append() insere elementos na lista
    clear() remove todos os elementos na lista
    copy() retorna uma cópia da lista
    count() returns número de elementos com valor especificado
    extend() insere os elementos de outra lista (ou iterável) ao final da lista
    index() returna o índice do 1º elemento com valor especificado
    insert() insere elemento em posição especificada
    pop() remove elemento em posição especificada
    remove() remove elemento em posição especificada por índice
    reverse() inverte a ordem da lista
    sort() ordena a lista

    Além desses as seguintes funções são úteis para se tratar com sequências:

    Função Descrição
    cmp(x, y) compara dois valores
    len(seq) retorna o comprimento da sequência
    list(seq) converte uma sequência em lista
    max(args) retorna o valor máximo na sequência
    min(args) retorna o valor mínimo na sequência
    eversed(seq) permite a iteração sobre valores na sequência
    sorted(seq) retorna lista ordenada dos elementos na sequência
    tuple(seq) converte a sequência em uma tuple

    Métodos das Tuplas (tuples)

    Método Descrição
    count() retorna quantas vezes um valor especificado ocorre na tupla
    index() procura por valor especificado e retorna sua posição

    Métodos de Conjuntos (sets)

    Método Descrição
    add() insere elemento no set
    clear() remove todos os elementos do set
    copy() retorna cópia do set
    difference() retorna um set com a diferença entre 2 ou mais sets
    difference_update() remove elementos incluidos no segundo set
    discard() remove item especificado
    intersection() retorna o set interseção de 2 sets
    intersection_update() remove items do set não presentes no segundo set especificado
    isdisjoint() retorna True se os 2 sets são disjuntos
    issubset() retorna True se o set é subconjunto do segundo set
    issuperset() retorna True se o set contém o segundo set
    pop() remove (e retorna) um elemento arbitrário do set
    remove() remove o elemento especificado
    symmetric_difference() retorna o set com a diferença simétrica de dois sets
    symmetric_difference_update() insere a diferença simétrica desse set em outro
    union() retorna um set com a união dos sets
    update() atualiza o primeiro set com sua união com um ou mais sets

    Métodos de Dicionários (dictionaries)

    Método Descrição
    clear() remove todos os elementos from the dictionário
    copy() retorna uma cópia do dicionário
    fromchaves() retorna dicionário com chaves e valores especificados
    get() retorna o valor relativo a chave dada, ou valor default dado
    items() retorna uma lista contendo uma tupla para cada par chave:valor
    chaves() retorna lista com as chaves do dicionário
    pop() remove o elemento relativo à chave especificada
    popitem() remove o último par chave:valor inserido
    setdefault() retorna o valor relativo à chave dada. Se a chave não existe insere chave:valor
    update() Atualiza o dicionário com pares chave:valor dados
    valors() retorna uma lista com os valores do dicionário

    Métodos de Arquivos (files)

    Método Descrição
    open(arquivo, modo) Abre o arquivo (descrição dos parâmetros abaixo)

    Os seguintes valores são válidos para modo.

    Parâmetro Descrição
    r para leitura – O ponteiro é colocado no início do arquivo. Default.
    r+ para leitura e gravação. O ponteiro fica no início do arquivo.
    w apenas para gravação. Substitui arquivo existente, cria novo se o arquivo não existir.
    w+ escrita e leitura. Substitui arquivo existente, cria novo se arquivo não existir.
    rb para leitura em formato binário. O ponteiro fica no início do arquivo.
    rb+ para leitura e escrita em formato binário.
    wb+ para escrita e leitura em formato binário. Substitui o arquivo existente. Cria novo se não existir.
    a para anexar. O ponteiro fica no final do arquivo, se o arquivo existir. Cria novo se existir.
    ab um arquivo para anexar em formato binário. O ponteiro fica no final do arquivo. Cria novo se não existir.
    a+ para anexar e ler. O ponteiro fica no final do arquivo, se arquivo existir. Cria novo se não existir.
    ab+ anexar e ler em formato binário. O ponteiro no final do arquivo. Cria novo se não existir.
    x cria novo arquivo lançando erro se já existir.
    Método Descrição
    close() fecha o arquivo; sem efeito se arq. já está fechado
    detach() retorna o fluxo bruto separado do buffer
    fileno() retorna número inteiro diferente para cada arquivo aberto
    flush() descarrega no disco as alterações no buffer
    isatty() retorna True se o fluxo de arquivo é interativo
    next(arquivo) itera sobre arquivo, lançando erro no final
    read() retorna o conteúdo do arquivo
    readable() retorna True se o fluxo do arquivo pode ser lido
    readline() retorna uma única linha do arquivo
    readlines() retorna lista com todas as linhas do arquivo
    search() localiza item no arquivo e retorna sua posição
    searchable() retorna True se o arquivo é pesquisável, por ex. com seek()
    tell() retorna a posição atual do arquivo
    truncate([tamanho]) redimensiona (truncando) o arquivo para um tamanho especificado
    writable() retorna True se o arquivo pode receber gravações
    write() grava a string especificada no arquivo
    writelines() escreve a sequencia no arquivo. Qualquer objeto iterável composto por strings pode ser usado

    Métodos de OS

    Método Descrição
    chdir(“novaPasta”) mudar a pasta ativa para novaPasta. Se novaPasta = “..” vai para pasta mãe
    getcwd() ler diretório (pasta) atual
    listdir(“pasta”) lista arquivos e subpastas de “pasta”
    makedirs(sequencia) criar pastas com nomes em sequencia
    mkdir(“nomeDaPasta”) criar uma pasta
    remove(arquivo) apagar arquivo
    removedirs(caminho) apagar pastas recursivamente
    rmdir(“pastaRemover”) apagar pasta “pastaRemover”. Não pode ser pasta atual ou estar em uso por ouro processo
    rename(“nomeAntigo”,”nomeNovo”) renomear pasta “nomeAntigo” para “nomeNovo”
    uname() retorna dados sobre o sistema operacional, usuário e máquina usada

    Exceções

    Todas as exceções no Python se derivam da classe base BaseException. As exceções built-in que não provocam uma queda do sistema são todas derivadas de Exception, incluive as definidas pelo usuário. A tabela abaixo mostra uma hierarquia de classes das exceções, cada indentação representando um grupo de derivadas da classe acima. Por exemplo, a classe ZeroDivisionError tem a seguinte linha hierárquica:

    BaseException ⟶ Exception ⟶ ArithmeticError ⟶ ZeroDivisionError

    BaseException
    SystemExit
    KeyboardInterrupt
    GeneratorExit
    Exception
    StopIteration
    StopAsyncIteration
    ArithmeticError
    FloatingPointError
    OverflowError
    ZeroDivisionError
    AssertionError
    AttributeError
    BufferError
    EOFError
    ImportError
    ModuleNotFoundError
    LookupError
    IndexError
    KeyError
    MemoryError
    NameError
    UnboundLocalError
    OSError
    BlockingIOError
    ChildProcessError
    ConnectionError
    BrokenPipeError
    ConnectionAbortedError
    ConnectionRefusedError
    ConnectionResetError
    FileExistsError
    FileNotFoundError
    InterruptedError
    IsADirectoryError
    NotADirectoryError
    PermissionError
    ProcessLookupError
    TimeoutError
    ReferenceError
    RuntimeError
    NotImplementedError
    RecursionError
    SyntaxError
    IndentationError
    TabError
    SystemError
    TypeError
    ValueError
    UnicodeError
    UnicodeDecodeError
    UnicodeEncodeError
    UnicodeTranslateError
    Warning
    DeprecationWarning
    PendingDeprecationWarning
    RuntimeWarning
    SyntaxWarning
    UserWarning
    FutureWarning
    ImportWarning
    UnicodeWarning
    BytesWarning
    ResourceWarning
    🔺Início do artigo

    Bibliografia

    Consulte a bibliografia no final do primeiro artigo dessa série.