É uma boa prática declarar variáveis de instância como None em uma classe em Python?

56

Considere a seguinte classe:

class Person:
    def __init__(self, name, age):
        self.name = name
        self.age = age

Meus colegas de trabalho tendem a defini-lo assim:

class Person:
    name = None
    age = None

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

A principal razão para isso é que o editor de escolha mostra as propriedades de autocompletar.

Pessoalmente, eu não gosto do último, porque não faz sentido que uma classe tenha essas propriedades definidas como None .

Qual seria a melhor prática e por quais motivos?

    
por Remco Haszing 27.08.2014 / 13:08
fonte

5 respostas

60

Eu chamo a última prática ruim sob a regra "isso não faz o que você pensa que faz".

A posição de seu colega de trabalho pode ser reescrita como: "Vou criar um monte de variáveis quase globais de classe estática que nunca são acessadas, mas que ocupam espaço nas várias tabelas de namespace da classe ( __dict__ ), apenas para fazer meu IDE fazer algo. "

    
por 27.08.2014 / 16:53
fonte
23

1. Torne seu código fácil de entender

O código é lido com muito mais frequência do que escrito. Facilite a tarefa do seu mantenedor de código (pode ser você mesmo no ano que vem).

Não conheço regras rígidas, mas prefiro ter qualquer estado de instância futuro declarado claramente. Bater com um AttributeError é ruim o suficiente. Não ver claramente o ciclo de vida de um atributo de instância é pior. A quantidade de ginástica mental necessária para restaurar possíveis sequências de chamada que levam ao atributo atribuído pode facilmente tornar-se não trivial, levando a erros.

Então, eu geralmente não apenas defino tudo no construtor, mas também me esforço para manter o número de atributos mutáveis no mínimo.

2. Não misture membros no nível da classe e no nível da instância

Qualquer coisa que você defina dentro da declaração class pertence à classe e é compartilhada por todas as instâncias da classe. Por exemplo. quando você define uma função dentro de uma classe, ela se torna um método que é o mesmo para todas as instâncias. O mesmo se aplica aos membros de dados. Isso é totalmente diferente dos atributos de instância que você geralmente define em __init__ .

Os membros de dados em nível de classe são mais úteis como constantes:

class Missile(object):
  MAX_SPEED = 100  # all missiles accelerate up to this speed
  ACCELERATION = 5  # rate of acceleration per game frame

  def move(self):
    self.speed += self.ACCELERATION
    if self.speed > self.MAX_SPEED:
      self.speed = self.MAX_SPEED
    # ...
    
por 27.08.2014 / 17:14
fonte
14

Pessoalmente, eu defino os membros no método __ init __ (). Eu nunca pensei em defini-los na parte da aula. Mas o que eu sempre faço: eu inicio todos os membros no método init, mesmo aqueles que não são necessários no método init.

Exemplo:

class Person:
    def __init__(self, name, age):
        self._name = name
        self._age = age
        self._selected = None

   def setSelected(self, value):
        self._selected = value

Acho importante definir todos os membros em um só lugar. Isso torna o código mais legível. Se está dentro de __ init __ () ou fora, isso não é importante. Mas é importante que uma equipe se comprometa com mais ou menos o mesmo estilo de codificação.

Ah, e você pode notar que eu já adicionei o prefixo "_" às variáveis dos membros.

    
por 27.08.2014 / 14:08
fonte
10

Esta é uma prática ruim. Você não precisa desses valores, eles entopem o código e podem causar erros.

Considere:

>>> class WithNone:
...   x = None
...   y = None
...   def __init__(self, x, y):
...     self.x = x
...     self.y = y
... 
>>> class InitOnly:
...   def __init__(self, x, y):
...     self.x = x
...     self.y = y
... 
>>> wn = WithNone(1,2)
>>> wn.x
1
>>> WithNone.x #Note that it returns none, no error
>>> io = InitOnly(1,2)
>>> InitOnly.x
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
AttributeError: class InitOnly has no attribute 'x'
    
por 27.08.2014 / 16:38
fonte
0

Eu irei com "um pouco como docstrings, então" e declaro isso inofensivo, contanto que seja sempre None , ou uma faixa estreita de outros valores, todos imutáveis.

Tem gosto de atavismo e apego excessivo a idiomas estaticamente tipados. E isso não é bom como código. Mas tem um propósito menor que permanece, na documentação.

Ele documenta quais são os nomes esperados, então se eu combinar código com alguém e um de nós tiver 'username' e o outro user_name, existe uma pista para os humanos de que nos separamos e não estamos usando as mesmas variáveis .

Forçar a inicialização completa como uma política atinge a mesma coisa de uma maneira mais Pythonica, mas se houver código real no __init__ , isso fornece um local mais claro para documentar as variáveis em uso.

Obviamente, o grande problema aqui é que as pessoas tentam inicializar com valores diferentes de None , o que pode ser ruim:

class X:
    v = {}
x = X()
x.v[1] = 2

deixa um rastreio global e não cria uma instância para x.

Mas isso é mais um capricho no Python como um todo do que nessa prática, e já deveríamos estar paranoicos sobre isso.

    
por 27.08.2014 / 20:44
fonte

Tags