a = 2 + 3j
print(a, type(a))

"""
Explanation: Numero complessi builtin
L'esempio più semplice in Python di istanza di una classe in Python è un oggetto che rappresenta un numero complesso.
End of explanation
"""


class NumeroComplesso(object):
    def __init__(self, real, imag):
        """Metodo costruttore, chiamato quando viene 
           inizializzato un nuovo oggetto"""
        self.a = real
        self.b = imag
        
    def somma(self, c):
        """Somma al numero corrente il numero complesso c"""
        # DA COMPLETARE
        pass
        
    def __str__(self):
        """Ritorna una stringa che rappresenta il numero"""
        return str(self.a) + ' + ' + str(self.b) +'i'

type(NumeroComplesso)

a = NumeroComplesso(2,3)
b = NumeroComplesso(1,-2)
print(a)
a.somma(b)
print(a)

"""
Explanation: Definizione di un nuovo ADT: NumeroComplesso
Vediamo ora come aggiungere un nuovo tipo che sia una nostra implementazione di un numero complesso. Una possibile implementazione di base è la seguente. Si noti che qualsiasi oggetto in Python eredità dall'oggetto base object.
ESERCIZIO 1: Completare il metodo somma nel codice seguente.
End of explanation
"""


a.a = 0
a.somma(b)
print(a)

"""
Explanation: Danger ZONE !!!
End of explanation
"""


class NCO(NumeroComplesso):
    # EREDITA DI I METODI E ATTRIBUTI DELLA CLASSE "NUMERO COMPLESSO"
    def __add__(self, c):
        """Esempio di OPERATOR OVERLOADING: addizione"""
        # DA COMPLETARE
        pass
    
    def __eq__(self, c):
        """Esempio di OPERATOR OVERLOADING: confronto"""
        return self.a == c.a and self.b == c.b

c = NCO(1,2)
print(c)

type(c.somma)

type(c.a)

a == c

d = NCO(1,2)
print(c, id(c))
print(d, id(d))
print(d == c)

"""
Explanation: Inheritance e Operator Overloading
Supponiamo ora di definire un nuovo tipo chiamato NCO che rappresenta un numero complesso, ma su cui vogliamo specificare l'overloading di due operazione:

La somma di due numeri complessi
L'operatore di confronto logico di uguaglianza tra due numeri complessi

Per specificare che la classe NCO estende la classe base NumeroComplesso basta dichiararla come:
class NCO(NumeroComplesso):
Si notiche si è racchiuso tra parentesi il nomde del tipo da cui si ereditano attributi e metodi.
ESERCIZIO 2: Si completi il metodo __add__ nella classe NCO in modo che restituisca un nuovo oggetto che, dati due numeri a e b, rappresenti il numero complesso c = a + b.
End of explanation
"""


# Definizione di una classe che implementa un "adder"
class Adder(object):
    def __init__(self, n=0):
        self.n = n   # Stato mutabile della classe (DANGER ZONE!!!)
    def __call__(self, m):
        return self.n + m
    
add5_istanza = Adder(5)
print(add5_istanza(1), add5_istanza(7))

# Definizione di una closure che implementa un "adder"
def make_adder(n=0):
    def adder(m):
        return n + m
    return adder
add5_function = make_adder(5)
print(add5_function(1), add5_function(7))

add5_istanza.n = 1
print(add5_istanza(1), add5_istanza(7))
print(add5_function(1), add5_function(7))

"""
Explanation: Classes vs. Closures
End of explanation
"""


# Definizione di una classe che implementa un "counter"
class Counter(object):
    def __init__(self, n=0):
        self.n = n
    def __call__(self):
        self.n += 1
        return self.n
    
counter_istanza = Counter(5)
print(counter_istanza(), counter_istanza())

# Definizione di una closure che implementa un "counter"
def make_counter(n=0):
    def state():
        c = n
        while True:
            c += 1
            yield c
        
    def counter():
        return next(f)
        
    f = state()
    
    return counter

counter_function = make_counter(5)
print(counter_function(), counter_function())

# faccio una modifica apparentemente "innocua"
counter_istanza.n = 'ciao'

print(counter_function(), counter_function())

# ... dopo un po` riutilizzo il counter_istanza
print(counter_istanza())

"""
Explanation: NOTA: Con gli oggetti per poter capire l'esecuzione del codice bisogna capire qual'è stata la storia di esecuzione precedente...
End of explanation
"""


