{
"cells": [
{
"cell_type": "markdown",
"metadata": {},
"source": [
"Object Oriented Programming (OOP) e Classi in Python\n",
"===================================================\n",
"\n",
"Introduzione\n",
"------------\n",
"Questo notebook si basa sulle lezioni 8 e 9 di:\n",
"https://ocw.mit.edu/courses/electrical-engineering-and-computer-science/6-0001-introduction-to-computer-science-and-programming-in-python-fall-2016/\n",
"\n",
"Python contiene molti tipi di dati:\n",
"- 1234 int\n",
"- 0.27 float\n",
"- 'Hello' str\n",
"- [1,2,3,4] list\n",
"- {1:'a', 2:'b', 3:'c'} dict\n",
"- numpy.array([1,2]) numpy arrays\n",
"\n",
"Ciascuno è un **oggetto**, caratterizzato da \n",
"- un **tipo** (`type`)\n",
"- una **rappresentazione interna**, tipicamente non nota e non modificabile dall'utilizzatore\n",
"- un set di procedure che permettono di interagire con gli **esemplari** (`instance`) di un oggetto\n",
"\n",
"- 1234 e 356 sono esemplari di int\n",
"- 'Hello' e 'World' sono esemplari di str\n",
"\n",
"- 1234 + 356 è la procedura che permette di sommare due interi\n",
"- 'Hello' + 'World' è la procedura che permette di concatenare due stringhe\n",
"\n",
"### In Python **TUTTO È UN OGGETTO**\n",
"- ha un **tipo**\n",
"- può essere **creato**\n",
"- può essere **manipolato**\n",
"- può essere **distrutto** (cancellato dalla memoria)\n",
"\n",
"OOP permette di separare la rappresentzione interna dall'interfaccia attraverso cui interagire con gli oggetti e dai dettagli delle manipolazioni a cui gli oggetti possono essere soggetti: dato un **esemplare** di **oggetto** `automobile` una delle sue caratteristiche è la `velocità`. Per ottenere che l'esemplare aumenti la sua velocità devo devo applicare la procedura `aumentare la pressione sul pedale dell'acceleratore`. Tutti gli esemplari di oggetto `automobile` aumentano la propria velocità usando la stessa procedura. Non ho bisogna di conoscere i dettagli di come un aumento della pressione sull'acceleratore porta al risultato di aumentare la velocità. Un eventuale miglioramento dei meccanismi interni non richiede una modifica dell'interfaccia `pedale dell'acceleratore` e quindi del modo con cui l'utilizzatore interagisce con l'esemplare.\n",
"\n",
"Bisogna distinguere chiaramente fra **creare una classe** e **utilizzare un esemplare di una classe**:\n",
"- Creare una classe richiede:\n",
" - Definire il nome della classe\n",
" - Definire gli attributi (dati e metodi) della classe\n",
"- Utilizzare un esemplare della classe richiede:\n",
" - Creare degli esemplari della classe\n",
" - Eseguire operazioni sugli esemplari\n",
" \n",
"## Creare una classe"
]
},
{
"cell_type": "code",
"execution_count": 3,
"metadata": {},
"outputs": [],
"source": [
"#keyword nome classe da cui eredita (opzionale)\n",
"\n",
"class Coordinate(object):\n",
" # definire qui gli attributi (dati e procedure)\n",
"\n",
" def __init__(self,p,q): # metodo indispensabile. Inizializza gli esemplari. \n",
" # a self viene sostituito il nome assegnato a ogni esemplare\n",
" # p e p sono input che vengono assegnati agli attributi `x` e `y`\n",
" self.x = p \n",
" self.y = q"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"## Utilizzare esemplari della classe"
]
},
{
"cell_type": "code",
"execution_count": 4,
"metadata": {},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"3\n",
"0\n"
]
}
],
"source": [
"c = Coordinate(3,4)\n",
"origin = Coordinate(0,0)\n",
"# su `c` e `origin` possiamo utilizzare solo i metodi `x` e `y`\n",
"print(c.x)\n",
"print(origin.x)"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"Aggiungiamo alla classe `Coordinate` un metodo per calcolare la distanza fra due punti. Uno dei due punti viene identificato con `self` in modo da poter chiamare il metodo su un singolo punto.
\n",
"A parte l'uso di `self` e della notazione `.` per la chiamata, i metodi interni ad una classe si comportano esattamente come funzioni: prendono input, eseguono operazioni, restituiscono risultati."
]
},
{
"cell_type": "code",
"execution_count": 5,
"metadata": {},
"outputs": [],
"source": [
"class Coordinate(object):\n",
"\n",
" def __init__(self,p,q): \n",
" self.x = p \n",
" self.y = q\n",
" \n",
" def distance(self, other):\n",
" x_diff_sq = (self.x-other.x)**2\n",
" y_diff_sq = (self.y-other.y)**2\n",
" return (x_diff_sq + y_diff_sq)**0.5"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"#### Come usare i metodi di una classe"
]
},
{
"cell_type": "code",
"execution_count": 7,
"metadata": {},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"5.0\n"
]
}
],
"source": [
"c = Coordinate(3,4)\n",
"zero = Coordinate(0,0)\n",
"print(c.distance(zero)) # c viene automaticamente assegnato a `self`\n",
" # essendo c un esemplare di Coordinate, `distance` è automaticamente disponibile"
]
},
{
"cell_type": "code",
"execution_count": 9,
"metadata": {},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"5.0\n"
]
}
],
"source": [
"c = Coordinate(3,4)\n",
"zero = Coordinate(0,0)\n",
"print(Coordinate.distance(c, zero)) # `distance` può essere chiamata come una funzione contenuta nel modulo `Coordinate`\n",
" # gli oggetti che vengono passati come input devono essere entrambi esemplari di `Coordinate`;\n",
" # In caso contrario `distance` non potrebbe accedere alle proprietà `x` e `y` degli oggetti"
]
},
{
"cell_type": "code",
"execution_count": 10,
"metadata": {},
"outputs": [
{
"data": {
"text/plain": [
"<__main__.Coordinate at 0x103500e80>"
]
},
"execution_count": 10,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"r = (2,3)\n",
"s = (0,0)\n",
"Coordinate(r,s)"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"#### Stampare un esemplare di una classe e metodo `__str__`"
]
},
{
"cell_type": "code",
"execution_count": 11,
"metadata": {},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"<__main__.Coordinate object at 0x1035000a0>\n"
]
}
],
"source": [
"c = Coordinate(3,4)\n",
"print(c)"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"Il comportamento di default di `print` su un membro di una classe non è molto utile. Può essere modificato definendo il metodo speciale __str__. Supponiamo di volere che `print(c)` restituisca <3,4>. Possiamo fare così:"
]
},
{
"cell_type": "code",
"execution_count": 12,
"metadata": {},
"outputs": [],
"source": [
"class Coordinate(object):\n",
"\n",
" def __init__(self,p,q): \n",
" self.x = p \n",
" self.y = q\n",
" \n",
" def distance(self, other):\n",
" x_diff_sq = (self.x-other.x)**2\n",
" y_diff_sq = (self.y-other.y)**2\n",
" return (x_diff_sq + y_diff_sq)**0.5\n",
" \n",
" def __str__(self):\n",
" return \"<\"+str(self.x)+\",\"+str(self.y)+\">\""
]
},
{
"cell_type": "code",
"execution_count": 14,
"metadata": {},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"<3,4>\n"
]
}
],
"source": [
"c = Coordinate(3,4)\n",
"print(c)"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"Esistono molti altri metodi speciali. Per esempio:\n",
"``` python\n",
"__add__(self, other) --> self + other\n",
"__sub__(self, other) --> self - other\n",
"__eq__(self, other) --> self == other\n",
"__lt__(self, other) --> self < other\n",
"__len__(self) --> len(self)\n",
"```\n",
"Ciascuno di questi metodi può essere ridefinito come abbiamo fatto per ```__str__```."
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"#### Un esempio piú elaborato\n",
"Definiamo una classe `Fraction` che rappresenti una frazione come una coppia numeratore-denominatore e permetta di eseguire le operazioni fra frazioni in modo esatto, senza trasformarle in floating point."
]
},
{
"cell_type": "code",
"execution_count": 45,
"metadata": {},
"outputs": [],
"source": [
"# Better solution in https://stackoverflow.com/questions/36539460/implementing-negation-by-overriding-neg-in-python\n",
"class Fraction():\n",
" \n",
" # missing check that denominators are non zero and that input are integers\n",
" def __init__(self,n,d=1): # default value for d \n",
" # built in simplification\n",
" for div in range(2,min(abs(n),abs(d))+1): # Notice start- and end-point\n",
" # abs needed for negative fractions\n",
" while n%div==0 and d%div==0: # can have multiple powers of div\n",
" n=n/div\n",
" d=d/div\n",
" self.n = int(n)\n",
" self.d = int(d)\n",
" \n",
" def __str__(self):\n",
" if self.d ==1:\n",
" return str(self.n) # don't print denominator if equal 1\n",
" else:\n",
" return str(self.n)+\"/\"+str(self.d)\n",
" \n",
" def __add__(self, other): # defines self + other\n",
" nr = self.n*other.d + self.d*other.n\n",
" dr = self.d*other.d\n",
" res = Fraction(nr,dr)\n",
" return res\n",
"\n",
" def __sub__(self, other): # defines self - other\n",
" nr = self.n*other.d - self.d*other.n\n",
" dr = self.d*other.d\n",
" res = Fraction(nr,dr)\n",
" return res\n",
" \n",
" def __mul__(self, other): # defines self*other\n",
" nr = self.n*other.n\n",
" dr = self.d*other.d\n",
" res = Fraction(nr,dr)\n",
" return res\n",
"\n",
" def __truediv__(self, other): # defines self/other\n",
" nr = self.n*other.d\n",
" dr = self.d*other.n\n",
" res = Fraction(nr,dr)\n",
" return res\n",
"\n",
" def __neg__(self): # defines - self\n",
" nr = -self.n\n",
" dr = self.d\n",
" res = Fraction(nr,dr)\n",
" return res\n",
" \n",
" def __eq__(self, other): # defines self==other\n",
" if self.n == other.n and self.d == other.d:\n",
" return True\n",
" else:\n",
" return False\n",
" \n",
" \n",
" "
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"Alcuni esempi di utilizzo della classe `Fraction`."
]
},
{
"cell_type": "code",
"execution_count": 25,
"metadata": {},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"2/3\n"
]
}
],
"source": [
"a = Fraction(4,6)\n",
"print(a)"
]
},
{
"cell_type": "code",
"execution_count": 11,
"metadata": {},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"1/3\n"
]
}
],
"source": [
"b = Fraction(3,9)\n",
"print(b)"
]
},
{
"cell_type": "code",
"execution_count": 4,
"metadata": {},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"1/1\n"
]
}
],
"source": [
"print(a+b)"
]
},
{
"cell_type": "code",
"execution_count": 5,
"metadata": {},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"-1/3\n"
]
}
],
"source": [
"print(b-a)"
]
},
{
"cell_type": "code",
"execution_count": 82,
"metadata": {},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"False\n"
]
}
],
"source": [
"print(a==b)"
]
},
{
"cell_type": "code",
"execution_count": 83,
"metadata": {},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"True\n"
]
}
],
"source": [
"print(a==a)"
]
},
{
"cell_type": "code",
"execution_count": 12,
"metadata": {},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"2/9\n"
]
}
],
"source": [
"print(a*b)"
]
},
{
"cell_type": "code",
"execution_count": 13,
"metadata": {},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"2/1\n"
]
}
],
"source": [
"print(a/b)"
]
},
{
"cell_type": "code",
"execution_count": 54,
"metadata": {},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"-2/3\n",
"-2\n",
"type: \n",
"value of minusa: -2/3 \n",
"numerator: -2 \n",
"denominator: 3\n"
]
}
],
"source": [
"print(a.__neg__())\n",
"print(a.__neg__().n)\n",
"minusa =-a\n",
"print('type:',type(minusa),'\\nvalue of minusa:',minusa,'\\nnumerator:',minusa.n,'\\ndenominator:',minusa.d)\n"
]
},
{
"cell_type": "code",
"execution_count": 23,
"metadata": {},
"outputs": [],
"source": [
"f = Fraction(3)"
]
},
{
"cell_type": "code",
"execution_count": 24,
"metadata": {},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"3\n"
]
}
],
"source": [
"print(f)"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"#### Accedere e modificare i dati di un esemplare: getters and setters\n",
"\n",
"Abbiamo visto che se nell\\' `__init__` di una classe `C` è definito l'attributo `x` è possibile accedere al valore dell'attributo per un esemplare `es` con la notazione `es.x`.
\n",
"È però preferibile e fortemente incoraggiato accedere e modificare, dall'esterno della classe, gli attributi di un esemplare attraverso metodi appositi, detti getters e setters, che devono essere definiti all'interno della classe."
]
},
{
"cell_type": "code",
"execution_count": 36,
"metadata": {},
"outputs": [],
"source": [
"class Animal(object):\n",
" def __init__(self, age): # è possibile e necessario dichiarare solamente `age` quando una instance viene creata \n",
" self.age = age\n",
" self.name = None\n",
"\n",
" def get_age(self): # getter. Restituiscono l'informazione. Posso stamparla o manipolarla ulteriormente.\n",
" return self.age\n",
" \n",
" def get_name(self): # getter\n",
" return self.name\n",
"\n",
" def set_age(self, newage): # setter\n",
" self.age = newage\n",
"\n",
" def set_name(self, newname=\"\"): # setter con valore di default\n",
" self.name = newname\n",
" \n",
" def __str__(self):\n",
" return \"animal:\"+str(self.name)+\":\"+str(self.age)"
]
},
{
"cell_type": "code",
"execution_count": 37,
"metadata": {},
"outputs": [],
"source": [
"cane = Animal(3)"
]
},
{
"cell_type": "code",
"execution_count": 40,
"metadata": {},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"age: 3\n",
"name: None\n"
]
}
],
"source": [
"print('age:',cane.get_age())\n",
"print('name:',cane.get_name())"
]
},
{
"cell_type": "code",
"execution_count": 43,
"metadata": {},
"outputs": [],
"source": [
"cane.set_name('Fido')"
]
},
{
"cell_type": "code",
"execution_count": 44,
"metadata": {},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"name: Fido\n"
]
}
],
"source": [
"print('name:',cane.get_name())"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"Una delle idee basilari dell'Object Oriented programmi è di interagire con gli oggetti **solamente** attraverso le funzioni della classe, nascondendo i dettagli interni dell'implementazione.\n",
"\n",
"## Inheritance\n",
"Una classe può ereditare metodi e attributi da una superclasse già esistente.\n",
"\n",
"\n",
"

\n",
"
\n",
"\n",
"Dalla (super)classe `Animal` possiamo costruire sottoclassi che possiedono metodi e attributi particolari.\n",
"\n",
"#### Esempio 1: \n",
"Una sottoclasse con due metodi addizionali e una implementazione diversa del metodo `__str__`:"
]
},
{
"cell_type": "code",
"execution_count": 67,
"metadata": {},
"outputs": [],
"source": [
"class Cat(Animal): # `Cat` eredita tutti gli attributi e i metodi di `Animal`\n",
"\n",
" def speak(self): # Il metodo `speak` stampa un' informazione. Restituisce `None`.\n",
" print(\"meow\")\n",
" \n",
" def purr(self):\n",
" print(\"Purr\")\n",
"\n",
" def __str__(self):\n",
" return \"cat:\"+str(self.name)+\":\"+str(self.age)"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"Tutti i metodi di `Animal` sono applicabili a una instance di `Cat`:"
]
},
{
"cell_type": "code",
"execution_count": 68,
"metadata": {},
"outputs": [],
"source": [
"gatto = Cat(5)\n",
"gatto.set_name('Fuffy')"
]
},
{
"cell_type": "code",
"execution_count": 69,
"metadata": {},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"age: 5\n",
"name: Fuffy\n"
]
}
],
"source": [
"print('age:',gatto.get_age())\n",
"print('name:',gatto.get_name())"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"Metodi nuovi o modificati"
]
},
{
"cell_type": "code",
"execution_count": 70,
"metadata": {},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"meow\n",
"Purr\n",
"cat:Fuffy:5\n"
]
}
],
"source": [
"gatto.speak()\n",
"gatto.purr()\n",
"print(gatto)"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"#### Esempio 2: \n",
"Una sottoclasse con metodi e attributi addizionali:"
]
},
{
"cell_type": "code",
"execution_count": 72,
"metadata": {},
"outputs": [],
"source": [
"class Person(Animal): # `Person` eredita tutti gli attributi e i metodi di `Animal`. Non eredita da `Cat`.\n",
" \n",
" def __init__(self, name, age): # La creazione di una instance di `Person` richiede un nome e un'età\n",
" Animal.__init__(self, age)\n",
" self.set_name(name)\n",
" self.friends = [] # attributo non presente nella superclasse\n",
"\n",
" def get_friends(self): # getter \n",
" return self.friends\n",
" \n",
" def add_friend(self, fname): # setter appropriato per una lista\n",
" if fname not in self.friends:\n",
" self.friends.append(fname)\n",
"\n",
" def speak(self): # metodo con nome identico ad un metodo di `Cat` ma implementazione diversa\n",
" print(\"hello\")\n",
" \n",
" def age_diff(self, other):\n",
" diff = self.age - other.age\n",
" print(abs(diff), \"year difference\")\n",
"\n",
" def __str__(self):\n",
" return \"person:\"+str(self.name)+\":\"+str(self.age)"
]
},
{
"cell_type": "code",
"execution_count": 75,
"metadata": {},
"outputs": [],
"source": [
"p1 = Person(\"Bob\",22)\n",
"p1.add_friend(\"Carol\")\n",
"p1.add_friend(\"George\")"
]
},
{
"cell_type": "code",
"execution_count": 76,
"metadata": {},
"outputs": [
{
"data": {
"text/plain": [
"['Carol', 'George']"
]
},
"execution_count": 76,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"p1.get_friends()"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"Una `Person` non può fare le fusa:"
]
},
{
"cell_type": "code",
"execution_count": 77,
"metadata": {},
"outputs": [
{
"ename": "AttributeError",
"evalue": "'Person' object has no attribute 'purr'",
"output_type": "error",
"traceback": [
"\u001b[0;31m---------------------------------------------------------------------------\u001b[0m",
"\u001b[0;31mAttributeError\u001b[0m Traceback (most recent call last)",
"\u001b[0;32m\u001b[0m in \u001b[0;36m\u001b[0;34m\u001b[0m\n\u001b[0;32m----> 1\u001b[0;31m \u001b[0mp1\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mpurr\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0m",
"\u001b[0;31mAttributeError\u001b[0m: 'Person' object has no attribute 'purr'"
]
}
],
"source": [
"p1.purr()"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"#### Esempio 3: \n",
"Una sottoclasse che eredita da un'altra sottoclasse con metodi e attributi addizionali e/o modificati"
]
},
{
"cell_type": "code",
"execution_count": 82,
"metadata": {},
"outputs": [],
"source": [
"import random\n",
"\n",
"class Student(Person): # `Student` eredita metodi e attributi da `Person`\n",
"\n",
" def __init__(self, name, age, major=None):\n",
" Person.__init__(self, name, age)\n",
" self.major = major\n",
"\n",
" def get_major(self): # getter \n",
" return self.major\n",
" \n",
" def change_major(self, major): # setter\n",
" self.major = major\n",
"\n",
" def speak(self):\n",
" r = random.random()\n",
" if r < 0.25:\n",
" print(\"I have homework\")\n",
" elif 0.25 <= r < 0.5:\n",
" print(\"I need sleep\")\n",
" elif 0.5 <= r < 0.75:\n",
" print(\"I should eat\")\n",
" else:\n",
" print(\"I am watching tv\")\n",
"\n",
" def __str__(self):\n",
" return \"student:\"+str(self.name)+\":\"+str(self.age)+\":\"+str(self.major)"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"Uno `Student`, essendo una `Person`, può avere amici:"
]
},
{
"cell_type": "code",
"execution_count": 83,
"metadata": {},
"outputs": [],
"source": [
"s1 = Student(age= 19,name=\"Peter\")"
]
},
{
"cell_type": "code",
"execution_count": 84,
"metadata": {},
"outputs": [
{
"data": {
"text/plain": [
"['Susan']"
]
},
"execution_count": 84,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"s1.add_friend(\"Susan\")\n",
"s1.get_friends()"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"Ha una raffinata capacità di conversazione:"
]
},
{
"cell_type": "code",
"execution_count": 87,
"metadata": {},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"I should eat\n",
"I am watching tv\n",
"I am watching tv\n"
]
}
],
"source": [
"s1.speak()\n",
"s1.speak()\n",
"s1.speak()"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"#### Esempio 4: \n",
"Una sottoclasse che possiede un attributo condiviso con tutti gli esemplari della classe"
]
},
{
"cell_type": "code",
"execution_count": 88,
"metadata": {},
"outputs": [],
"source": [
"# Note to self: opportuno commentare le parti di codice che non si vogliono attivare immediatamente\n",
"# Evita di riscrivere più volte la parte iniziale \n",
"class Rabbit(Animal):\n",
" tag = 1\n",
" \n",
" def __init__(self, age, parent1=None, parent2=None):\n",
" Animal.__init__(self, age)\n",
" self.parent1 = parent1\n",
" self.parent2 = parent2\n",
" self.rid = Rabbit.tag\n",
" Rabbit.tag += 1 # ogni volta che viene creato un esemplare tag aumenta di uno\n",
"\n",
" def get_rid(self): \n",
" return str(self.rid).zfill(3) # zfill(3) aggiunge gli eventuali zeri iniziali per arrivare a tre caratteri \n",
"\n",
" def get_parent1(self):\n",
" return self.parent1\n",
"\n",
" def get_parent2(self):\n",
" return self.parent2\n",
" \n",
" def __add__(self, other):\n",
"# returning object of same type as this class\n",
" return Rabbit(0, self, other)\n",
"\n",
" def __eq__(self, other): # L'operatore == controlla che due esemplari abbiano gli stessi due genitori \n",
" parents_same = (self.parent1.rid == other.parent1.rid \n",
" and self.parent2.rid == other.parent2.rid)\n",
" parents_opposite = (self.parent2.rid == other.parent1.rid\n",
" and self.parent1.rid == other.parent2.rid)\n",
" return parents_same or parents_opposite"
]
},
{
"cell_type": "code",
"execution_count": 89,
"metadata": {},
"outputs": [],
"source": [
"r1 = Rabbit(1)"
]
},
{
"cell_type": "code",
"execution_count": 90,
"metadata": {},
"outputs": [],
"source": [
"r2 = Rabbit(1,\"Leopold\",\"Margarete\")"
]
},
{
"cell_type": "code",
"execution_count": 94,
"metadata": {},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"id of r1: 001\n",
"id of r1: 002\n"
]
}
],
"source": [
"print('id of r1:',r1.get_rid())\n",
"print('id of r1:',r2.get_rid())"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"#### Esempio 5\n",
"\n",
"Una classe con metodi generici che vengono specificati in sottoclassi.\n",
"\n",
"La classe 'Shape' contiene l'attributo *name* e i metodi generici *area* e *perimeter*."
]
},
{
"cell_type": "code",
"execution_count": 1,
"metadata": {},
"outputs": [],
"source": [
"class Shape:\n",
" def __init__(self, name):\n",
" self.name = name\n",
" \n",
" def area(self):\n",
" pass\n",
" \n",
" def perimeter(self):\n",
" pass"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"Introduciamo due classi (*Circle* and *Rectangle*), che estendono *Shape* e contengono gli attributi necessari per calcolare area e perimetroa. I metodi generici vengono sovrascritti per ottenere il risultato corretto.\n",
"L'attributo *name* viene inizializzato al volore voluto usando il costruttore super()."
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"# super() va chiarito"
]
},
{
"cell_type": "code",
"execution_count": 2,
"metadata": {},
"outputs": [],
"source": [
"import math\n",
"\n",
"class Circle(Shape):\n",
" def __init__(self, radius):\n",
" super().__init__('circle')\n",
" self.radius = radius\n",
" \n",
" def area(self):\n",
" return math.pi * math.pow(self.radius, 2)\n",
" \n",
" def perimeter(self):\n",
" return 2 * math.pi * self.radius\n",
" \n",
"class Rectangle(Shape): \n",
" def __init__(self, b, h):\n",
" super().__init__('rectangle')\n",
" self.b = b\n",
" self.h = h\n",
" \n",
" def perimeter(self):\n",
" return 2 * (self.b + self.h)\n",
" \n",
" def area(self):\n",
" return self.b * self.h\n",
" "
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"Creiamo un oggetto Circle con raggio=5 un oggetto Rectangle con base=3 and altezza=2."
]
},
{
"cell_type": "code",
"execution_count": 6,
"metadata": {},
"outputs": [],
"source": [
"my_circle = Circle(radius=5)\n",
"# Alternative way\n",
"circle1 = Circle(5)\n",
"my_rectangle = Rectangle(b=3, h=2)"
]
},
{
"cell_type": "code",
"execution_count": 7,
"metadata": {},
"outputs": [
{
"data": {
"text/plain": [
"__main__.Circle"
]
},
"execution_count": 7,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"type(my_circle) # perchè stampa __main__.Circle? "
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"Stampiamo l'area e il perimetro di ciascun oggetto:"
]
},
{
"cell_type": "code",
"execution_count": 8,
"metadata": {},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"Circle area: 78.54, perimeter: 31.42\n",
"Circle1 area: 78.54, perimeter: 31.42\n",
"Rectangle area: 6.00, perimeter: 10.00\n"
]
}
],
"source": [
"print(f\"my_ircle area: {my_circle.area():.2f}, perimeter: {circle.perimeter():.2f}\")\n",
"print(f\"circle1 area: {circle1.area():.2f}, perimeter: {circle1.perimeter():.2f}\")\n",
"print(f\"my_rectangle area: {my_rectangle.area():.2f}, perimeter: {rectangle.perimeter():.2f}\")"
]
},
{
"cell_type": "code",
"execution_count": 11,
"metadata": {},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"my_circle radius: 5.00\n",
"my_circle name: 'circle'\n"
]
}
],
"source": [
"print(f\"my_circle radius: {my_circle.radius:.2f}\")\n",
"print(f\"my_circle name: '{my_circle.name}'\")"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"Scriviamo una funzione *print_shape* che prende un esemplare di *Shape* e stampa il suo nome, l'area e il perimetro. Poi le pasiamo gli oggetti che abbiamo creato."
]
},
{
"cell_type": "code",
"execution_count": 20,
"metadata": {},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"circle area: 78.54, 31.42 perimeter\n",
"rectangle area: 6.00, 10.00 perimeter\n"
]
}
],
"source": [
"def print_shape(shape: Shape):\n",
" print(f\"{shape.name} area: {shape.area():.2f}, {shape.perimeter():.2f} perimeter\")\n",
" \n",
"print_shape(circle)\n",
"print_shape(rectangle)"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"Se si cerca di stampare direttamente le istanze:"
]
},
{
"cell_type": "code",
"execution_count": 21,
"metadata": {},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"<__main__.Circle object at 0x00000274FF826088>\n",
"<__main__.Rectangle object at 0x00000274FF826848>\n"
]
}
],
"source": [
"print(circle)\n",
"print(rectangle)"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": []
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"#Add decoratori + @classmethod + @staticmethod"
]
}
],
"metadata": {
"kernelspec": {
"display_name": "Python 3",
"language": "python",
"name": "python3"
},
"language_info": {
"codemirror_mode": {
"name": "ipython",
"version": 3
},
"file_extension": ".py",
"mimetype": "text/x-python",
"name": "python",
"nbconvert_exporter": "python",
"pygments_lexer": "ipython3",
"version": "3.8.5"
}
},
"nbformat": 4,
"nbformat_minor": 4
}