[Python] Ereditarietà tra classi: un semplice esempio pratico da chiarire

2014-11-21 Per discussione Marco Ippolito
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

2014-11-21 Per discussione Daniele Varrazzo

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

2014-11-21 Per discussione Daniele Varrazzo

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

2014-11-21 Per discussione Daniele Varrazzo

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

2014-11-21 Per discussione Marco Ippolito
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,