Citado em http://www.geekinterview.com/question_details/64739 :
Vantagens da classe interna:
- Agrupamento lógico de classes : se uma classe é útil para apenas uma outra classe, então é lógico incorporá-la nessa classe e manter as duas juntas. Aninhar essas "classes auxiliares" torna seu pacote mais simplificado.
- Encapsulamento aumentado : Considere duas classes de nível superior A e B onde B precisa de acesso aos membros de A que de outra forma seriam declarados privados. Ao ocultar a classe B dentro da classe AA, os membros podem ser declarados privados e B pode acessá-los. Além disso, o próprio B pode ser escondido do mundo exterior.
- Código mais legível e sustentável : aninhar pequenas classes nas classes de nível superior coloca o código mais perto de onde ele é usado.
A principal vantagem é a organização. Qualquer coisa que pode ser realizada com classes internas pode ser realizada sem elas.
DataLoader
classe que pode lançar umaCacheMiss
exceção. Aninhar a exceção na classe principalDataLoader.CacheMiss
significa que você pode importar apenas,DataLoader
mas ainda usar a exceção.Não. Eles são absolutamente equivalentes a definir a classe normalmente no nível superior e, em seguida, copiar uma referência a ela na classe externa.
Não acho que haja qualquer razão especial para as classes aninhadas serem 'permitidas', exceto que também não faz nenhum sentido específico 'proibi-las' explicitamente.
Se você está procurando por uma classe que existe dentro do ciclo de vida do objeto externo / proprietário e sempre tem uma referência a uma instância da classe externa - classes internas como o Java faz - então as classes aninhadas do Python não são isso. Mas você pode hackear algo assim :
(Isso usa decoradores de classe, que são novos no Python 2.6 e 3.0. Caso contrário, você teria que dizer “Inner = innerclass (Inner)” após a definição da classe.)
fonte
self
sem nenhum trabalho extra necessário (apenas use um identificador diferente onde você normalmente colocaria o internoself
; comoinnerself
), e será capaz de acessar a instância externa por meio dele.WeakKeyDictionary
neste exemplo não permite que as chaves sejam coletadas como lixo, porque os valores fazem referência a suas respectivas chaves por meio de seusowner
atributos.Há algo em que você precisa entender para ser capaz de entender isso. Na maioria das linguagens, as definições de classe são diretivas para o compilador. Ou seja, a classe é criada antes que o programa seja executado. Em python, todas as instruções são executáveis. Isso significa que esta declaração:
é uma instrução que é executada em tempo de execução como esta:
Isso significa que você não só pode criar classes dentro de outras classes, como também pode criar classes em qualquer lugar que desejar. Considere este código:
Assim, a ideia de uma "classe interna" não é realmente uma construção de linguagem; é uma construção do programador. Guido tem um ótimo resumo de como isso aconteceu aqui . Mas, essencialmente, a ideia básica é que isso simplifica a gramática do idioma.
fonte
Aninhando classes dentro de classes:
As classes aninhadas aumentam a definição da classe, tornando mais difícil ver o que está acontecendo.
Classes aninhadas podem criar acoplamento que tornaria o teste mais difícil.
No Python, você pode colocar mais de uma classe em um arquivo / módulo, ao contrário do Java, então a classe ainda permanece perto da classe de nível superior e pode até ter o nome da classe prefixado com um "_" para ajudar a significar que outras não deveriam ser usando isso.
O lugar onde as classes aninhadas podem ser úteis é dentro das funções
A classe captura os valores da função permitindo que você crie dinamicamente uma classe como a metaprogramação de modelo em C ++
fonte
Eu entendo os argumentos contra classes aninhadas, mas há um caso para usá-los em algumas ocasiões. Imagine que estou criando uma classe de lista duplamente vinculada e preciso criar uma classe de nó para manter os nós. Tenho duas opções, criar a classe Node dentro da classe DoublyLinkedList ou criar a classe Node fora da classe DoublyLinkedList. Prefiro a primeira escolha neste caso, porque a classe Node só faz sentido dentro da classe DoublyLinkedList. Embora não haja nenhum benefício de ocultação / encapsulamento, há um benefício de agrupamento em poder dizer que a classe Node faz parte da classe DoublyLinkedList.
fonte
Node
classe não é útil para outros tipos de classes de lista encadeada que você também pode criar, caso em que provavelmente deve ser apenas externa.Node
está sob o namespace deDoublyLinkedList
, e faz sentido lógico ser assim. Isto é Pythônico: "Os namespaces são uma ótima ideia - vamos fazer mais disso!"Há algo que não pode ser feito facilmente sem : herança de classes relacionadas .
Aqui está um exemplo minimalista com as classes relacionadas
A
eB
:Este código leva a um comportamento bastante razoável e previsível:
Se
B
fosse uma classe de nível superior, você não poderia escreverself.B()
no método,make_B
mas simplesmente escreveriaB()
, e assim perderia a vinculação dinâmica para as classes adequadas.Observe que, nesta construção, você nunca deve se referir à classe
A
no corpo da classeB
. Essa é a motivação para a introdução doparent
atributo nas aulasB
.Obviamente, essa vinculação dinâmica pode ser recriada sem uma classe interna ao custo de uma instrumentação tediosa e sujeita a erros das classes.
fonte
O principal caso de uso para o qual uso isso é para evitar a proliferação de pequenos módulos e evitar a poluição do namespace quando módulos separados não são necessários. Se estou estendendo uma classe existente, mas essa classe existente deve fazer referência a outra subclasse que sempre deve ser acoplada a ela. Por exemplo, posso ter um
utils.py
módulo que contém muitas classes auxiliares, que não são necessariamente acopladas, mas quero reforçar o acoplamento para algumas dessas classes auxiliares. Por exemplo, quando implemento https://stackoverflow.com/a/8274307/2718295:
utils.py
:Então nós podemos:
Claro, poderíamos ter evitado herdar
json.JSONEnocder
completamente e apenas substituir default ()::
Mas às vezes, apenas por convenção, você deseja
utils
ser composto de classes para extensibilidade.Aqui está outro caso de uso: eu quero uma fábrica para mutáveis em minha OuterClass sem ter que invocar
copy
:Prefiro esse padrão ao
@staticmethod
invés do decorador que você usaria para uma função de fábrica.fonte
1. Duas maneiras funcionalmente equivalentes
As duas maneiras mostradas antes são funcionalmente idênticas. No entanto, existem algumas diferenças sutis e existem situações em que você gostaria de escolher um em vez do outro.
Modo 1: definição de classe aninhada
(= "classe aninhada")
Modo 2: com a classe interna de nível de módulo anexada à classe externa
(= "Classe interna referenciada")
O sublinhado é usado para seguir o PEP8 "interfaces internas (pacotes, módulos, classes, funções, atributos ou outros nomes) devem - ser prefixados com um único sublinhado inicial."
2. Semelhanças
O trecho de código abaixo demonstra as semelhanças funcionais da "Classe aninhada" vs "Classe interna referenciada"; Eles se comportariam da mesma maneira na verificação de código para o tipo de uma instância de classe interna. Escusado será dizer que o
m.inner.anymethod()
comportaria de forma semelhante comm1
em2
3. Diferenças
As diferenças de "Classe aninhada" e "Classe interna referenciada" estão listadas abaixo. Eles não são grandes, mas às vezes você gostaria de escolher um ou outro com base neles.
3.1 Encapsulamento de código
Com "Classes aninhadas" é possível encapsular o código melhor do que com "Classe interna referenciada". Uma classe no namespace do módulo é uma variável global . O objetivo das classes aninhadas é reduzir a confusão no módulo e colocar a classe interna dentro da classe externa.
Embora ninguém * esteja usando
from packagename import *
, uma quantidade baixa de variáveis de nível de módulo pode ser boa, por exemplo, ao usar um IDE com autocompletar de código / intellisense.* Certo?
3.2 Legibilidade do código
A documentação do Django instrui o uso da classe interna Meta para os metadados do modelo. É um pouco mais claro * instruir os usuários do framework a escrever um
class Foo(models.Model)
com o innerclass Meta
;em vez de "escreva a
class _Meta
, depois escreva aclass Foo(models.Model)
comMeta = _Meta
";Com a abordagem "Classe aninhada", o código pode ser lido como uma lista de marcadores aninhados , mas com o método "Classe interna referenciada" é necessário rolar para cima para ver a definição de
_Meta
para ver seus "itens filho" (atributos).O método "Classe interna referenciada" pode ser mais legível se o nível de aninhamento do código aumentar ou as linhas forem longas por algum outro motivo.
* Claro, uma questão de gosto
3.3 Mensagens de erro ligeiramente diferentes
Isso não é grande coisa, mas apenas para completar: ao acessar um atributo inexistente para a classe interna, vemos exceções ligeiramente diferentes. Continuando o exemplo dado na Seção 2:
Isso ocorre porque os
type
s das classes internas sãofonte
Eu usei as classes internas do Python para criar subclasses com erros deliberados dentro das funções de teste de unidade (ou seja, dentro
def test_something():
) a fim de chegar mais perto de 100% de cobertura de teste (por exemplo, o teste de declarações de log muito raramente acionadas substituindo alguns métodos).Em retrospecto, é semelhante à resposta de Ed https://stackoverflow.com/a/722036/1101109
Essas classes internas devem sair do escopo e estar prontas para a coleta de lixo depois que todas as referências a elas forem removidas. Por exemplo, pegue o seguinte
inner.py
arquivo:Recebo os seguintes resultados curiosos no OSX Python 2.7.6:
Dica - Não tente fazer isso com modelos Django, que pareciam manter outras referências (em cache?) Para minhas classes com erros.
Portanto, em geral, eu não recomendaria usar classes internas para esse tipo de propósito, a menos que você realmente valorize essa cobertura de teste de 100% e não possa usar outros métodos. Embora eu ache que é bom estar ciente de que, se você usar o
__subclasses__()
, ele às vezes pode ser poluído por classes internas. De qualquer forma, se você seguiu até aqui, acho que estamos muito envolvidos com Python neste ponto, com dunderscores privados e tudo.fonte
.__subclasses__()
para entender como as classes internas interagem com o coletor de lixo quando as coisas saem do escopo no Python. Isso parece dominar visualmente o post, então os primeiros 1-3 parágrafos merecem um pouco mais de expansão.