Por que as referências de atributo agem assim com a herança do Python? [duplicado

StackOverflow https://stackoverflow.com/questions/206734

  •  03-07-2019
  •  | 
  •  

Pergunta

Esta pergunta já tem uma resposta aqui:

A seguir, parece estranho .. basicamente, o atributo Somedata parece compartilhado entre todas as classes que herdaram de the_base_class.

class the_base_class:
    somedata = {}
    somedata['was_false_in_base'] = False


class subclassthing(the_base_class):
    def __init__(self):
            print self.somedata


first = subclassthing()
{'was_false_in_base': False}
first.somedata['was_false_in_base'] = True
second = subclassthing()
{'was_false_in_base': True}
>>> del first
>>> del second
>>> third = subclassthing()
{'was_false_in_base': True}

Definindo self.somedata no __init__ A função é obviamente a maneira correta de contornar isso (então cada classe tem sua própria somedata dito) - mas quando esse comportamento é desejável?

Foi útil?

Solução

Você está certo, somedata é compartilhado entre todas as instâncias da classe e suas subclasses, porque é criada na aula definição Tempo. As linhas

somedata = {}
somedata['was_false_in_base'] = False

são executados quando a classe é definida, ou seja, quando o intérprete encontra o class declaração - não Quando a instância for criada (pense em blocos de inicializador estático em Java). Se um atributo não existir em uma instância de classe, o objeto de classe será verificado para o atributo.

No horário da definição da classe, você pode executar o código Arbritrary, como este:

 import sys
 class Test(object):
     if sys.platform == "linux2":
         def hello(self):
              print "Hello Linux"
     else:
         def hello(self):
              print "Hello ~Linux"

Em um sistema Linux, Test().hello() irá imprimir Hello Linux, em todos os outros sistemas, a outra string será impressa.

Construta, objetos em __init__ são criados em Instanciação tempo e pertencem apenas à instância (quando eles são atribuídos a self):

class Test(object):
    def __init__(self):
        self.inst_var = [1, 2, 3]

Objetos definidos em um objeto de classe em vez de instância podem ser úteis em muitos casos. Por exemplo, você pode querer armazenar em cache as instâncias da sua classe, para que as instâncias com os mesmos valores de membro possam ser compartilhadas (supondo que elas sejam imutáveis):

class SomeClass(object):
    __instances__ = {}

    def __new__(cls, v1, v2, v3):
        try:
            return cls.__insts__[(v1, v2, v3)]
        except KeyError:
            return cls.__insts__.setdefault(
               (v1, v2, v3), 
               object.__new__(cls, v1, v2, v3))

Principalmente, uso dados em corpos de classe em conjunto com metaclasses ou métodos de fábrica genérica.

Outras dicas

Observe que parte do comportamento que você está vendo é devido a somedata Começar um dict, em oposição a um tipo de dados simples, como um bool.

Por exemplo, veja este exemplo diferente que se comporta de maneira diferente (embora muito semelhante):

class the_base_class:
    somedata = False

class subclassthing(the_base_class):
    def __init__(self):
        print self.somedata


>>> first = subclassthing()
False
>>> first.somedata = True
>>> print first.somedata
True
>>> second = subclassthing()
False
>>> print first.somedata
True
>>> del first
>>> del second
>>> third = subclassthing()
False

A razão pela qual este exemplo se comporta de maneira diferente daquele dado na questão é porque aqui first.somedata está recebendo um novo valor (o objeto True), enquanto no primeiro exemplo o objeto ditado referenciado por first.somedata (e também pelas outras instâncias da subclasse) está sendo modificado.

Veja o comentário de Torsten Marek para esta resposta para obter mais esclarecimentos.

Eu acho que a maneira mais fácil de entender isso (para que você possa prever o comportamento) é perceber que o seu somedata é um atributo da classe e não a instância dessa classe, se você a definir dessa maneira.

Existe realmente apenas um somedata O tempo todo, porque no seu exemplo você não atribuiu a esse nome, mas o usou para procurar um ditado e depois atribuir um item (chave, valor) a ele. É um Gotcha que é uma conseqüência de como o intérprete Python funciona e pode ser confuso a princípio.

Licenciado em: CC-BY-SA com atribuição
Não afiliado a StackOverflow
scroll top