2009-08-09 11 views
12

Sono interessato ad ascoltare alcune discussioni sugli attributi di classe in Python. Ad esempio, qual è un buon caso per gli attributi di classe? Per la maggior parte, non riesco a trovare un caso in cui un attributo di classe sia preferibile all'utilizzo di un attributo di livello modulo. Se questo è vero, allora perché averli in giro?Attributi Python Class vs. Module

Il problema che ho con loro, è che è quasi troppo facile da clobare un valore di attributo di classe per errore, e quindi il valore "globale" è diventato un attributo di istanza locale.

Sentitevi liberi di commentare su come si dovrebbe gestire le seguenti situazioni:

  1. valori costanti utilizzati da una classe e/o sottoclassi. Questo può includere chiavi del dizionario "numero magico" o indici di lista che non cambieranno mai, ma è possibile che sia necessaria l'inizializzazione una tantum.
  2. Attributo predefinito della classe, che in rare occasioni è stato aggiornato per un'istanza speciale della classe.
  3. Struttura dati globale utilizzata per rappresentare uno stato interno di una classe condivisa tra tutte le istanze.
  4. Una classe che inizializza un numero di attributi predefiniti, non influenzato dagli argomenti del costruttore.

Alcune Related Posts:
Difference Between Class and Instance Attributes

+0

deve essere community wiki – SilentGhost

risposta

7

# 4: ho mai classe utilizzare gli attributi per inizializzare istanza predefinita gli attributi (quelli che normalmente mettono in __init__). Per esempio:

class Obj(object): 
    def __init__(self): 
     self.users = 0 

e mai:

class Obj(object): 
    users = 0 

Perché? Perché è incoerente: non fa quello che si vuole quando si assegna nulla, ma un oggetto invariante:

class Obj(object): 
    users = [] 

provoca lista per essere condivisi tra tutti gli oggetti, che in questo caso non è voluto gli utenti. È confuso dividerli in attributi di classe e assegnazioni in __init__ a seconda del loro tipo, quindi li metto sempre tutti in __init__, che trovo comunque più chiaro.


Come per il resto, generalmente inserisco valori specifici per classe all'interno della classe.Questo non è tanto perché i globals sono "cattivi" - non sono un affare così grande come in alcune lingue, perché sono ancora focalizzati sul modulo, a meno che il modulo stesso non sia troppo grande - ma se il codice esterno vuole accedervi, è utile avere tutti i valori rilevanti in un unico posto. Ad esempio, in module.py:

class Obj(object): 
    class Exception(Exception): pass 
    ... 

e quindi:

from module import Obj 

try: 
    o = Obj() 
    o.go() 
except o.Exception: 
    print "error" 

Oltre a permettere sottoclassi per modificare il valore (che non è sempre voluto comunque), significa che non ho importare laboriosamente nomi di eccezioni e un mucchio di altre cose necessarie per usare Obj. "dal modulo import Obj, ObjException, ..." diventa fastidioso rapidamente.

2

Gli attributi di classe sono spesso utilizzati per consentire default imperativi di sottoclassi. Ad esempio, BaseHTTPRequestHandler ha costanti di classe sys_version e server_version, il secondo predefinito a "BaseHTTP/" + __version__. SimpleHTTPRequestHandler sovrascrive server_version su "SimpleHTTP/" + __version__.

+0

Tuttavia, il metodo di accesso dell'attributo class sarebbe un fattore se questo ha funzionato o meno. Un altro motivo per cui gli attributi di classe sono pericolosi. – cmcginty

+0

Che "metodo di accesso"? cls.version, self.version, getattr, daranno la risposta giusta. * Definire in classe per consentire l'override * funziona altrettanto bene per i dati che per il codice - quindi, Casey, perché pensi che sia pericoloso per i dati, ma va bene per il codice ?! –

+0

L'utilizzo di Class.value anziché self.value in un metodo per accedere all'attributo impedirà l'esecuzione di un override.A volte questo è il caso desiderato, altre volte no. – cmcginty

1

L'incapsulamento è un buon principio: quando un attributo si trova all'interno della classe cui appartiene invece di essere nell'ambito globale, questo fornisce ulteriori informazioni alle persone che leggono il codice.

Nelle vostre situazioni 1-4, eviterei così globalmente il più possibile, e preferisco usare gli attributi di classe, che consentono di beneficiare dell'incapsulamento.

3

quello che è un caso d'uso buono per gli attributi di classe

Caso 0. metodi di classe sono gli attributi di classe solo. Questa non è solo una somiglianza tecnica: è possibile accedere e modificare i metodi di classe in fase di esecuzione assegnando loro delle calle.

Caso 1. Un modulo può facilmente definire diverse classi. È ragionevole racchiudere tutto ciò che riguarda class A in A... e tutto ciò che riguarda class B in B.... Ad esempio,

# module xxx 
class X: 
    MAX_THREADS = 100 
    ... 

# main program 
from xxx import X 

if nthreads < X.MAX_THREADS: ... 

Caso 2. Questa classe ha un sacco di attributi predefiniti che possono essere modificate in un'istanza. Qui la possibilità di lasciare che l'attributo sia un 'default globale' è una caratteristica, non un bug.

class NiceDiff: 
    """Formats time difference given in seconds into a form '15 minutes ago'.""" 

    magic = .249 
    pattern = 'in {0}', 'right now', '{0} ago' 

    divisions = 1 

    # there are more default attributes 

Uno crea un'istanza di NiceDiff di utilizzare la formattazione esistente o leggermente modificata, ma un localizzatore per una lingua diversa sottoclassi della classe per implementare alcune funzioni in un modo fondamentalmente diverso e ridefinire le costanti:

class Разница(NiceDiff): # NiceDiff localized to Russian 
    '''Из разницы во времени, типа -300, делает конкретно '5 минут назад'.''' 

    pattern = 'через {0}', 'прям щас', '{0} назад' 

I suoi casi:

  • costanti - ye s, li ho messi in classe. È strano dire self.CONSTANT = ..., quindi non vedo un grosso rischio di biasimarli.
  • Attributo predefinito - misto, come sopra può andare in classe, ma può anche andare a __init__ a seconda della semantica.
  • Struttura dati globali --- va a classe se usato solo dalla classe, ma può anche andare al modulo, in entrambi i casi deve essere molto ben documentato.