2013-03-07 19 views
5

Sono abbastanza nuovo in Python e devo dichiarare la mia struttura dati, sono un po 'confuso su come farlo. Al momento ho:Strutture dati personalizzate in Python

class Particle: 

    def __init__(self, mass, position, velocity, force): 

     self.mass = mass 
     self.position, self.velocity, self.force = position, velocity, force 

    def __getitem__(self, mass): 
     return self.mass 

    def __getitem__(self, position): 
     return self.position 

    def __getitem__(self, velocity): 
     return self.velocity 

    def __getitem__(self, force): 
     return self.force 

Questo non funziona, tuttavia, quando cerco di definire un'istanza della classe con:

p1 = Particle(mass, position, velocity, force) 

Ogni valore finisce proprio come una (0.0, 0.0) (che è il valore di velocità e forza).

Qualcuno potrebbe spiegare dove sto andando male, tutto ciò di cui ho bisogno dalla struttura dati è di essere in grado di estrarre i dati da esso, nient'altro. (Edit: in realtà, mi dispiace, dovrò cambiare loro un po 'più tardi)

Grazie

+2

questo funziona per me come è. – askewchan

+2

Tutte le definizioni '__getitem__' sono identiche. È necessario verificare quale oggetto viene effettivamente recuperato. –

+3

@askewchan Non dovrebbe. –

risposta

15

Prima di tutto, è necessario comprendere che lo __getitem__ è zucchero sintattico. È bello avere, ma se non ne hai bisogno, non usarlo. __getitem__ e __setitem__ sono fondamentalmente, se si vuole essere in grado di accedere agli elementi del tuo oggetto utilizzando la notazione staffa come:

p= Particle(foo) 
bar = p[0] 

se non c'è bisogno di questo, non ti preoccupare.

Ora, su tutto il resto. Sembra che tu abbia le caratteristiche principali che vuoi che il tuo oggetto abbia a portata di mano nella tua definizione __init__, che va bene. Ora è necessario legare in realtà questi valori sul vostro oggetto utilizzando self:

class Particle: 
    def __init__(self, mass, position, velocity, force): 
     self.mass = mass 
     self.position = position 
     self.velocity = velocity 
     self.force = force 

Questo è davvero. È ora possibile accedere a questi valori usando la notazione punto, in questo modo:

mass,pos,vel,f = 0,0,0,0 # just for readability 
p = Particle(mass,pos,vel,f) 
print p.mass, p.position, p.velocity, p.force 

Una delle cose belle che abbiamo uscire da questo è che se chiediamo pitone cosa p è, vi dirà che si tratta di un caso di il tipo Particle, in questo modo:

in [1]: p 
out[1]: <__main__.Particle instance at 0x03E1fE68> 

In teoria, quando si lavora con gli oggetti come questo si vuole che ci sia un "livello di astrazione" tra l'utente ei dati tali che non accedono o manipolare i dati direttamente. Per fare questo, puoi creare funzioni (come hai provato a fare con __getitem__) per mediare le interazioni tra l'utente e i dati attraverso i metodi di classe. Questo è bello, ma spesso non è necessario.

Nel tuo caso più semplice, per aggiornare i valori di questi attributi, si può solo farlo direttamente allo stesso modo in cui li si accede, con la notazione dot:

in [2]: p.mass 
out[2]: 0 

in [3]: p.mass = 2 
in [4]: p.mass 
out[4]: 2 

Potreste aver capito questo già, ma non c'è nulla di magico nella funzione __init__, o anche nella definizione class (in cui dovresti/dovresti generalmente definire la maggior parte degli attributi e dei metodi della tua classe). Alcuni tipi di oggetti sono piuttosto permissivi e ti permettono di aggiungere attributi quando e dove vuoi. Questo può essere conveniente, ma in genere è molto hacky e non è una buona pratica. Non sto suggerendo di farlo, solo dimostrandoti che è possibile.

in [5]: p.newattr ='foobar!' 
in [6]: p.newattr 
out[6]: 'foobar!' 

Strano, vero? Se questo ti fa accapponare la pelle ... beh, forse dovrebbe. Ma è possibile, e chi sono io per dire ciò che puoi o non puoi fare. Quindi questo è un assaggio di come funzionano le classi.

+0

È grandioso, grazie! Domanda di follow-up veloce però, come posso cambiare i valori dopo aver creato un'istanza di Particle? – djcmm476

+0

@Incredidave Assegnazione, come con le variabili (ci sono alcune sottili differenze, ma la maggior parte delle persone va d'accordo per mesi senza capirle, quindi dovresti stare bene ^^): 'p.mass = new value' – delnan

+0

@delnan Cool, grazie a sacco. – djcmm476

7
class Particle: 
    def __init__(self, mass, position, velocity, force): 
     self.mass = mass 
     self.position = position 
     self.velocity = velocity 
     self.force = force 

particle = Particle(1, 2, 3, 4) 
print(particle.mass) # 1 

Se si vuole far finta vostra classe ha proprietà, è possibile utilizzare il @property decoratore:

class Particle: 
    def __init__(self, mass, position, velocity, force): 
     self.mass = mass 
     self.position = position 
     self.velocity = velocity 
     self.force = force 

    @property 
    def acceleration(self): 
     return self.force/self.mass 

particle = Particle(2, 3, 3, 8) 
print(particle.acceleration) # 4.0 
+0

Questo è grandioso, grazie. Sono abbastanza nuovo per Python e stavo faticando a trovare qualcosa di decente per descriverlo. Come al solito con Python, la risposta è più semplice di quanto pensassi. – djcmm476

+0

Fuori interesse, perché * fingere * di avere proprietà, quando effettivamente avere proprietà è così facile? – delnan

+0

@delnan Supponiamo che una proprietà sia definita da una formula che si basa su altre due proprietà. Se ne modifichi uno (come la massa) dovresti anche aggiornare l'accelerazione. Questo probabilmente significa codice bacato o disordinato. –

4

Sembra come collections.namedtuple è quello che stai dopo:

from collections import namedtuple 

Particle = namedtuple('Particle', 'mass position velocity force') 
p = Particle(1, 2, 3, 4) 
print p.velocity 
+1

Solo se gli attributi non devono cambiare mai durante la vita di un oggetto, e tu stai bene con una sequenza. Anche l'accuratezza dei namedtuples subisce un duro colpo quando è necessario aggiungere metodi o qualcosa di simile al costruttore (sono comunque utili, ma richiedono una gestione più attenta e un codice brutto per farlo funzionare bene). – delnan

+1

@delnan Vero, ma sto seguendo la dichiarazione dell'OP di "tutto ciò di cui ho bisogno dalla struttura dati è di essere in grado di estrarre i dati da esso, nient'altro" –

+0

Sì, e la tua risposta va bene. È solo che non vedo quasi mai quei limiti nemmeno accennati, anche se importanti, quindi ho preso l'abitudine di menzionarli. – delnan

0

Se solo bisogno di memorizzare alcuni valori degli attributi (simile ad un linguaggio C struct), si può solo fare:

class myContainer(object): 
    pass # Do nothing 

myContainerObj = myContainer() 
myContainerObj.storedAttrib = 5 
print myContainerObj.storedAttrib 
+0

Nah, questo è solo un modo terribile di scrivere un dizionario. Sono contrario all'utilizzo della possibilità di aggiungere e rimuovere attributi agli oggetti senza ragioni molto forti. Rende gli errori più difficili da individuare (a volte questa cosa ha un attributo, a volte non lo è, a seconda del flusso di controllo). – delnan

+0

@delnan Sono d'accordo in generale, ma per qualcuno nuovo di Python è una soluzione semplice e non richiede una comprensione di dizionari, hashing e il metodo 'get()'. (È anche nei documenti tutorial Python: http://docs.python.org/3/tutorial/classes.html#odds-and-ends) – neilr8133

+0

modifica: (_insert_: "metodo' get() 'per specificare un valore se l'attributo non esiste, o qualche altro metodo per verificare se è stato definito "). Personalmente, preferirei vedere una classe completa con '__init__' e metodi get/set appropriati per tutte le proprietà, ma nel mio (possibilmente errato) parere, questo ha soddisfatto le esigenze dell'OP.) – neilr8133

Problemi correlati