PDA

View Full Version : [Python] Classi, oggetti e self


kwb
23-10-2012, 18:51
Sto facendo un programma per gestire un'ipotetica biblioteca.
Ho un dubbio sulla parola chiave self:
Uno sguardo a questo stralcio di codice:

class floor():

__closet=[]
__floorNumber=0

def __init__(self, closetNumber, n):
self.__floorNumber=n
self.__closet.append(closetNumber)

def addBook(self,bookObj,closetNumber,shelfNumber):
for i in range(len(self.__closet)):
if self.__closet[i].getClosetNumber()== closetNumber:
self.__closet[closetNumber].addBook(bookObj,shelfNumber)
Possibile che io debba sempre usare self davanti ad ogni variabile della classe? È veramente scomodo, in Java non era così...

EDIT: Una domanda ulteriore sull'import: ho letto in giro che l'import non funziona bene su sistemi Mac OS e Windows nel caso in cui si importi un modulo che ha un'importazione al suo interno, nello specifico:

from modulo import *

importerebbe solo i metodi della classe 'modulo' ma non i metodi ereditati da 'modulo' tramite una import dentro di se.
È vero?

ingframin
23-10-2012, 23:54
class floor():

__closet=[]
__floorNumber=0

def __init__(self, closetNumber, n):
self.__floorNumber=n
self.__closet.append(closetNumber)

...

Possibile che io debba sempre usare self davanti ad ogni variabile della classe? È veramente scomodo, in Java non era così...


Attento perché così stai facendo un po'di confusione:
__closet=[] e __floorNumber=0 come l'hai create tu non sono variabili di istanza ma di classe (come se in Java tu ci avessi messo davanti static).
Per renderle variabili di istanza cancella la dichiarazione fuori dall'__init__.

self. va apposto a tutte le variabili di istanza e a tutti i metodi che richiami nella classe ed è il puntatore all'istanza specifica.

Non ho mai avuto problemi con from modulo import *, sia su linux che su windows, sei sicuro che non sia un bug di qualche versione precedente alla 2.7.3?

kwb
24-10-2012, 00:15
io sto programmando in python indifferentemente sia su ubuntu sia su windows senza problemi di compatibilità, è il vantaggio dei linguaggi interpretati...il problema arriva quando hai bisogno di una libreria comptibile solo con alcuni SO e nn altri....x esempio il modulo BeautifulSoup non è compatibile con windows...basta che fai però una rapida ricerca su google e scopri subito le comptibilità e ti tolgi tutti i dubbi


Non ho mai avuto problemi con from modulo import *, sia su linux che su windows, sei sicuro che non sia un bug di qualche versione precedente alla 2.7.3?

Bho era una cosa che avevo letto di sfuggita su internet cercando informazioni sull'import.
Una cosa che mi aspettavo di poter fare era la seguente: nel main importo il modulo floor ( from floor import * ) e se dentro floor ho già importato altri moduli, mi aspettavo di poterli usare anche dal main, ma non è così...

Attento perché così stai facendo un po'di confusione:
__closet=[] e __floorNumber=0 come l'hai create tu non sono variabili di istanza ma di classe (come se in Java tu ci avessi messo davanti static).
Per renderle variabili di istanza cancella la dichiarazione fuori dall'__init__.

Ma se voglio dichiarare una variabile privata ( non accessibili da moduli esterni ) non devo usare __ davanti alla variabile? Come faccio a dichiarare una variabile privata, così come si farebbe in java:

private int n;

E se poi non la voglio inizializzare ( e quindi non metterla nel costruttore )?

Da quel che mi hai detto, ho capito che le variabili le dichiaro private solo dentro il costruttore ( anche se, è vero, non esiste una vera e propria dichiarazione delle variabili in python )


self. va apposto a tutte le variabili di istanza e a tutti i metodi che richiami nella classe ed è il puntatore all'istanza specifica.
Quindi, siccome io voglio dichiarare quelle variabili come private, i self che ho messo sono giusti no?
Non funziona come il this. di java?

Eln
24-10-2012, 06:28
Fai un pò di confusione, il this in Java veniva usato per richiamare un attributo/variabile (chiamala come vuoi) d'istanza, stessa cosa qui vale per il self. Servono esattamente alla stessa cosa.

La differenza è che in Java esistono i modificatori d'accesso, private ecc, cosa che in Python non c'è. Porre un _ d'avanti ad una variabile serve a far vedere agli altri, per convenzione, che quell'attributo è privato e quindi non dovrebbero accedervi direttamente (senza metodo get/set per capirci) e se lo fanno è a loro rischio e pericolo.

Come ti hanno scritto sotto quello che dovresti fare è mettere i tuoi attributi d'istanza nel Costruttore, ponendo un _ prima di quelli che vuoi indicare come privati. Fuori invece metti le variabili di classe, costanti, che infatti anche in Java si inizializzano fuori dal costruttore.

cdimauro
24-10-2012, 06:46
Esattamente, il _ davanti è per lo più una convenzione: "è roba mia, interna, per favore non toccarla". Ma nessuno t'impedisce di metterci le mani, perché non esiste nella maniera più assoluta il concetto di privato, protetto, pubblico.

Ma attenzione quando si importa tutto da un modulo con la sintassi from Modulo import *. In questo caso Python non importa tutti gli identificatori del modulo, ma soltanto quelli che NON iniziano con _. In pratica chi ha realizzato il modulo può decidere cosa far importare automaticamente e cosa no; un meccanismo più fine è rappresentato dall'uso della variabile __all__ .

Ovviamente non c'è alcun problema coi moduli rispetto al s.o. su cui gira Python.

Riguardo a self & this, anche qui ci sono differenze con gli altri linguaggi. All'interno di una funzione definita in una classe (che diventa un metodo a tutti gli effetti, se non viene "decorata" opportunamente per farla diventare metodo di classe o funzione statica; ma di questo non ne parliamo per ora), qualunque assegnazione produce una variabile locale alla funzione.

Se vogliamo creare, modificare, o semplicemente accedere a una variabile d'istanza, bisogna sempre utilizzare self.Variabile. Non è come negli altri linguaggi a oggetti, dove il self/this è implicitamente sempre presente; qui è obbligatorio, perché fa fede il motto di Python (explicit is better than implicit).

E' noioso scrivere sempre self.? Può darsi, ma il codice è più leggibile. Ciò detto, se il problema è il morbo dello scrivano, si può usare un qualunque altro identificatore al posto di self:
class c:
def m(s):
s.VariabileDIstanza = 'Hello!'
Meno caratteri da digitare, ma è una pratica che sconsiglio: il codice Python è bello anche perché è leggibile; non "sporchiamolo". ;)

kwb
24-10-2012, 11:02
Fai un pò di confusione, il this in Java veniva usato per richiamare un attributo/variabile (chiamala come vuoi) d'istanza, stessa cosa qui vale per il self. Servono esattamente alla stessa cosa.

La differenza è che in Java esistono i modificatori d'accesso, private ecc, cosa che in Python non c'è. Porre un _ d'avanti ad una variabile serve a far vedere agli altri, per convenzione, che quell'attributo è privato e quindi non dovrebbero accedervi direttamente (senza metodo get/set per capirci) e se lo fanno è a loro rischio e pericolo.
A me non interessa niente delle questioni etiche o morali che si possono celare dietro un _ . Semplicemente ho fatto così perchè quando ho iniziato la programmazione ad oggetti mi è stato spiegato di dichiarare ( salvo speciali eccezioni ) le variabili di una classe come private e fare il metodo per impostarle/stamparle. Ora da quello che ho letto rapidamente sul sito ufficiale, questo concetto pare non esistere ( o essere radicato ) come è per Java.
Quindi la domanda è: un programmatore che scrive in Python, come le deve gestire le variabili che dichiara di una classe?


Come ti hanno scritto sotto quello che dovresti fare è mettere i tuoi attributi d'istanza nel Costruttore, ponendo un _ prima di quelli che vuoi indicare come privati. Fuori invece metti le variabili di classe, costanti, che infatti anche in Java si inizializzano fuori dal costruttore.
Ma quindi invece di inizializzarle a 0 fuori dal costruttore e poi impostarle al valore scelto col costruttore, le sparo dirette dentro il costruttore con un underscore davanti? Oppure 2 (underscore)?


Ma attenzione quando si importa tutto da un modulo con la sintassi from Modulo import *. In questo caso Python non importa tutti gli identificatori del modulo, ma soltanto quelli che NON iniziano con _. In pratica chi ha realizzato il modulo può decidere cosa far importare automaticamente e cosa no; un meccanismo più fine è rappresentato dall'uso della variabile __all__ .Quindi mi pare di capire che le mie variabili che ho dichiarato tutte con un __ non saranno affatto visibili dentro altri moduli, no? Non è un po' il comportamento che si ottiene tramite il private di Java?


E' noioso scrivere sempre self.? Può darsi, ma il codice è più leggibile. Ciò detto, se il problema è il morbo dello scrivano, si può usare un qualunque altro identificatore al posto di self:
class c:
def m(s):
s.VariabileDIstanza = 'Hello!'
Meno caratteri da digitare, ma è una pratica che sconsiglio: il codice Python è bello anche perché è leggibile; non "sporchiamolo". ;)Fammi capire, quindi mi basta impostare come primo parametro un qualuque carattere per renderlo equivalente al self.?
Se così fosse andremmo moooolto meglio ;)

banryu79
24-10-2012, 11:24
Fammi capire, quindi mi basta impostare come primo parametro un qualuque carattere per renderlo equivalente al self.?
Se così fosse andremmo moooolto meglio ;)
Un consiglio spassionato (basato sulla mia esperienza). Quando studi per imparare un nuovo linguaggio, tenta sempre di:
1) dimenticare le idiosincrasie di altri linguaggi che già conosci;
2) rispettare le convenzioni e gli idiomi del nuovo linguaggio;
3) cercarne di capire le ragioni (col tempo & esperienza).

Ho trovato questo atteggiamento molto utile.

cdimauro
24-10-2012, 12:07
A me non interessa niente delle questioni etiche o morali che si possono celare dietro un _ . Semplicemente ho fatto così perchè quando ho iniziato la programmazione ad oggetti mi è stato spiegato di dichiarare ( salvo speciali eccezioni ) le variabili di una classe come private e fare il metodo per impostarle/stamparle. Ora da quello che ho letto rapidamente sul sito ufficiale, questo concetto pare non esistere ( o essere radicato ) come è per Java.
Infatti (e per fortuna :D) NON è Java. :)
Quindi la domanda è: un programmatore che scrive in Python, come le deve gestire le variabili che dichiara di una classe?
Come ritieni più opportuno. Il che significa che spesso un programmatore Python le dichiara:
- nell'__init__ se serve che siano sempre inizializzate, magari con valori di default;
- all'interno di altri metodi.

Non siamo forzati a impostare le variabili d'istanza per forza nel costruttore, insomma. Lo facciamo quando lo riteniamo più opportuno.

In altri casi è possibile definire delle property, Delphi/C# like per intenderci, e quindi controllare l'accesso di una variabile d'istanza tramite getter e/o setter e/o deleter.
Ma quindi invece di inizializzarle a 0 fuori dal costruttore e poi impostarle al valore scelto col costruttore, le sparo dirette dentro il costruttore con un underscore davanti? Oppure 2 (underscore)?
Io raramente uso l'underscore come prefisso di una variabile d'istanza. La dichiaro direttamente col nome che deve avere.
Quindi mi pare di capire che le mie variabili che ho dichiarato tutte con un __ non saranno affatto visibili dentro altri moduli, no? Non è un po' il comportamento che si ottiene tramite il private di Java?
No, non è così. Se c'è un _ davanti, le variabili non saranno importabili direttamente tramite from Modulo import *, ma rimangono sempre accessibili:
Modulo._NonSonoInvisibile = True
Oppure:
from Modulo import _NonSonoInvisibile

_NonSonoInvisibile = True
Fammi capire, quindi mi basta impostare come primo parametro un qualuque carattere per renderlo equivalente al self.?
Se così fosse andremmo moooolto meglio ;)
Sì, esatto, ma non troverai un solo programmatore Python che lo faccia.

Siamo una setta di integralisti votati alla leggibilità del sorgente. :Perfido:
Un consiglio spassionato (basato sulla mia esperienza). Quando studi per imparare un nuovo linguaggio, tenta sempre di:
1) dimenticare le idiosincrasie di altri linguaggi che già conosci;
2) rispettare le convenzioni e gli idiomi del nuovo linguaggio;
3) cercarne di capire le ragioni (col tempo & esperienza).

Ho trovato questo atteggiamento molto utile.
Sante parole. :)

kwb
24-10-2012, 14:06
Ho provato un po' a giocare con queste cose. Ma non ho ben afferrato come sono riuscito ( e se ci sono riuscito ) a fare una lista di oggetti ( 2 ) che contenesse un'altra lista di oggetti.
Ad esempio, il main:

from floor import *
from book import *
#Initialize the array
piano=[]

for i in range(2): #Use 2 floors
tempFloor=floor(0,i)
piano.append(tempFloor)

libroProva = book("Isaac Asimov","Cronache della Galassia")
#piano[0].addBook(book,1,1)

C'è questa classe floor che dovrebbe rappresentare il piano della biblioteca.
Su ogni piano della biblioteca ci devono essere un tot di armadi ( il cui numero è ancora da definire ), e poi per ogni armadio ci dovranno essere scaffali ecc... ( ma ancora non ci sono arrivato ).
Quello che ho fatto è stato creare una lista piano. Tramite il for creo degli oggetti piano temporanei ( tempFloor ) che inserisco dentro la lista piano.
Ora la classe floor:

class floor():

def __init__(self, closetNumber, n):
self._closet=[]
self._closet.append(closetNumber)
self._floorNumber=n

def addBook(self,bookObj,closetNumber,shelfNumber):
for i in range(len(self._closet)):
if self._closet[i].getClosetNumber()== closetNumber:
return self._closet[closetNumber].addBook(bookObj,shelfNumber)
return False

Siccome ho una classe che si chiama closet, ho preferito chiamare la variabile con un underscore davanti.
Tuttavia, mi aspettavo che scrivendo:

def __init__(self, closetNumber, n):
self._closet.append(closetNumber)
self._floorNumber=n
Funzionasse, in realtà non va. Credo perchè non ho mai specificato cos è _closet. Inoltre utilizzare self. o meno non da errori sintattici, però credo di doverlo mettere, anche se non capisco veramente il perchè.. :asd:

Premesso che _closet è una lista di numeri ( poi lo trasformerò in oggetto, tramite la sua classe ), sono riuscito a creare la seguente struttura ( che dura fino alla fine dell'esecuzione del programma )?

piano[0]:
closet[0]
piano[1]:
closet[0]

EDIT: Dal debugger mi pare di si, ma vorrei avere conferma da voi anche.

ingframin
24-10-2012, 14:30
Se ci scrivi il testo dell'esercizio per come lo hai concepito proviamo a farlo e poi ci scambiamo i sorgenti completi.
ti va?

kwb
24-10-2012, 16:41
La biblioteca è composta da:
- 2 piani
- Per ogni piano vi sono, diciamo, 5 armadi
- Per ogni armadio vi sono 5 scaffali
- Per ogni ripiano ci sono 10 libri

Dal main si chiama il metodo addBook che aggiunge l'oggetto book alla libreria, nella posizione indicata dai parametri ( specificando il piano ):

piano[0].addBook(oggetto_Book,NumeroArmadio,NumeroMensola)

addBook è un metodo presente nelle classi:
floor, closet e shelf.
Vi è una classe aggiuntiva, book che contiene, oltre al titlo e l'autore ( inizializzati tramite costruttore alla creazione dell'oggetto libro ) anche i metodi set/get per impostare il piano/armadio/scaffale a cui si trova e l'anno di pubblicazione del libro.

banryu79
24-10-2012, 17:11
Altro consiglio (non richiesto, lo so) spassionato:
Scrivi il sorgente (i nomi delle variabili e dei tipi custom) in una sola lingua, o inglese o italiano o altro (la regola sarebbe usare la ingua franca dell'informatica: l'inglese).
Altrimenti :) vien fuori un pasticcio (dal punto di vista della leggibilità del codice e della facilità di comprensione)

kwb
24-10-2012, 17:14
Altro consiglio (non richiesto, lo so) spassionato:
Scrivi il sorgente (i nomi delle variabili e dei tipi custom) in una sola lingua, o inglese o italiano o altro (la regola sarebbe usare la ingua franca dell'informatica: l'inglese).
Altrimenti :) vien fuori un pasticcio (dal punto di vista della leggibilità del codice e della facilità di comprensione)

Lo faccio bilingue perchè ho paura poi di finire in qualche scambio di metodi/variabili/classi inatteso, soprattutto ora che non sto capendo assolutamente niente ( no dai beh, poco ) di come si gestiscono le variabili..

banryu79
24-10-2012, 17:20
Lo faccio bilingue perchè ho paura poi di finire in qualche scambio di metodi/variabili/classi inatteso

Questa non l'ho capita..

kwb
24-10-2012, 17:24
Questa non l'ho capita :mbe:
Nel senso che poi magari mi confondo e invece di scrivere il nome della variabili, scrivo il nome della classe ( che magari si chiamano simili ).

banryu79
24-10-2012, 17:35
Nel senso che poi magari mi confondo e invece di scrivere il nome della variabili, scrivo il nome della classe ( che magari si chiamano simili ).
Ma scusa, adotta una convenzione.
Tipo: i nomi delle classi iniziano in maiuscolo, i campi delle istanze in minuscolo.
Floor è la classe, floor il campo.
Closet è la classe, closet il campo, ecc...
Comunque nessuno ti vieta di fare qual cavolo che vuoi eh, il mio era solo un consiglio ;)

kwb
25-10-2012, 00:11
Ho seguito il tuo suggerimento e ho fatto così.
Ora però, alle prese con la creazione della biblioteca vuota ( senza libri ), mi da un errore, dentro il file Closet.py

self._shelf[i].append(dummyS)
IndexError: list index out of range


I moduli
Main.py:

from Floor import *
from Book import *
#Initialize the array
floor=[]

for i in range(2): #Use 2 floors
tempFloor=Floor(i)
floor.append(tempFloor)


Floor.py:

from Closet import *

class Floor():
floorNumber =0
def __init__(self, n):
self._closet=[]
for i in range(5): #Create 5 closets
dummyC=Closet(i)
self._closet[i].append(dummyC)
self.floorNumber=n

Closet.py:

from Shelf import *
class Closet():

closetNumber=0
def __init__(self, closetNumber):
self.closetNumber=closetNumber
self._shelf=[]
for i in range(5): #Create 5 shelves
dummyS=Shelf(i)
self._shelf[i].append(dummyS)

Shelf.py:

class Shelf():

shelfNumber=0
book=[]
def __init__(self, shelfNumber):
self.shelfNumber=shelfNumber


Non capisco perchè gli indici i nei for, non aumentano...
Inoltre, è giusto come ho gestito le variabili?

cdimauro
25-10-2012, 06:19
Ci sono diversi errori. Il primo è che hai dichiarato floorNumber, closetNumber, shelfNumber, e (soprattutto!) book come variabili di classe, quando è chiaro che dovrebbero essere d'istanza, per l'uso che ne fai. Quindi ti basta eliminare l'assegnazione a zero (e lista vuota per l'ultimo) che nei fai all'interno della definizione della classe.

Quello che ti crea problemi, comunque, è il fatto che accedi a un elemento della lista quando ancora non gliel'hai inserito con append. Infatti per popolare la lista al posto di questo:
self._closet[i].append(dummyC)
devi scrivere:
self._closet.append(dummyC)
Ciò vale anche per Closet.py.

Infatti tu hai creato una lista vuota con self._closet = [] , ma... devi ancora popolarla. E lo fai col metodo append, ma devi applicarlo a self._closet, perché è questa lista, mentre self._closet[i] non è una lista (potrebbe anche esserlo, eh!) ma un suo elementi, che nello specifico è un'istanza di Closet.

Un'ottimizzazione che potresti fare è quella di eliminare l'uso delle variabili temporanee. Un esempio:
for i in range(2): #Use 2 floors
floor.append(Floor(i))

Se sistemi tutto il codice in modo che sia in questa forma, puoi sfruttare un bellissimo strumento di Python che sono le list comprehension, che ti permettono di creare (e popolare) una lista in maniera compatta ed elegante:
floor = [Floor(i) for i in range(2)] #Use 2 floors
Se hai un background matematico, ti dovrebbe essere facile leggerla e comprenderne il significato. ;)

kwb
25-10-2012, 12:32
Allora, non riesco ancora ad afferrare la differenza tra variabile di classe e d'istanza.
A livello di allocazione di memoria ed accesso alla variabile ( perchè credo che la differenza sia in quello ), cosa cambia?
Non sono riuscito a trovare qualcosa che lo spiegasse chiaramente, nemmeno il tutorial sul sito di python lo spiega...
Da quello che ho capito io la variabile di istanza esiste ( e lo spazio in memoria per essa viene allocato ) quando creo l'oggetto ( ovvero con un Floor(i) ad esempio ) e dura fino alla deallocazione in memoria dell'oggetto.
Ma la variabile di classe?

Ci sono diversi errori. Il primo è che hai dichiarato floorNumber, closetNumber, shelfNumber, e (soprattutto!) book come variabili di classe, quando è chiaro che dovrebbero essere d'istanza, per l'uso che ne fai. Quindi ti basta eliminare l'assegnazione a zero (e lista vuota per l'ultimo) che nei fai all'interno della definizione della classe.

Per book=[] devo ancora vedere come gestirlo, ma penso tu abbia ragione, perchè deve essere creato dentro l'oggetto shelf.


Quello che ti crea problemi, comunque, è il fatto che accedi a un elemento della lista quando ancora non gliel'hai inserito con append. Infatti per popolare la lista al posto di questo:
self._closet[i].append(dummyC)
devi scrivere:
self._closet.append(dummyC)
Ciò vale anche per Closet.py.

Infatti tu hai creato una lista vuota con self._closet = [] , ma... devi ancora popolarla. E lo fai col metodo append, ma devi applicarlo a self._closet, perché è questa lista, mentre self._closet[i] non è una lista (potrebbe anche esserlo, eh!) ma un suo elementi, che nello specifico è un'istanza di Closet.
Si è vero, è una svista. Mi confondono un casino ste liste... :muro:


Un'ottimizzazione che potresti fare è quella di eliminare l'uso delle variabili temporanee. Un esempio:
for i in range(2): #Use 2 floors
floor.append(Floor(i))

Se sistemi tutto il codice in modo che sia in questa forma, puoi sfruttare un bellissimo strumento di Python che sono le list comprehension, che ti permettono di creare (e popolare) una lista in maniera compatta ed elegante:
floor = [Floor(i) for i in range(2)] #Use 2 floors
Se hai un background matematico, ti dovrebbe essere facile leggerla e comprenderne il significato. ;)
Mi sono guardato le list comprehension sul sito di python.
Mi pare di capire che essenzialmente ciò che vuoi far fare in seguito ad un condizione lo metti prima e dopo metti la/e condizione/i.
Poi la teoria è sempre diversa dalla pratica :D

kwb
25-10-2012, 13:04
Ho aggiustato un po' e ora almeno stampa qualcosa.
Tuttavia, come faccio a creare lo spazio di memoria per l'oggetto book ( creare una lista lunga 10 book, dentro shelf ) senza però dover assegnare i valori ( perchè lo farò tramite il metodo addBook )?

cdimauro
25-10-2012, 21:01
Allora, non riesco ancora ad afferrare la differenza tra variabile di classe e d'istanza.
A livello di allocazione di memoria ed accesso alla variabile ( perchè credo che la differenza sia in quello ), cosa cambia?
Non sono riuscito a trovare qualcosa che lo spiegasse chiaramente, nemmeno il tutorial sul sito di python lo spiega...
Da quello che ho capito io la variabile di istanza esiste ( e lo spazio in memoria per essa viene allocato ) quando creo l'oggetto ( ovvero con un Floor(i) ad esempio ) e dura fino alla deallocazione in memoria dell'oggetto.
Ma la variabile di classe?
La variabile di classe esiste in unica copia, e viene condivisa da tutte le istanze della stessa classe. Per questo viene definita all'interno della definizione della classe, e non dentro a uno qualunque dei metodi.

Può servire, ad esempio, per tenere traccia di quante istanze di una classe sono state create, per implementare il pattern Singleton (che molti odiano), e altro ancora.
Si è vero, è una svista. Mi confondono un casino ste liste... :muro:
Pensale come agli array di Java.
Mi sono guardato le list comprehension sul sito di python.
Mi pare di capire che essenzialmente ciò che vuoi far fare in seguito ad un condizione lo metti prima e dopo metti la/e condizione/i.
Poi la teoria è sempre diversa dalla pratica :D
E' molto più semplice di quel possa sembra, ed è molto vicino alla teoria questa volta.

Devi leggerla come: "fai questo calcolo e appendi il risultato alla lista, per ogni elemento che appartiene a quest'insieme (sequenza, o altro) che soddisfi questa condizione (solo se presente; è opzionale)".
Ho aggiustato un po' e ora almeno stampa qualcosa.
Tuttavia, come faccio a creare lo spazio di memoria per l'oggetto book ( creare una lista lunga 10 book, dentro shelf ) senza però dover assegnare i valori ( perchè lo farò tramite il metodo addBook )?
In questo caso per convenzione possiamo usare il valore None (il null di C & co.) per ognuno dei 10 possibili libri. Si fa così:
self.book = [None] * 10
perché in Python si può "moltiplicare" una sequenza tante volte quante specificato dal numero che specifichi.
In questo caso la lista contiene un solo "valore", None, e quindi la nuova lista avrà 10 elementi con None per ognuno di essi.

kwb
25-10-2012, 22:23
La variabile di classe esiste in unica copia, e viene condivisa da tutte le istanze della stessa classe. Per questo viene definita all'interno della definizione della classe, e non dentro a uno qualunque dei metodi.

Beh, per come funziona il mio programma allora non vedo una sostanziale differenza tra la variabile di classe e quella di istanza, siccome ci sarà una sola copia di ogni oggetto che andrò a creare ( eccetto per Book, ma quello ha i suoi metodi ).


E' molto più semplice di quel possa sembra, ed è molto vicino alla teoria questa volta.

Devi leggerla come: "fai questo calcolo e appendi il risultato alla lista, per ogni elemento che appartiene a quest'insieme (sequenza, o altro) che soddisfi questa condizione (solo se presente; è opzionale)".
Proverò a implementare parte del codice con queste list comprehension


In questo caso per convenzione possiamo usare il valore None (il null di C & co.) per ognuno dei 10 possibili libri. Si fa così:
self.book = [None] * 10
perché in Python si può "moltiplicare" una sequenza tante volte quante specificato dal numero che specifichi.
In questo caso la lista contiene un solo "valore", None, e quindi la nuova lista avrà 10 elementi con None per ognuno di essi.
Ottimo. Ma se prima faccio così, poi una volta che farò self.book.append(Book(i)) che succede? Uno dei valori None viene cancellato e sostituito dall'oggeto Book-iesimo?

cdimauro
25-10-2012, 23:25
No, ogni volta aggiungeresti un nuovo elemento alla lista.

Dovresti, invece, sostituire l'attuale valore col nuovo, per quella posizione:
self.book[i] = Book(i)

kwb
26-10-2012, 20:38
Sono incappato in un problema da cui non riesco ad uscire.
Ho creato una funzione per trovare la posizione di un libro dato il nome ( o l'autore ).
Ma, nonostante ci sia il matching, non riesco ad entrare nell'if statement:
Shelf.py:
def findBook(self,name):
result=""
for i in range(len(self._book)):
if self._book[i] != None:
if name in(self._book[i].getTitle(), self._book[i].getAuthor()):
result += "F:"+str(self._book[i].getFloor())+"C:"+str(self._book[i].getCloset())+"S:"+str(self._book[i].getShelf())+'\n'
return result
name contiene la stringa da ricercare.
Credevo con in di poter matchare anche parte della stringa... :stordita:

cdimauro
27-10-2012, 10:12
Sì, ma l'operatore in si comporta in maniera diversa a seconda del tipo di oggetto che c'è alla sua destra. Se c'è una stringa, ti dice se l'elemento a sinistra è in essa contenuto. Se c'è una tupla, lista, dizionario o insieme, ti dice se l'elemento vi appartiene (quindi ce n'è almeno uno che coincide completamente con l'elemento.
Nel pezzo di codice che hai postato tu hai chiesto a Python se il nome è presente nella tupla di due elementi (che hai "costruito" al volo), per cui vale la seconda.

Per ottenere quello che chiedi devi applicare 2 volte l'operatore in ai due elementi. Ne approfitto per sistemare un po' il tuo codice e renderlo più "pythonico":
def findBook(self,name):
result = ''
for Book in self._book:
if Book and (name in Book.getTitle() or name in Book.getAuthor()):
result += 'F:%sC:%sS:%s\n' % (Book.getFloor(), Book.getCloset(), Book.getShelf())
return result
Questo perché col for puoi interare tutti gli elementi di una sequenza (la lista lo è).
Inoltre Python assegna come valore logico "vero" qualunque istanza di una classe, mentre None ha come valore logico "falso", per cui il controllo della presenza o meno di un libro si semplifica molto (piccola nota: eventualmente non usare l'== per controllare se un valore è None, ma is None).
Altra cosa, ho usato l'operatore di interpolazione per le stringhe, che è comodo quando hai un template e devi generare una stringa, "fondendo" il template coi dei valori che gli passi. %s indica che in quel posto deve starci il valore preso dalla tupla o lista che specifichi a destra dell'operatore %, e che tale valore dev'essere convertito eventualmente in stringa (se non lo è già).

Comunque cerca di evitare l'uso di getter e setter modello Java. Al posto di self.getTitle() metti self.Title, ecc. Al limite se vuoi limitare / controllare l'accesso a un membro/attributo in Python, puoi usare le property (che sono un argomento più avanzato). ;)

kwb
27-10-2012, 13:23
Sì, ma l'operatore in si comporta in maniera diversa a seconda del tipo di oggetto che c'è alla sua destra. Se c'è una stringa, ti dice se l'elemento a sinistra è in essa contenuto. Se c'è una tupla, lista, dizionario o insieme, ti dice se l'elemento vi appartiene (quindi ce n'è almeno uno che coincide completamente con l'elemento.
Nel pezzo di codice che hai postato tu hai chiesto a Python se il nome è presente nella tupla di due elementi (che hai "costruito" al volo), per cui vale la seconda.

Si il fatto è che l'esempio che c'è sul sito Python è piuttosto ingannevole secondo me: c'è un if con appunto questo in, ma usato su una tupla ( che ora so che rappresenta una tupla ). Io credevo che fosse la sintassi propria dell'in. ovvero

if variabile in (qualcosa)

Non avevo realizzato fosse una tupla..


Ne approfitto per sistemare un po' il tuo codice e renderlo più "pythonico":
def findBook(self,name):
result = ''
for Book in self._book:
if Book and (name in Book.getTitle() or name in Book.getAuthor()):
result += 'F:%sC:%sS:%s\n' % (Book.getFloor(), Book.getCloset(), Book.getShelf())
return result
Questo perché col for puoi interare tutti gli elementi di una sequenza (la lista lo è).
Lo trovo ancora veramente difficile da pensare :D. Sono ancora troppo in modalità 'non-python' :stordita:

(piccola nota: eventualmente non usare l'== per controllare se un valore è None, ma is None).
Is non sarebbe la stessa cosa di 'coincide' ( come mi pare sia == in java )?


Comunque cerca di evitare l'uso di getter e setter modello Java. Al posto di self.getTitle() metti self.Title, ecc. Al limite se vuoi limitare / controllare l'accesso a un membro/attributo in Python, puoi usare le property (che sono un argomento più avanzato). ;)
Intendi questo: http://docs.python.org/library/functions.html#property ?
Non ne capisco la differenza tra il fare il mio metodo get/set.
Come nell'esempio:

class Parrot(object):
def __init__(self):
self._voltage = 100000

@property
def voltage(self):
"""Get the current voltage."""
return self._voltage


Cosa cambia da:

class Parrot(object):
def __init__(self):
self._voltage = 100000

def voltage(self):
"""Get the current voltage."""
return self._voltage


Tra l'altro ora vorrei provare ad implementare l'interfaccia grafica :D
TkInter?

cdimauro
27-10-2012, 16:27
Si il fatto è che l'esempio che c'è sul sito Python è piuttosto ingannevole secondo me: c'è un if con appunto questo in, ma usato su una tupla ( che ora so che rappresenta una tupla ). Io credevo che fosse la sintassi propria dell'in. ovvero

if variabile in (qualcosa)

Non avevo realizzato fosse una tupla..
Il problema principale è che al momento hai dato un'occhiata molto veloce a Python. Immagino che non ti sia studiato nemmeno i tipi standard, coi loro metodi e operatori.

Quando avrai acquisito padronanza con questi tipi, compreso cosa ti permettono di fare, l'uso di operatori come in ti verrà perfettamente naturale.
Lo trovo ancora veramente difficile da pensare :D. Sono ancora troppo in modalità 'non-python' :stordita:
Il foreach mi sembra ci sia anche in Java, no?

Per il resto, sì, le espressioni logiche di Python sono molto diverse dagli altri linguaggi. Anche qui, quando le avrai studiate ti verrà tutto intuitivo e naturale.
Is non sarebbe la stessa cosa di 'coincide' ( come mi pare sia == in java )?
Sì, is è QUASI l'equivalente di == di Java. Per gli oggetti. Per i tipi primitivi di Java, invece, non è così. Ma non voglio confonderti le idee per adesso.

Occhio che ==, invece, non lo è. E' abbastanza diverso. Considera che Python ti permette di valutazione la dis/uguaglianza, ma anche l'ordine, di strutture dati complesse, e il tutto in maniera intuitiva:
[1, 2, 3, 4] < [2, 149]
Risulta vero.
Intendi questo: http://docs.python.org/library/functions.html#property ?
Non ne capisco la differenza tra il fare il mio metodo get/set.
Come nell'esempio:

class Parrot(object):
def __init__(self):
self._voltage = 100000

@property
def voltage(self):
"""Get the current voltage."""
return self._voltage


Cosa cambia da:

class Parrot(object):
def __init__(self):
self._voltage = 100000

def voltage(self):
"""Get the current voltage."""
return self._voltage

Cambia che nel primo caso ti basta scrivere self.voltage per ottenere subito 100000, mentre nel secondo caso devi scrivere self.voltage().

Nel mio caso è Python che invoca il metodo voltage in maniera trasparente quando accedi alla proprietà (che ha proprio quel nome).
Nel secondo caso devi provvedere tu a eseguire il metodo con le (), esattamente come faresti in Java.

Non aggiungo altro al momento per non confonderti le idee.
Tra l'altro ora vorrei provare ad implementare l'interfaccia grafica :D
TkInter?
Meglio di no. Su Python sono molto gettonati PyQt (ma c'è PySide che si sta facendo strada ormai) o wxwidget. Su .NET puoi provare IronPython con le meravigliose WPF (o Silverlight per applicazioni web).

Se ti vuoi divertire un po' c'è anche Camelot (http://www.python-camelot.com/). :fagiano:

kwb
27-10-2012, 16:58
Il problema principale è che al momento hai dato un'occhiata molto veloce a Python. Immagino che non ti sia studiato nemmeno i tipi standard, coi loro metodi e operatori.
Bhe tuple, dizionari e liste me li sono guardati, ma rapidamente perchè non ero molto interessato a queste, chiamiamole, banalità :D


Il foreach mi sembra ci sia anche in Java, no?
Si e anche in altri linguaggi, ma ammetto di averlo usato pochissimo ( o non averlo affatto usato per alcuni linguaggi ) in quanto mi ha sempre turbato assai... :sofico:


Cambia che nel primo caso ti basta scrivere self.voltage per ottenere subito 100000, mentre nel secondo caso devi scrivere self.voltage().

Nel mio caso è Python che invoca il metodo voltage in maniera trasparente quando accedi alla proprietà (che ha proprio quel nome).
Nel secondo caso devi provvedere tu a eseguire il metodo con le (), esattamente come faresti in Java.

Non aggiungo altro al momento per non confonderti le idee.

Cioè, questa cosa è stata fatta per risparmiare al programmatore la fatica di mettere () ? :mbe:


Meglio di no. Su Python sono molto gettonati PyQt (ma c'è PySide che si sta facendo strada ormai) o wxwidget. Su .NET puoi provare IronPython con le meravigliose WPF (o Silverlight per applicazioni web).

Se ti vuoi divertire un po' c'è anche Camelot (http://www.python-camelot.com/). :fagiano:
Ma sono multipiattaforma? Inoltre, in caso di sviluppo web, questa cosa dell'interfaccia non serve a niente, si fa in CSS no?
Perchè avevo un bel progettino in mente... Ma si vedrà se avrò tempo di realizzarlo...

cdimauro
27-10-2012, 18:56
Si e anche in altri linguaggi, ma ammetto di averlo usato pochissimo ( o non averlo affatto usato per alcuni linguaggi ) in quanto mi ha sempre turbato assai... :sofico:
Peccato, perché è un bello strumento, che ti semplifica molto la vita.
Cioè, questa cosa è stata fatta per risparmiare al programmatore la fatica di mettere () ? :mbe:
Assolutamente no. Quello è un esempio banale. Le proprietà ti consentono di controllare l'accesso a un membro, come hai parzialmente visto, ma possono anche emulare attributi che fisicamente non esistono, o che magari sono conservati in altra forma.

Immagina di avere un timestamp conservato come Unix epoch (quindi in secondi dal 1/1/1970, se non ricordo male). Puoi definire delle property per manipolare giorno, mese, anno, ore, minuti e secondi; ognuna di essere cambierà o estrarrà opportunamente quel dato conservato in secondi, anziché manipolare 6 campi interni.

E' soltanto un esempio, ma le possibilità sono tante, e con la pratica e l'esperienza troverai molti modi di utilizzare le proprietà.
Ma sono multipiattaforma?
Sì.
Inoltre, in caso di sviluppo web, questa cosa dell'interfaccia non serve a niente, si fa in CSS no?
Perchè avevo un bel progettino in mente... Ma si vedrà se avrò tempo di realizzarlo...
Il web già è una cosa diversa. Per Python ormai esistono tanti framework che ti consentono di tirare sù velocemente un sito o delle pagine web, ma alla fine devi sempre generare HTML + CSS + Javascript (eventualmente).

Io, tanto per cambiare, sono andato contro corrente, e ho preferito studiarmi un po' pyjs (http://pyjs.org/): programmo tutto in Python (ma un po' di CSS sono necessari), e poi il mio codice viene compilato direttamente in Javascript per essere caricato dal brower. :cool:

kwb
29-10-2012, 11:27
Alla fine ho optato per wxPython.
Camelot mi è parso un po' troppo per iniziare.
Le Qt coi loro sottoprogetti le ho trovate un po' confusionarie.
Chiaramente sono impressioni che ho avuto girando un po' sui rispettivi siti ( e sotto-siti ) e su wikipedia.

wxPython, open source, con un tutorial che pare essere buono. Vedremo. :D

EDIT: Tra l'altro, se il tempo me lo permetterà, mi butterò anche e soprattuto sul web, perchè è la parte che più mi interessa. HTML e CSS già li conosco.
Avendo fatto C, JS a livello base lo gestisco, quindi penso di partire già messo bene