[Python] Ereditarietà tra classi: un semplice esempio pratico da chiarire
Ciao a tutti, con l'obiettivo di capire fino in fondo alcuni aspetti dell'ereditarietà tra classi che non mi sono ancora chiari (pensavo lo fossero), mi sono scritto un esempio forzatamente banale. #!/usr/bin/python class Super: def __init__(self, nome, cognome, citta): self.nome = nome self.cognome = cognome self.citta = citta self.nome_intero = '%s %s' % (self.nome, self.cognome) def super_meth_1(self): return '%s abita a %s' % (self.nome_intero, self.citta) def get_super_result(self): return self.super_meth_1() class Sub(Super): def __init__(self, indirizzo,cosa_fa): Super.__init__(self, nome, cognome, indirizzo) self.cosa_fa = cosa_fa def sub_meth_1(self): return '%s %s' % (self.nome_intero, self.cosa_fa) def get_sub_meth_1(self): return self.sub_meth_1() if __name__ == '__main__': nome = 'Marco' cognome = 'Ippolito' abita = 'Milano' super_class = Super(nome, cognome, abita) ris_super = super_class.get_super_result() print 'ris_super: ', ris_super cosa = 'suona' sub = Sub(abita, cosa) ris_sub_1 = sub.get_sub_meth_1() print ris_sub_1: , ris_sub_1 ris_sub_2 = sub.get_super_result() print ris_sub_2: , ris_sub_2 time ./classInheritage2.py ris_super: Marco Ippolito abita a Milano ris_sub_1: Marco Ippolito suona ris_sub_2: Marco Ippolito abita a Milano ora... se modifico la Sub class in questo modo: #!/usr/bin/python class Super: def __init__(self, nome, cognome, citta): self.nome = nome self.cognome = cognome self.citta = citta self.nome_intero = '%s %s' % (self.nome, self.cognome) def super_meth_1(self): return '%s abita a %s' % (self.nome_intero, self.citta) def get_super_result(self): return self.super_meth_1() class Sub(Super): def __init__(self, cosa_fa): Super.__init__(self, nome, cognome, citta) self.cosa_fa = cosa_fa def sub_meth_1(self): return '%s %s' % (self.nome_intero, self.cosa_fa) def get_sub_meth_1(self): return self.sub_meth_1() if __name__ == '__main__': nome = 'Marco' cognome = 'Ippolito' abita = 'Milano' super_class = Super(nome, cognome, abita) ris_super = super_class.get_super_result() print 'ris_super: ', ris_super cosa = 'suona' sub = Sub(cosa) ris_sub_1 = sub.get_sub_meth_1() print ris_sub_1: , ris_sub_1 ris_sub_2 = sub.get_super_result() print ris_sub_2: , ris_sub_2 l'ouput risulta: ris_super: Marco Ippolito abita a Milano Traceback (most recent call last): File ./classInheritage3.py, line 37, in module sub = Sub(cosa) File ./classInheritage3.py, line 18, in __init__ Super.__init__(self, nome, cognome, citta) NameError: global name 'citta' is not defined Cosa differenzia l'attributo citta dagli altri attributi (nome e cognome) anche essi presenti in Super class? Qual è il modo corretto per far sì che Sub class erediti da Super class gli attributi nome, cognome e citta ? Marco ___ Python mailing list Python@lists.python.it http://lists.python.it/mailman/listinfo/python
Re: [Python] Ereditarietà tra classi: un semplice esempio pratico da chiarire
On 2014-11-21 17:19, Marco Ippolito wrote: class Sub(Super): def __init__(self, indirizzo,cosa_fa): Super.__init__(self, nome, cognome, indirizzo) self.cosa_fa = cosa_fa ... ora... No, anche prima... se modifico la Sub class in questo modo: class Sub(Super): def __init__(self, cosa_fa): Super.__init__(self, nome, cognome, citta) self.cosa_fa = cosa_fa Ne' questo ne' quello di sopra sono corretti. Forse hai delle variabili globali che accidentalmente si chiamano nome e cognome ma non ne hai una che si chiama citta. nome e cognome sono variabili locali: le devi passare al costruttore di sub, che nel primo caso potrebbe essere ad esempio: class Sub(Super): def __init__(self, nome, cognome, indirizzo, cosa_fa): Super.__init__(self, nome, cognome, indirizzo) self.cosa_fa = cosa_fa Il secondo caso funziona bene, ovvero ti da` un errore. Anche il primo dovrebbe. -- Daniele ___ Python mailing list Python@lists.python.it http://lists.python.it/mailman/listinfo/python
Re: [Python] Ereditarietà tra classi: un semplice esempio pratico da chiarire
On 2014-11-21 17:35, Daniele Varrazzo wrote: On 2014-11-21 17:19, Marco Ippolito wrote: class Sub(Super): def __init__(self, indirizzo,cosa_fa): Super.__init__(self, nome, cognome, indirizzo) self.cosa_fa = cosa_fa ... ora... No, anche prima... se modifico la Sub class in questo modo: class Sub(Super): def __init__(self, cosa_fa): Super.__init__(self, nome, cognome, citta) self.cosa_fa = cosa_fa Ne' questo ne' quello di sopra sono corretti. Forse hai delle variabili globali che accidentalmente si chiamano nome e cognome ma non ne hai una che si chiama citta. Ah si`, giusto: le variabili sono definite qui: if __name__ == '__main__': nome = 'Marco' cognome = 'Ippolito' Il costruttore accidentalmente pesca questi valori dal namespace globale. Questo e` un comportamento sbagliato. Chiamale in un altro modo e vedrai che avrai un'eccezione anche nel primo caso. -- Daniele ___ Python mailing list Python@lists.python.it http://lists.python.it/mailman/listinfo/python
Re: [Python] Ereditarietà tra classi: un semplice esempio pratico da chiarire
On 2014-11-21 17:53, Marco Ippolito wrote: Ciao Daniele, ti ringrazio per l'aiuto. Ho modificato l'esempio, passando tutte le variabili locali (nome, cognome, citta) al costruttore di sub. E così funziona (come vedi sotto). #!/usr/bin/python class Super: def __init__(self, nome, cognome, indirizzo): self.nome = nome self.cognome = cognome self.indirizzo = indirizzo self.nome_intero = '%s %s' % (self.nome, self.cognome) def super_meth_1(self): return '%s abita in %s' % (self.nome_intero, self.indirizzo) def get_super_result(self): return self.super_meth_1() class Sub(Super): def __init__(self, nome, cognome, indirizzo, cosa_fa): Super.__init__(self, nome, cognome, indirizzo) self.cosa_fa = cosa_fa def sub_meth_1(self): return '%s %s' % (self.nome_intero, self.cosa_fa) def get_sub_meth_1(self): return self.sub_meth_1() if __name__ == '__main__': nome_f = 'Marco' cognome_f = 'Ippolito' abita_f = 'Milano' super_class = Super(nome_f, cognome_f, abita_f) ris_super = super_class.get_super_result() print ris_super: , ris_super cosa_f = 'suona' sub = Sub(nome_f, cognome_f, abita_f, cosa_f) ris_sub_1 = sub.get_sub_meth_1() print ris_sub_1: , ris_sub_1 ris_sub_2 = sub.get_super_result() print ris_sub_2: , ris_sub_2 ./classInheritage.py ris_super: Marco Ippolito abita in Milano ris_sub_1: Marco Ippolito suona ris_sub_2: Marco Ippolito abita in Milano C'è un modo per passare far sì che nel main passi a Sub solo le variabili che lo differenziano (specializzano) rispetto a Super? Cioè c'è un modo per far sì che io possa passare in main solo la variabile cosa_f? Nel caso in esempio si fa male. Il pattern migliore e` quello di elencare gli argomenti. La cosa comincia ad essere utile se stabilisci che i costruttori delle tue classi debbano essere chiamati solo con argomenti keyword, ovvero, se stabilisci che chiamerai sub = Sub(nome=nome_f, cognome=cognome_f, abita=abita_f, cosa_fa=cosa_f) allora puoi usare **kwargs e fare: class Super: def __init__(self, nome, cognome, indirizzo): self.nome = nome self.cognome = cognome self.indirizzo = indirizzo self.nome_intero = '%s %s' % (self.nome, self.cognome) class Sub(Super): def __init__(self, cosa_fa, **kwargs): Super.__init__(self, **kwargs) self.cosa_fa = cosa_fa considerando che tipicamente definirai il costruttore solo in un punto ma lo chiamerai in diversi punti ti conviene essere piu` verboso nelle definizioni degli __init__ e risparmiare nell'invocazioe, quindi lasciare le cose come stanno. Questa non e` l'unica cosa che si potrebbe tenere in considerazione pero`. Il tuo e` un esempio di studio, che non ha veri vincoli. In casi piu` reali potresti avere una gerarchia di oggetti dove pochi argomenti (tipo fino a 3, ma meno sono e meglio e`) andranno *sempre* specificati per ogni oggetto della gerarchia, mentre ci puo` essere un turbinare di argomenti che sono a) opzionali e b) specifici solo di certe sottoclassi. In questo caso il modo migliore (secondo me, YMMV) di organizzare il codice e` quello di passare gli argomenti fondamentali in maniera posizionale (potrebbero anche essere passati con keyword, l'importante e` mantenere la possibilita` di fare entrambe le cose) e usare **kwargs per tutti gli altri, in modo da ignorare quelli che non si conoscono ma di propagarli. Per esempio (non testato, ovviamente): class Persona(object): def __init__(self, nome): self.nome = nome class Lavoratore(Persona): def __init__(self, nome, ruolo, salario=None, **kwargs): super(Lavoratore, self).__init__(nome, **kwargs) self.ruolo = ruolo self.salario = salario class Studente(Persona): def __init__(self, nome, scuola, classe=None, **kwargs): super(Studente, self).__init__(nome, **kwargs) self.scuola = ... class StudenteUniversitario(Studente): def __init__(self, nome, fuoricorso_dal=None, **kwargs): super(StudenteUniversitario, self).__init__(nome, **kwargs) ... Il nome puoi passarlo con keyword o meno, gli altri argomenti devono avere una keyword. p1 = StudenteUniversitario(Tizio Caio, scuola=Anormale, fuoricorso_dal=1996) p2 = Lavoratore(nome=Pinco Pallini, ruolo=...) Avere classi che richiedono un gran numero di parametri posizionali da passare non e` una buona cosa: e` facile sbagliare nel passaggio. Se gli argomenti cominciano ad essere tanti e` meglio richiedere che vengano passati con keyword (ma e` ancora meglio chiedersi come mai ci siano tanti parametri e cambiare qualcosa nel codice). -- Daniele ___ Python mailing list Python@lists.python.it
Re: [Python] Ereditarietà tra classi: un semplice esempio pratico da chiarire
Mille grazie Daniele delle tue interessanti spiegazioni. Ammetto che non sapevo che si potesse usare un costrutto del tipo: class NomeClasse(Super): def __init__(self, variabili,., **kwargs): super(NomeClasse, self).__init__(variab_super, **kwargs) Hai ragione nel dire che se vengono passati tanti parametri alla classe, vuol dire che bisogna riguardare il codice, e magari suddividerlo in più classi. Marco Il 21 novembre 2014 20:41, Daniele Varrazzo p...@develer.com ha scritto: On 2014-11-21 17:53, Marco Ippolito wrote: Ciao Daniele, ti ringrazio per l'aiuto. Ho modificato l'esempio, passando tutte le variabili locali (nome, cognome, citta) al costruttore di sub. E così funziona (come vedi sotto). #!/usr/bin/python class Super: def __init__(self, nome, cognome, indirizzo): self.nome = nome self.cognome = cognome self.indirizzo = indirizzo self.nome_intero = '%s %s' % (self.nome, self.cognome) def super_meth_1(self): return '%s abita in %s' % (self.nome_intero, self.indirizzo) def get_super_result(self): return self.super_meth_1() class Sub(Super): def __init__(self, nome, cognome, indirizzo, cosa_fa): Super.__init__(self, nome, cognome, indirizzo) self.cosa_fa = cosa_fa def sub_meth_1(self): return '%s %s' % (self.nome_intero, self.cosa_fa) def get_sub_meth_1(self): return self.sub_meth_1() if __name__ == '__main__': nome_f = 'Marco' cognome_f = 'Ippolito' abita_f = 'Milano' super_class = Super(nome_f, cognome_f, abita_f) ris_super = super_class.get_super_result() print ris_super: , ris_super cosa_f = 'suona' sub = Sub(nome_f, cognome_f, abita_f, cosa_f) ris_sub_1 = sub.get_sub_meth_1() print ris_sub_1: , ris_sub_1 ris_sub_2 = sub.get_super_result() print ris_sub_2: , ris_sub_2 ./classInheritage.py ris_super: Marco Ippolito abita in Milano ris_sub_1: Marco Ippolito suona ris_sub_2: Marco Ippolito abita in Milano C'è un modo per passare far sì che nel main passi a Sub solo le variabili che lo differenziano (specializzano) rispetto a Super? Cioè c'è un modo per far sì che io possa passare in main solo la variabile cosa_f? Nel caso in esempio si fa male. Il pattern migliore e` quello di elencare gli argomenti. La cosa comincia ad essere utile se stabilisci che i costruttori delle tue classi debbano essere chiamati solo con argomenti keyword, ovvero, se stabilisci che chiamerai sub = Sub(nome=nome_f, cognome=cognome_f, abita=abita_f, cosa_fa=cosa_f) allora puoi usare **kwargs e fare: class Super: def __init__(self, nome, cognome, indirizzo): self.nome = nome self.cognome = cognome self.indirizzo = indirizzo self.nome_intero = '%s %s' % (self.nome, self.cognome) class Sub(Super): def __init__(self, cosa_fa, **kwargs): Super.__init__(self, **kwargs) self.cosa_fa = cosa_fa considerando che tipicamente definirai il costruttore solo in un punto ma lo chiamerai in diversi punti ti conviene essere piu` verboso nelle definizioni degli __init__ e risparmiare nell'invocazioe, quindi lasciare le cose come stanno. Questa non e` l'unica cosa che si potrebbe tenere in considerazione pero`. Il tuo e` un esempio di studio, che non ha veri vincoli. In casi piu` reali potresti avere una gerarchia di oggetti dove pochi argomenti (tipo fino a 3, ma meno sono e meglio e`) andranno *sempre* specificati per ogni oggetto della gerarchia, mentre ci puo` essere un turbinare di argomenti che sono a) opzionali e b) specifici solo di certe sottoclassi. In questo caso il modo migliore (secondo me, YMMV) di organizzare il codice e` quello di passare gli argomenti fondamentali in maniera posizionale (potrebbero anche essere passati con keyword, l'importante e` mantenere la possibilita` di fare entrambe le cose) e usare **kwargs per tutti gli altri, in modo da ignorare quelli che non si conoscono ma di propagarli. Per esempio (non testato, ovviamente): class Persona(object): def __init__(self, nome): self.nome = nome class Lavoratore(Persona): def __init__(self, nome, ruolo, salario=None, **kwargs): super(Lavoratore, self).__init__(nome, **kwargs) self.ruolo = ruolo self.salario = salario class Studente(Persona): def __init__(self, nome, scuola, classe=None, **kwargs): super(Studente, self).__init__(nome, **kwargs) self.scuola = ... class StudenteUniversitario(Studente): def __init__(self, nome, fuoricorso_dal=None, **kwargs): super(StudenteUniversitario, self).__init__(nome, **kwargs) ... Il nome puoi passarlo con keyword o meno, gli altri argomenti devono avere una keyword. p1 = StudenteUniversitario(Tizio Caio,