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 sua escolha mostra as propriedades do preenchimento automático.
Pessoalmente, não gosto do último, porque não faz sentido que uma classe tenha essas propriedades definidas None
.
Qual seria a melhor prática e por que razões?
__init__
já fornece preenchimento automático etc. Além disso, o usoNone
evita que o IDE deduza um tipo melhor para o atributo, por isso é melhor usar um padrão sensato (quando possível).typing
módulo, que permitem fornecer dicas para o IDE e linter, se esse tipo de coisa lhe apeteça ...self
. Mesmo seself.name
ouself.age
não foram atribuídos em__init__
que não iria aparecer na instânciaself
, eles só aparecem na classePerson
.Respostas:
Eu chamo a última prática ruim sob a regra "isso não faz o que você pensa que faz".
A posição do seu colega de trabalho pode ser reescrita como: "Vou criar um monte de variáveis quase globais estáticas de classe que nunca são acessadas, mas que ocupam espaço nas tabelas de namespace das várias classes (
__dict__
), apenas para fazer meu IDE funcionar alguma coisa."fonte
-OO
, para os poucos que precisam dela.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 (também pode ser você mesmo no próximo ano).
Não conheço regras rígidas, mas prefiro que qualquer estado de instância futuro seja claramente declarado. Bater com um já
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 chamadas que levam ao atributo a ser atribuído pode facilmente se tornar não trivial, levando a erros.Portanto, eu geralmente não apenas defino tudo no construtor, mas também me esforço para manter o número mínimo de atributos mutáveis.
2. Não misture membros de classe e instância
Qualquer coisa que você definir dentro da
class
declaração 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 igual para todas as instâncias. O mesmo se aplica aos membros de dados. Isso é totalmente diferente dos atributos de instância nos quais você geralmente define__init__
.Membros de dados em nível de classe são mais úteis como constantes:
fonte
self
e não precisamself
ser passados, enquanto os métodos no nível da classe são ilimitados e são funções simples, como visto nodef
momento, e aceitam uma instância como primeiro argumento. Então, essas são coisas diferentes.AttributeError
é um bom sinal do que um bug. Caso contrário, você engoliria o None e obteria resultados sem sentido. Especialmente importante no caso em que os atributos são definidos no diretório__init__
, portanto, um atributo ausente (mas existente no nível da classe) só pode ser causado por herança de buggy.None
e esse valor não fizer sentido no momento da construção da instância, você terá um problema em sua arquitetura e precisará repensar o ciclo de vida do valor do atributo ou seu valor inicial. Observe que, definindo seus atributos antecipadamente, você pode detectar esse problema antes mesmo de escrever o restante da classe, sem falar em executar o código.Pessoalmente, defino os membros no método __ init __ (). Eu nunca pensei em defini-los na parte da aula. Mas o que eu sempre faço: inicio todos os membros no método __ init__, mesmo aqueles que não são necessários no método __ init__.
Exemplo:
Eu acho que é importante definir todos os membros em um só lugar. Isso torna o código mais legível. Se é dentro de __ init __ () ou fora, não é tã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 adiciono o prefixo "_" às variáveis de membro.
fonte
_
: Para indicar que é privado! (Por que tantas pessoas neste segmento confuso ou meio confuso Python com outras línguas?)Esta é uma má prática. Você não precisa desses valores, eles confundem o código e podem causar erros.
Considerar:
fonte
Eu irei com "um pouco como as doutrinas, então" e declararei isso inofensivo, desde que seja sempre
None
, ou uma faixa estreita de outros valores, todos imutáveis.Cheira a atavismo e apego excessivo a linguagens estaticamente tipadas. E isso não serve como código. Mas tem um objetivo menor que permanece na documentação.
Ele documenta quais são os nomes esperados; portanto, se eu combinar código com alguém e um de nós tiver 'username' e o outro user_name, haverá 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 consegue a mesma coisa de uma maneira mais pitônica, mas se houver um código real no
__init__
, isso fornece um local mais claro para documentar as variáveis em uso.Obviamente, o grande problema aqui é que ele tenta que as pessoas inicializem com valores diferentes de
None
, o que pode ser ruim:deixa um rastreio global e não cria uma instância para x.
Mas isso é mais uma peculiaridade no Python como um todo do que nesta prática, e já deveríamos estar paranóicos com relação a isso.
fonte