È buona norma dichiarare variabili di istanza come None in una classe in Python?

56

Considera la seguente classe:

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

I miei colleghi tendono a definirlo in questo modo:

class Person:
    name = None
    age = None

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

La ragione principale di ciò è che il loro editor di scelta mostra le proprietà per il completamento automatico.

Personalmente, non mi piace quest'ultimo, perché non ha senso che una classe abbia quelle proprietà impostate su None .

Quale sarebbe la migliore pratica e per quali ragioni?

    
posta Remco Haszing 27.08.2014 - 13:08
fonte

5 risposte

60

Io chiamo quest'ultima cattiva pratica sotto la regola "questo non fa quello che pensi che faccia".

La posizione del tuo collega può essere riscritta come: "Creerò un sacco di variabili quasi globali statiche di classe a cui non si accede mai, ma che occupano spazio nelle tabelle dello spazio dei nomi di varie classi ( __dict__ ), solo per rendere il mio IDE fare qualcosa. "

    
risposta data 27.08.2014 - 16:53
fonte
23

1. Rendi il tuo codice facile da capire

Il codice viene letto molto più spesso di quanto scritto. Rendi più semplice il compito del manutentore del codice (potrebbe essere anche tu l'anno prossimo).

Non conosco regole rigide, ma preferisco che ogni stato di istanza futuro sia dichiarato chiaramente definitivo. Crash con AttributeError è già abbastanza grave. Non vedendo chiaramente il ciclo di vita di un attributo di istanza è peggio. La quantità di ginnastica mentale richiesta per ripristinare possibili sequenze di chiamate che portano all'attributo assegnato può facilmente diventare non banale, portando a errori.

Quindi di solito non solo definisco tutto nel costruttore, ma cerco anche di mantenere il numero di attributi mutabili al minimo.

2. Non unire membri di livello di classe e livello di istanza

Tutto ciò che si definisce proprio all'interno della dichiarazione class appartiene alla classe ed è condiviso da tutte le istanze della classe. Per esempio. quando definisci una funzione all'interno di una classe, diventa un metodo che è lo stesso per tutte le istanze. Lo stesso vale per i membri dei dati. Questo è totalmente diverso dagli attributi di istanza che di solito definisci in __init__ .

I membri di dati di livello di classe sono più utili come costanti:

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
    # ...
    
risposta data 27.08.2014 - 17:14
fonte
14

Personalmente definisco i membri nel metodo __ init __ (). Non ho mai pensato di definirli nella parte di classe. Ma quello che faccio sempre: avvio tutti i membri nel metodo __ init__, anche quelli non necessari nel metodo __ init__.

Esempio:

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

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

Penso che sia importante definire tutti i membri in un unico posto. Rende il codice più leggibile. Che sia all'interno __ init __ () o all'esterno, non è così importante. Ma è importante per una squadra impegnarsi più o meno nello stesso stile di codifica.

Oh, e potresti notare che aggiungo sempre il prefisso "_" alle variabili membro.

    
risposta data 27.08.2014 - 14:08
fonte
10

Questa è una cattiva pratica. Non hai bisogno di questi valori, ingombrano il codice e possono causare errori.

Si consideri:

>>> 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'
    
risposta data 27.08.2014 - 16:38
fonte
0

Vado con "un po 'come docstring, quindi" e dichiararlo innocuo, purché sia sempre None , o un ristretto intervallo di altri valori, tutti immutabili.

Ha un atavismo e un eccessivo attaccamento alle lingue tipizzate in modo statico. E non va bene come codice. Ma ha uno scopo minore che rimane, nella documentazione.

Documenta quali sono i nomi attesi, quindi se unisco il codice con qualcuno e uno di noi ha 'username' e l'altro user_name, c'è un indizio per gli umani che abbiamo separato i modi e non stiamo usando le stesse variabili .

Forzare l'inizializzazione completa come criterio ottiene la stessa cosa in un modo più pitone, ma se c'è un codice effettivo in __init__ , questo fornisce un luogo più chiaro per documentare le variabili in uso.

Ovviamente il GRANDE problema qui è che induce le persone a inizializzare con valori diversi da None , il che può essere negativo:

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

lascia una traccia globale e non crea un'istanza per x.

Ma è più una stranezza in Python nel suo complesso che in questa pratica, e dovremmo essere paranoici al riguardo.

    
risposta data 27.08.2014 - 20:44
fonte

Leggi altre domande sui tag