Existe alguma distinção significativa entre:
class A(object):
foo = 5 # some default value
vs.
class B(object):
def __init__(self, foo=5):
self.foo = foo
Se você estiver criando muitas instâncias, há alguma diferença nos requisitos de desempenho ou espaço para os dois estilos? Ao ler o código, você considera o significado dos dois estilos significativamente diferentes?
python
attributes
member-variables
Dan Homerick
fonte
fonte
Respostas:
Além das considerações de desempenho, há uma diferença semântica significativa . No caso do atributo de classe, há apenas um objeto referido. Na instância-atributo-definido-na-instanciação, pode haver vários objetos referidos. Por exemplo
fonte
int
estr
eles ainda anexados a cada instância, em vez de classe.int
e tambémstr
são compartilhados, exatamente da mesma maneira. Você pode verificar isso facilmente com ou . Ou apenas procure nas instâncias e nas classes . Geralmente, não importa muito se tipos imutáveis são compartilhados ou não.is
id
__dict__
__dict__
a.foo = 5
, em ambos os casos você verá ob.foo
retorno[]
. Isso ocorre porque, no primeiro caso, você está substituindo o atributo de classea.foo
por um novo atributo de instância com o mesmo nome.A diferença é que o atributo na classe é compartilhado por todas as instâncias. O atributo em uma instância é exclusivo para essa instância.
Se provenientes do C ++, os atributos na classe são mais parecidos com variáveis de membro estáticas.
fonte
>>> class A(object): foo = 5
>>> a, b = A(), A()
>>> a.foo = 10
>>> b.foo
5
a.foo.append(5)
está mudando o valor a quea.foo
se refere, enquantoa.foo = 5
está transformandoa.foo
em um novo nome para o valor5
. Então, você acaba com um atributo de instância que oculta o atributo de classe. Tente o mesmoa.foo = 5
na versão de Alex e você verá queb.foo
não é alterado.Aqui está um post muito bom , e resuma-o como abaixo.
E na forma visual
Atribuição de atributo de classe
Se um atributo de classe for definido acessando a classe, ele substituirá o valor para todas as instâncias
Se uma variável de classe for configurada acessando uma instância, ela substituirá o valor somente para essa instância . Substitui essencialmente a variável de classe e a transforma em uma variável de instância disponível, intuitivamente, apenas para essa instância .
Quando você usaria o atributo class?
Armazenando constantes . Como os atributos da classe podem ser acessados como atributos da própria classe, geralmente é bom usá-los para armazenar constantes em toda a classe e específicas da classe
Definindo valores padrão . Como um exemplo trivial, podemos criar uma lista limitada (ou seja, uma lista que pode conter apenas um determinado número de elementos ou menos) e optar por ter um limite padrão de 10 itens
fonte
Como as pessoas nos comentários aqui e em duas outras perguntas marcadas como duplas parecem estar confusas sobre isso da mesma maneira, acho que vale a pena adicionar uma resposta adicional sobre a de Alex Coventry .
O fato de Alex estar atribuindo um valor de um tipo mutável, como uma lista, não tem nada a ver com o fato de as coisas serem compartilhadas ou não. Podemos ver isso com a
id
função ou ois
operador:(Se você está se perguntando por que eu usei, em
object()
vez de, digamos,5
isso, é para evitar dois outros problemas que eu não quero abordar aqui; por duas razões diferentes, s totalmente criados separadamente5
podem acabar sendo o mesma instância do número5
. Mas s totalmente criados separadamenteobject()
não podem.)Então, por que isso
a.foo.append(5)
afeta no exemplo de Alexb.foo
, masa.foo = 5
no meu exemplo não? Bem, tentea.foo = 5
no exemplo de Alex, e aviso de que ela não afetab.foo
não quer .a.foo = 5
está apenas criandoa.foo
um nome para5
. Isso não afetab.foo
, ou qualquer outro nome para o valor antigo quea.foo
costumava se referir. * É um pouco complicado criar um atributo de instância que oculte um atributo de classe **, mas quando você obtém isso, nada é complicado. acontecendo aqui.Espero que agora seja óbvio porque Alex usou uma lista: o fato de você poder alterar uma lista significa que é mais fácil mostrar que duas variáveis denominam a mesma lista e também significa que é mais importante no código da vida real saber se você tem duas listas ou dois nomes para a mesma lista.
* A confusão para pessoas provenientes de uma linguagem como C ++ é que, no Python, os valores não são armazenados em variáveis. Os valores residem em áreas de valor, por si só, variáveis são apenas nomes de valores e a atribuição apenas cria um novo nome para um valor. Se ajudar, pense em cada variável Python como uma em
shared_ptr<T>
vez de aT
.** Algumas pessoas aproveitam isso usando um atributo de classe como um "valor padrão" para um atributo de instância que as instâncias podem ou não definir. Isso pode ser útil em alguns casos, mas também pode ser confuso, portanto, tenha cuidado com isso.
fonte
Há mais uma situação.
Os atributos de classe e instância são Descritor .
Acima irá produzir:
O mesmo tipo de acesso à instância por classe ou instância retorna resultados diferentes!
E eu encontrei na definição c.PyObject_GenericGetAttr, e um ótimo post .
Explicar
fonte