{ "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 }