Classes

Prévio Próximo

Python Logo9. Classes

Em comparação com outras linguagens de programação, o mecanismo de classes de Python acrescenta classes com um mínimo de nova sintaxe e semântica. É uma mistura de mecanismos equivalentes encontrados em C ++ e Modula-3. Classes Python fornecem todas as características padrão de Programação Orientada a Objetos: o mecanismo de herança permite múltiplas classes base, uma classe derivada pode substituir os métodos de sua classe base ou classes, e um método pode chamar o método de uma classe base com o mesmo nome . Os objetos podem conter quantidades e tipos  arbitrários de dados. Como é verdadeiro para os módulos, classes participam da natureza dinâmica do Python: elas são criadas em tempo de execução, e podem ser modificadas ainda mais após a criação.
Na terminologia do C ++, normalmente os membros de classe (incluindo os membros de dados) são públicos (exceto veja abaixo Variáveis Privadas e Referências de Classe-local), e todas as funções de membro são virtual. Como em Modula-3, não há atalhos para referenciar membros do objeto de seus métodos: a função método é declarada com um primeiro argumento explícito representando o objeto, que é fornecido implicitamente pela chamada. Como em Smalltalk, as próprias classes são objetos. Isso proporciona semântica para importação e renomeação. Ao contrário de C ++ e Modula-3, tipos built-in podem ser usados como classes-base para extensão pelo usuário. Além disso, como em C ++, mais operadores internos com sintaxe especial (operadores aritméticos, subscripting etc.) podem ser redefinidos para instâncias de classe.
(Falta universalmente terminologia aceita para falar de classes, vou fazer uso ocasional de termos Smalltalk e C ++. Eu usaria termos Modula-3, uma vez que sua semântica orientada a objetos está mais próximo à de Python do que C ++, mas eu espero que poucos leitores tenham ouvido falar dela.)

9.1. Uma Palavra Sobre Nomes e Objetos

Os objetos têm individualidade, e vários nomes (em vários escopos) podem ser ligados ao mesmo objeto. Isto é conhecido como aliasing em outras linguagens. Isso geralmente não é apreciado em um primeiro olhar sobre Python, e pode ser ignorado quando se lida com tipos básicos imutáveis (números, cordas, linhas). No entanto, aliasing tem um efeito possivelmente surpreendente sobre a semântica do código Python envolvendo objetos mutáveis como listas, dicionários, e a maioria dos outros tipos. Isso geralmente é usado para o benefício do programa, uma vez que aliases comportam-se como ponteiros em alguns aspectos. Por exemplo, passando um objeto é barato, pois só o ponteiro é passado pela execução; e se uma função modifica um objeto passado como argumento, o caller vai ver a mudança – o que elimina a necessidade de dois mecanismos de passagem de parâmetros como em Pascal.

9.2. Escopos e Namespaces Python

Antes de introduzir classes, primeiro eu tenho que lhe dizer algo sobre regras de escopo Python. Definições de classe empregam alguns truques com namespaces, e você precisa saber como escopos e namespaces trabalhar para entender plenamente o que está acontecendo. Aliás, o conhecimento sobre o assunto é útil para qualquer programador avançado em Python.

Vamos começar com algumas definições.
Um namespace é um mapeamento entre nomes e objetos. A maioria dos namespaces são atualmente implementados como dicionários Python, mas normalmente não é perceptível de forma alguma (exceto para o desempenho), e isso pode mudar no futuro. Exemplos de namespaces são: o conjunto de  nomes built-in (funções contendo tais como abs(), e nomes de exceção built-in); os nomes globais em módulos; e nomes locais em uma chamada de função. Em certo sentido, o conjunto de atributos de um objeto também formam um namespace. A coisa importante a saber sobre namespaces é que não há absolutamente nenhuma relação entre nomes em diferentes namespaces; por exemplo, dois módulos podem definir uma função maximizar sem confusão – usuários dos módulos devem prefixar com o nome do módulo.
A propósito, eu utilizo a palavra attribute para qualquer nome depois de um ponto – por exemplo, na expressão z.real, real é um atributo do objeto z. A rigor, as referências a nomes de módulos são referências a atributos: na expressão modname.funcname, modname é um módulo objeto e funcname é um atributo do mesmo. Neste caso, não acontece de ser um mapeamento direto entre os atributos do módulo e os nomes globais definidos no módulo: eles compartilham o mesmo namespace! [1]
Os atributos podem ser graváveis ou somente para leitura. Neste último caso, a atribuição a atributos é possível. Atributos do módulo são graváveis: você pode escrever modname.the answer = 42. Atributos graváveis também podem ser excluídos com o comando del. Por exemplo, del modname.the_answer irá remover o atributo the_answer do objeto nomeado por modname.
Namespaces são criados em momentos diferentes e têm diferentes tempos de vida. O namespace contendo os nomes embutidos é criado quando o interpretador Python se inicia, e nunca é eliminado. O namespace global para um módulo é criado quando a definição de módulo é lido; normalmente, namespaces de módulo também duram até que o interpretador seja encerrado. Os comandos executados pela invocação de nível superior do interpretador, ou lendo a partir de um arquivo de script ou interativamente, são considerados parte de um módulo chamado __main__, então eles têm o seu próprio namespace global. (A nomes internos (built-in), na verdade, também vivem em um módulo, o que é chamado __builtin__.)
namespace local para uma função é criado quando a função é chamada, e excluído quando a função retorna ou levanta uma exceção que não é tratada dentro da função. (Na verdade, o esquecimento seria a melhor maneira de descrever o que realmente acontece.) Naturalmente, em invocações recursivas cada uma tem seu próprio namespace local.
Um escopo (scope) é uma região textual de um programa Python onde um namespace é diretamente acessível. “Diretamente acessível” significa que uma referência não qualificada a um nome tenta encontrar o nome no namespace.

Ainda que escopos sejam determinados estaticamente, eles são usados de forma dinâmica. A qualquer momento durante a execução, há pelo menos três escopos aninhados cujos namespaces são diretamente acessíveis:

  • o escopo interno, que é procurado em primeiro lugar, contém os nomes locais
  • os escopos de quaisquer funções que abrangem, as quais são pesquisadas começando com o escopo envolvente mais próxima, contém não-local, mas também nomes não-globais
  • o próximo ao último escopo contém nomes globais do módulo atual
  • o escopo externo (pesquisado por último) é o namespace que contém os nomes nativos
Se um nome fôr declarado global, em seguida, todas as referências e as atribuições vão diretamente para o scope do meio contendo nomes globais do módulo. Caso contrário, todas as variáveis encontradas fora do escopo interno são somente para leitura (uma tentativa de escrever a tal variável simplesmente cria uma nova variável local no escopo interno, deixando a variável externa de nome idêntico inalterado).
Normalmente, o escopo local referencia os nomes locais da (textualmente) função atual. Funções fora, o escopo local referencia os nomes do escopo global: namespace do módulo. Definições de classes ainda adicionam um outro namespace no escopo local.
É importante perceber que escopos são determinados textualmente: o escopo global de uma função definida em um módulo é namespace deste módulo, não importa de onde ou de que alias a função é chamada. Por outro lado, a efetiva busca de nomes é dinâmica, em tempo de execução – no entanto, a definição da linguagem está evoluindo para a resolução de nomes estática, em “compilar” o tempo, por isso não contar com a resolução de nomes dinâmico! (Na verdade, as variáveis locais já estão determinados estaticamente.)
Um detalhe especial é que Python – se nenhuma instrução global estiver em vigor – atribuições a nomes sempre vão para o escopo interno. Atribuições não copiam dados – eles só ligam nomes a objetos. O mesmo é verdadeiro para a exclusão: a declaração del x remove a ligação de x do namespace referenciado pelo escopo local. Na verdade, todas as operações que introduzem novos nomes usam o escopo local: em particular, declarações de importação e definições de função ligam o módulo ou nome da função no escopo local. (A declaração global pode ser usada para indicar que as variáveis particulares vivem no scopo global.)

9.3. Primeira Olhada em Classes

Classes introduzem um pouco de nova sintaxe, três novos tipos de objetos, e algumas novas semânticas.

9.3.1. Sintaxe de Definição de Classe

A forma mais simples de definição de classe se parece com isso:
class ClassName:
    <statement-1>
    .
    .
    .
    <statement-N>

As definições de classe, como definições de funções (comandos def) devem ser executados antes que tenham qualquer efeito. (Você pode conseguir colocar uma definição de classe em um galho de uma instrução if, ou dentro de uma função.)

Na prática, os comandos dentro de uma definição de classe geralmente serão definições de função, mas outras declarações são permitidas, e às vezes úteis – nós voltaremos a isso mais tarde. As definições de função dentro da classe têm uma forma peculiar de lista de argumentos, ditada pelas convenções de chamada para os métodos – de novo, isso é explicado mais tarde.

Quando uma definição de classe é inserida, um novo namespace é criado e usado como o escopo local – assim, todas as atribuições para as variáveis locais irão para este novo namespace. Em particular, as definições de funções ligam o nome da nova função aqui.
Quando uma definição de classe é deixada normalmente(via o final), um objeto de classe é criado. Este é basicamente um invólucro em torno do conteúdo do namespace criado pela definição de classe; vamos aprender mais sobre objetos de classe na próxima seção. O escopo local (aquele em vigor imediatamente antes da definição da classe foi digitado) é reintegrado, e o objeto classe é vinculado ao nome da classe dada no cabeçalho da definição de classe (ClassName no exemplo).

9.3.2. Objetos Classe

Objetos Classe suportam dois tipos de operações: referências a atributos e instanciação.

Atributo referências usam a sintaxe padrão utilizada para todas as referências de atributos em Python: obj.name. Nomes de atributos válidos são todos os nomes que estavam no namespace da classe quando o objeto classe foi criado. Assim, se a definição de classe ficou assim:

class MyClass:
    """A simple example class"""
    i = 12345
    def f(self):
        return 'hello world'

então MyClass.i e MyClass.f são referências a atributos válidos, retornando um inteiro e um objeto de função, respectivamente. Atributos de classe também pode ser atribuídos, para que você possa alterar o valor de MyClass.i por atribuição. __doc__ também é um atributo válido, que retorna a docstring pertencente à classe: “Um simples exemplo de classe”.

Instanciação de classe usa a notação de função. Basta fingir que o objeto de classe é uma função sem parâmetros e retorna uma nova instância da classe. Por exemplo, (assumindo classe acima):

x = MyClass()
cria uma nova instância da classe e atribue esse objeto para a variável x local.
A operação de instanciação (“vocação” um objeto de classe) cria um objeto vazio. Muitas classes gostam de criar objetos com instâncias customizadas para um estado inicial específico. Portanto, uma classe pode definir um método chamado __init __() especial, como este:
def __init__(self):
    self.data = []
Quando uma classe define um método __init __(), a instanciação de classe automaticamente invoca __init __() para a instância da classe recém-criada. Assim, neste exemplo, uma nova instância, inicializada pode ser obtida por:
x = MyClass()
Naturalmente, o método __init __ () pode ter argumentos para uma maior flexibilidade. Nesse caso, os argumentos passados para o operador de instanciação de classe são passados para __init __(). Por exemplo,
>>> class Complex:
...     def __init__(self, realpart, imagpart):
...         self.r = realpart
...         self.i = imagpart
...
>>> x = Complex(3.0, -4.5
)>>> x.r, x.i(3.0, -4.5)

9.3.3. Objetos de Instância

Agora, o que podemos fazer com instâncias? As únicas operações reconhecidas por instâncias são referências a atributos. Existem dois tipos de nomes de atributos válidos, atributos de dados e métodos.

Atributos de dados correspondem a “variáveis de instância” em Smalltalk, e aos “membros de dados” em C ++. Atributos de dados não precisam ser declarados; como variáveis locais, eles vêm à existência quando são atribuídos primeiramente. Por exemplo, se x é a instância da classe criada acima, o seguinte trecho de código irá imprimir o valor de 16, sem deixar vestígios:
x.counter = 1
while x.counter < 10:
    x.counter = x.counter * 2
print x.counter
del x.counter
O outro tipo de atributo de referência é um método. Um método é uma função que “pertence” a um objecto. (Em Python, o termo método não é exclusivo para instâncias de classe.:. Outros tipos de objetos podem ter métodos, assim, por exemplo, objetos de lista têm métodos chamados de appendinsertremovesort, e assim por diante. No entanto, na discussão a seguir, vamos usar o termo método exclusivamente para significar métodos de objetos de instância de classe, salvo indicação em contrário.)
Nomes de método válidos de um objeto instância depende da sua classe. Por definição, todos os atributos de uma classe que são objetos de função definem métodos correspondentes de suas instâncias. Assim, em nosso exemplo, xf é uma referência método válido, uma vez que MyClass.f é uma função, mas xi não é, uma vez que MyClass.i não é. Mas xf não é a mesma coisa que MyClass.f – é um objeto de método, não um objeto de função.

9.3.4. Objetos Método

Normalmente, um método é chamado logo após ser ligado (bound):
x.f()
No exemplo MyClass, esta será a string ‘Olá Mundo’. No entanto, não é necessário chamar o método imediatamente: xf é um objeto método, e pode ser armazenado e invocado em um momento posterior. Por exemplo:
xf = x.f
while True:
    print xf()

continuará a imprimir Hello World até o fim dos tempos.

O que acontece exatamente quando um método é chamado? Você deve ter notado que xf() foi chamado sem um argumento acima, mesmo que a definição da função de f() especificado um argumento. O que aconteceu com o argumento? Certamente Python levanta uma exceção quando uma função que requer um argumento é chamada sem qualquer – mesmo que o argumento não é realmente usado …
Na verdade, você já deve ter adivinhado a resposta: o que há de especial sobre os métodos é que o objeto é passado como o primeiro argumento da função. No nosso exemplo, a chamada xf() é exatamente equivalente a My Class.f (x). Em geral, chamando um método com uma lista de n argumentos é equivalente a chamar a função correspondente com uma lista de argumentos que é criada através da inserção do objeto do método antes do primeiro argumento.
Se você ainda não entendeu como métodos funcionam, um olhar para a implementação talvez possa esclarecer as coisas. Quando um atributo de instância é referenciado que não é um atributo de dados, sua classe é pesquisada. Se o nome indicar um atributo de classe válida que seja um objeto de função, um objeto método é criado por empacotamento (ponteiros para) o objeto de instância e o objeto de função apenas encontrados juntos em um objeto abstrato: este é o objeto método. Quando o método é chamado com uma lista de argumentos, uma nova lista de argumentos é criada a partir da instância e a lista de argumentos, e o objeto função é chamado com a nova lista de argumentos.

9.3.5. Class and Instance Variables

De um modo geral, as variáveis de instância são para dados exclusivos para cada uma das instâncias, e variáveis de classe são para atributos e métodos compartilhados por todas as instâncias da classe:
class Dog:

    kind = 'canine'         # class variable shared by all instances

    def __init__(self, name):
        self.name = name    # instance variable unique to each instance

>>> d = Dog('Fido')
>>> e = Dog('Buddy')
>>> d.kind                  # shared by all dogs
'canine'
>>> e.kind                  # shared by all dogs
'canine'
>>> d.name                  # unique to d
'Fido'
>>> e.name                  # unique to e
'Buddy'
Como discutido em Uma Palavra Sobre Nomes e Objetos, dados compartilhados podem ter possivelmente efeitos surpreendentes com objetos mutáveis envolventes, como listas e dicionários. Por exemplo, a lista tricks no código a seguir não deve ser usada como uma variável de classe, porque apenas uma única lista seria compartilhada por todas as instâncias Dog:
class Dog:

    tricks = []             # mistaken use of a class variable

    def __init__(self, name):
        self.name = name

    def add_trick(self, trick):
        self.tricks.append(trick)

>>> d = Dog('Fido')
>>> e = Dog('Buddy')
>>> d.add_trick('roll over')
>>> e.add_trick('play dead')
>>> d.tricks                # unexpectedly shared by all dogs
['roll over', 'play dead']
Concepção correta da classe deveria usar uma variável de instância em vez disso:
class Dog:

    def __init__(self, name):
        self.name = name
        self.tricks = []    # creates a new empty list for each dog

    def add_trick(self, trick):
        self.tricks.append(trick)

>>> d = Dog('Fido')
>>> e = Dog('Buddy')
>>> d.add_trick('roll over')
>>> e.add_trick('play dead')
>>> d.tricks
['roll over']
>>> e.tricks
['play dead']

9.4. Observações aleatórias

Atributos de dados substituem atributos de método com o mesmo nome; para evitar conflitos de nome acidentais, que podem causar bugs difíceis de encontrar em programas grandes, é aconselhável usar algum tipo de convenção que minimiza a possibilidade de conflitos. Convenções possíveis incluem capitalizando nomes de métodos, antepondo nomes de atributos de  dados com uma string única (talvez apenas um sublinhado), ou usando verbos para métodos e substantivos para atributos de dados.
Atributos de dados podem ser referenciados por meio de métodos, bem como por usuários comuns (“clientes”) de um objeto. Em outras palavras, classes não são utilizáveis para implementar tipos de dados abstractos puros. De fato, nada em Python torna possível assegurar o encapsulamento de dados – é tudo baseado em convenção. (Por outro lado, a implementação Python, escrita em C, pode esconder completamente detalhes de implementação e controle de acesso a um objeto, se necessário, o que pode ser usado por extensões para Python escritas em C.)
Clientes deveriam utilizar atributos de dados com cuidado – os clientes podem bagunçar invariantes mantidos pelos métodos por estampagem em seus atributos de dados. Observe que os clientes podem adicionar seus próprios atributos de dados a um objeto instanciado sem afetar a validade dos métodos, desde que sejam evitados conflitos de nome – mais uma vez, uma convenção de nomenclatura pode salvar um monte de dores de cabeça aqui.
Não há atalho para referenciar atributos de dados (ou outros métodos) de dentro de métodos. Acho que isso realmente aumenta a legibilidade dos métodos: não há nenhuma chance de confundir variáveis locais e variáveis de instância quando olhando através de um método.
Muitas vezes, o primeiro argumento de um método é chamado self. Isto nada mais é do que uma convenção: o nome self não tem absolutamente nenhum significado especial para Python. Note-se, no entanto, que por não seguir a convenção,  seu código pode ser menos legível para outros programadores Python, e também é concebível que um programa class browser pode ser escrito dependente de uma tal convenção.
Qualquer objeto função, que é um atributo de classe, define um método para ocorrências dessa classe. Não é necessário que a definição de função seja textualmente embutida na definição da classe: atribuição de uma função a um objeto variável local da classe é também ok. Por exemplo:
# Function defined outside the class
def f1(self, x, y):
    return min(x, x+y)

class C:
    f = f1
    def g(self):
        return 'hello world'
    h = g

Agora fg e h são todos os atributos da classe C que se referem a objetos-função, e, consequentemente, são todos os métodos de instâncias de C – h é equivalente a g. Note-se que esta prática geralmente só serve para confundir o leitor de um programa.

Métodos podem chamar outros métodos utilizando atributos método do argumento self:

class Bag:
    def __init__(self):
        self.data = []
    def add(self, x):
        self.data.append(x)
    def addtwice(self, x):
        self.add(x)
        self.add(x)

Métodos podem referenciar nomes globais da mesma forma como funções comuns. O escopo global associado a um método é o módulo que contém a sua definição. (A classe nunca é usado como um escopo global.) Embora raramente se encontra um bom motivo para o uso de dados globais em um método, há muitos usos legítimos do escopo global: Por um lado, as funções e módulos importados para o escopo global pode ser utilizado por métodos, bem como as funções e classes definidas no mesmo. Normalmente, a classe que contém o método é em si definida neste escopo global, e na próxima seção, vamos encontrar algumas boas razões pelas quais um método desejaria referenciar sua própria classe.

Cada valor é um objeto, e, portanto, tem uma classe (também chamado de seu tipo). Ele é armazenado como object.__ class__.

9.5. Herança

É claro que um recurso de linguagem não seria digna do nome “classe” sem o apoio de herança. A sintaxe para uma definição de classe derivada se parece com isso:
class DerivedClassName(BaseClassName):
    <statement-1>
    .
    .
    .
    <statement-N>
O nome BaseClassName deve ser definido em um escopo contendo a derivada definição de classe. No lugar de um nome de classe base, outras expressões arbitrárias também são permitidos. Isto pode ser útil, por exemplo, quando a classe de base é definida em outro módulo:
class DerivedClassName(modname.BaseClassName):
Execução de uma definição de classe derivada procede da mesma forma como para uma classe base. Quando o objeto de classe é construído, a classe base é lembrada. Isto é usado para resolver referências a atributos: Se um atributo requisitado não é encontrado na classe, a busca passa a olhar na classe base. Essa regra é aplicada recursivamente se a classe base é derivada de outra classe.
Não há nada de especial sobre instanciação de classes derivadas: DerivedClassName() cria uma nova instância da classe. Referências a métodos são resolvidos da seguinte forma: o atributo correspondente é procurado, através da cadeia de classes base, se necessário, e a referência de método é válido desde que produzam um objeto de função.
As classes derivadas podem substituir os métodos de suas classes base. Como os métodos não têm privilégios especiais ao chamar outros métodos do mesmo objeto, um método de uma classe base que invocava um outro método da mesma classe base pode acabar chamando um método de uma classe derivada que o substitui. (Para programadores C ++: todos os métodos em Python são efetivamente virtuais.)
Um método de substituição em uma classe derivada pode de fato desejar estender ao invés de simplesmente substituir o método da classe base do mesmo nome. Há uma maneira simples de chamar o método da classe base diretamente: é só chamar Base ClassName.methodname(self, arguments). Isso é ocasionalmente útil para os clientes também. (Note que isso só funciona se a classe base é acessível como BaseClassName no âmbito global.)

Python tem duas funções internas que trabalham com herança:

  • Use isinstance() para verificar o tipo de uma instância: isinstance(obj, int) será verdadeira apenas se obj.__ class__ fôr int ou alguma classe derivada de int.
  • Use issubclass() para verificar a herança de classe: issubclass(bool, int) é verdadeira, pois bool é uma subclasse de int. No entanto, issubclass(unicode, str) é falsa, já que unicode não é uma subclasse de str (eles só compartilham um ancestral comum, basestring).

9.5.1. Herança Múltipla

Python suporta uma forma limitada de herança múltipla também. A definição de classe com várias classes de base parece com isso:
class DerivedClassName(Base1, Base2, Base3):
    <statement-1>
    .
    .
    .
    <statement-N>
Para as classes de estilo antigo, a única regra é depth-first(profundidade primeiro), left-to-right (da esquerda para a direita). Assim, se um atributo não é encontrado em DerivedClassName, ele é procurado em Base1, então (recursivamente) nas classes de base de Base1, e somente se ele não for encontrado lá, ele é procurado em Base2, e assim por diante.
(Para algumas pessoas em geral – em buscando na Base2 e Base3 antes das classes de base de Base1 – parece mais natural. No entanto, isso exigiria que você soubesse se um determinado atributo de Base1 é realmente definido em Base1 ou em uma de suas classes base antes que você possa descobrir as consequências de um conflito de nome com um atributo de Base2. A regra em profundidade não faz diferenças entre os atributos diretos e herdados de Base1.)
Para as classes de estilo novo, o método de resolução muda dinamicamente para suportar chamadas de cooperativas para super(). Esta abordagem é conhecida em algumas outras linguagens de multiple-inheritance (múltipla herança) como call-next-méthod e é mais poderosa do que a chamada de super encontrados em linguagens de single-inheritance (herança simples).
Com classes de estilo novo, ordenação dinâmica é necessário porque todos os casos de herança múltipla exibem uma ou mais diamond relationships (relações tipo diamantes) (em que pelo menos uma das classes pai pode ser acessada através de vários caminhos da classe mais inferior). Por exemplo, todas as classes do estilo novo herdam do objeto, de modo que qualquer caso de herança múltipla fornece mais de um caminho para chegar ao objeto. Para manter as classes base de serem acessados mais de uma vez, o algoritmo dinâmico lineariza a ordem de pesquisa de uma forma que preserva a ordem da esquerda para a direita especificado em cada classe, que chama cada um dos pais apenas uma vez, e que é monotônica (o que significa que uma classe pode ser uma subclasse sem afetar a ordem de precedência de seus pais). Tomadas em conjunto, essas propriedades tornam possível criar classes confiáveis e expansíveis com herança múltipla. Para mais detalhes, consulte https://www.python.org/download/releases/2.3/mro/.

9.6. Variáveis Privadas e Referências de Class Local

Variáveis de instância “privada”, que não podem ser acessadas com exceção de dentro de um objeto, não existem em Python. No entanto, existe uma convenção que é seguido pela maioria de códigos Python: um nome prefixado com um sublinhado (ex: _spam) deve ser tratado como uma parte não-pública do API (se é uma função, um método ou um membro de dados) . Deve-se considerar um detalhe de implementação e sujeitos a alteração sem aviso prévio.
Uma vez que há um caso de uso válido para os membros privados de classe (ou seja, para evitar conflitos nominais de nomes com nomes definidos por subclasses), não há suporte limitado para um tal mecanismo, chamado de name mangling (desconfiguração de nome). Qualquer identificador da forma __spam (pelo menos dois sublinhados à esquerda, no máximo, um como sufixo) é textualmente substituído com __classname_spam, onde classname é o nome da classe atual com sublinhado inicial despojado. Esta desconfiguração ocorre sem ter em conta a posição sintática do identificador, contanto que ocorra dentro da definição de uma classe.
Desconfiguração do nome é útil para deixar subclasses substituirem métodos sem quebrar as chamadas de método intraclasse. Por exemplo:
class Mapping:
    def __init__(self, iterable):
        self.items_list = []
        self.__update(iterable)

    def update(self, iterable):
        for item in iterable:
            self.items_list.append(item)

    __update = update   # private copy of original update() method

class MappingSubclass(Mapping):

    def update(self, keys, values):
        # provides new signature for update()
        # but does not break __init__()
        for item in zip(keys, values):
            self.items_list.append(item)

Note-se que as regras de nomenclatura foram projetada para evitar acidentes; ainda é possível acessar ou modificar uma variável que é considerada privada. Isso pode até ser útil em circunstâncias especiais, como no depurador.

Observe que o código passado a execeval() ou execfile() não considera o classname da classe que chamou sendo a classe atual; isto é semelhante ao efeito da declaração global, o efeito dos quais, da mesma forma, é restrito ao código que é byte-compiled junto. A mesma restrição se aplica aos comandos getattr()setattr() e delattr(), bem como ao fazer referência a __dict__ diretamente.

9.7. Miusezas

Às vezes é útil ter um tipo de dados semelhante ao “record” Pascal ou C “struct“, agregando juntos alguns itens de dados nomeados. Uma definição de classe vazia vai fazer isso muito bem:
class Employee:
    pass

john = Employee() # Create an empty employee record

# Fill the fields of the record
john.name = 'John Doe'
john.dept = 'computer lab'
john.salary = 1000

Um pedaço de código Python que espera um tipo abstrato de dados em particular, pode sempre em troca ser passada uma classe que emula os métodos desse tipo de dados. Por exemplo, se você tem uma função que formata dados em um objeto de arquivo, você pode definir uma classe com métodos read() e readline() que obtêm os dados de um buffer de cadeia, e passá-la como um argumento.

Objetos do método da instância têm atributos, tambem: m.im_self é o objecto com o método m(), e m.im_func é o objeto de função correspondente ao método.

9.8. Exceções São Classes Tambem

Exceções definidas pelo usuário são identificadas por classes também. Através deste mecanismo, é possível criar hierarquias extensíveis de exceções.

Há duas novas formas válidas (semântica) para o comando raise:

raise Class, instance

raise instance
Na primeira forma, instance deve ser uma instância de classe ou de uma classe derivada dela. A segunda forma é um atalho para:
raise instance.__class__, instance
Uma classe em uma cláusula except é compatível com uma exceção se ela fôr da mesma classe ou de uma de suas classes base (mas não o contrário – uma cláusula except listando uma classe derivada não é compatível com uma classe base). Por exemplo, o seguinte será impresso B, C, D naquela ordem:
class B:
    passclass C(B):
    passclass D(C):
    pass

for c in [B, C, D]:
    try:
        raise c()
    except D:
        print "D"
    except C:
        print "C"
    except B:
        print "B"
Note que se as cláusulas except fossem invertidas (exceto B em primeiro lugar), seria impresso B, B, B – a primeira cláusula except achada seria acionada.
Quando uma mensagem de erro é impressa para uma exceção não tratada, o nome da classe de exceção é impresso e, em seguida dois pontos e um espaço, e, finalmente, a instância convertida em uma string usando a função interna str().

9.9. Iterators

Até agora você já deve ter notado que a maioria dos objetos de recipiente podem ser postos em loop usando uma instrução:
for element in [1, 2, 3]:
    print element
for element in (1, 2, 3):
    print element
for key in {'one':1, 'two':2}:
    print key
for char in "123":
    print char
for line in open("myfile.txt"):
    print line,
Este estilo de acesso é claro, conciso, e conveniente. O uso de iterators permeia e unifica Python. Nos bastidores, a instrução for chama iter() no objeto de recipiente. A função retorna um objecto iterator que define o método next() que acessa elementos em um recipiente de cada vez. Quando não há mais elementos, next() gera uma exceção StopIteration que manda o loop for terminar. Este exemplo mostra como tudo funciona:
>>> s = 'abc'
>>> it = iter(s)
>>> it
<iterator object at 0x00A1DB50>
>>> it.next()
'a'
>>> it.next()
'b'
>>> it.next()
'c'
>>> it.next()
Traceback (most recent call last):
  File "<stdin>", line 1, in ?
    it.next()
StopIteration
Depois de ter visto a mecânica por trás do protocolo iterator, é fácil adicionar comportamento iterador para suas classes. Defina um método __iter__() que retorne um objeto com um método next(). Se a classe definir next(), então __iter__() pode retornar apenas self:
class Reverse:
    """Iterator for looping over a sequence backwards."""
    def __init__(self, data):
        self.data = data
        self.index = len(data)
    def __iter__(self):
        return self
    def next(self):
        if self.index == 0:
            raise StopIteration
        self.index = self.index - 1
        return self.data[self.index]
>>> rev = Reverse('spam')
>>> iter(rev)
<__main__.Reverse object at 0x00A1DB50>
>>> for char in rev:
...     print char
...
m
a
p
s

9.10. Geradores

Os geradores são uma ferramenta simples e poderosa para criar iterators. Eles são escritos como funções regulares, mas usam a declaração de yield sempre que eles queiram retornar dados. Cada vez que next()  é chamada sobre ele, o gerador de retoma de onde parou (ele se lembra de todos os valores de dados e em que declaração foi executada na última vez). Um exemplo mostra como os geradores pode ser tão fácil de criar:
def reverse(data):
    for index in range(len(data)-1, -1, -1):
        yield data[index]
>>> for char in reverse('golf'):
...     print char
...
f
l
o
g
Tudo o que pode ser feito com geradores também pode ser feito com iterators de classe base, como descrito na secção anterior. O que faz geradores tão compactos é que os métodos __iter()__ e next() são criados automaticamente.

Outra característica fundamental é que as variáveis locais e estado de execução são salvos automaticamente entre as chamadas. Isso fez a função mais fácil de se escrever e muito mais claro do que uma abordagem utilizando variáveis de instância como self.index e self.data.Além da criação método automático e salvar o estado do programa, quando geradores terminam, eles levantam automaticamente StopIteration. Em conjunto, estas características tornam mais fácil criar iterators sem mais esforço do que escrever uma função regular.

9.11. Expressões de Geradores

Alguns geradores simples podem ser codificados de forma sucinta como expressões usando uma sintaxe semelhante à compreensões sde lista, mas com parênteses em vez de colchetes. Estas expressões são projetadas para situações em que o gerador é usado imediatamente por uma função de inclusão. Gerador de expressões são mais compactos, mas menos versáteis do que as definições do gerador completos e tendem a ser mais amigáveis à memória do que  compreensões de lista equivalentes.
Exemplos:
>>> sum(i*i for i in range(10))                 # sum of squares
285

>>> xvec = [10, 20, 30]
>>> yvec = [7, 5, 3]
>>> sum(x*y for x,y in zip(xvec, yvec))         # dot product
260

>>> from math import pi, sin
>>> sine_table = dict((x, sin(x*pi/180)) for x in range(0, 91))

>>> unique_words = set(word  for line in page  for word in line.split())

>>> valedictorian = max((student.gpa, student.name) for student in graduates)

>>> data = 'golf'
>>> list(data[i] for i in range(len(data)-1,-1,-1))
['f', 'l', 'o', 'g']
Notas de Rodapé
[1] Exceto por uma coisa. Objetos Módulo têm um de leitura atributo readonly secreto chamado __dict__ que retorna o dicionário utilizado para implementar o namespace do módulo; o nome __dict__ é um atributo mas não um nome global. Obviamente, utilizar isto violaria a abstração da implementação namespace, e deve ser restrito a coisas como depuradores post-mortem.
Prévio Próximo