Python - Orientato agli oggetti
Python è stato un linguaggio orientato agli oggetti da quando esisteva. Per questo motivo, creare e utilizzare classi e oggetti è decisamente facile. Questo capitolo ti aiuta a diventare un esperto nell'uso del supporto per la programmazione orientata agli oggetti di Python.
Se non hai alcuna esperienza precedente con la programmazione orientata agli oggetti (OO), potresti consultare un corso introduttivo su di esso o almeno un tutorial di qualche tipo in modo da avere una comprensione dei concetti di base.
Tuttavia, ecco una piccola introduzione della programmazione orientata agli oggetti (OOP) per portarti alla velocità -
Panoramica della terminologia OOP
Class- Un prototipo definito dall'utente per un oggetto che definisce un insieme di attributi che caratterizzano qualsiasi oggetto della classe. Gli attributi sono membri di dati (variabili di classe e variabili di istanza) e metodi, accessibili tramite notazione a punti.
Class variable- Una variabile condivisa da tutte le istanze di una classe. Le variabili di classe sono definite all'interno di una classe ma al di fuori di qualsiasi metodo della classe. Le variabili di classe non vengono utilizzate così frequentemente come le variabili di istanza.
Data member - Una variabile di classe o una variabile di istanza che contiene i dati associati a una classe e ai suoi oggetti.
Function overloading- L'assegnazione di più di un comportamento a una particolare funzione. L'operazione eseguita varia in base ai tipi di oggetti o argomenti coinvolti.
Instance variable - Una variabile definita all'interno di un metodo e che appartiene solo all'istanza corrente di una classe.
Inheritance - Il trasferimento delle caratteristiche di una classe ad altre classi che ne derivano.
Instance- Un oggetto individuale di una certa classe. Un oggetto obj che appartiene a una classe Circle, ad esempio, è un'istanza della classe Circle.
Instantiation - La creazione di un'istanza di una classe.
Method - Un tipo speciale di funzione definita in una definizione di classe.
Object- Un'istanza univoca di una struttura dati definita dalla sua classe. Un oggetto comprende sia membri di dati (variabili di classe e variabili di istanza) che metodi.
Operator overloading - L'assegnazione di più di una funzione a un particolare operatore.
Creazione di classi
L' istruzione class crea una nuova definizione di classe. Il nome della classe segue immediatamente la parola chiave class seguita da due punti come segue:
class ClassName:
'Optional class documentation string'
class_suite
La classe ha una stringa di documentazione, a cui è possibile accedere tramite ClassName .__ doc__ .
La class_suite consiste di tutte le dichiarazioni componenti che definiscono i membri della classe, attributi di dati e funzioni.
Esempio
Di seguito è riportato l'esempio di una semplice classe Python:
class Employee:
'Common base class for all employees'
empCount = 0
def __init__(self, name, salary):
self.name = name
self.salary = salary
Employee.empCount += 1
def displayCount(self):
print "Total Employee %d" % Employee.empCount
def displayEmployee(self):
print "Name : ", self.name, ", Salary: ", self.salary
La variabile empCount è una variabile di classe il cui valore è condiviso tra tutte le istanze di questa classe. È possibile accedervi come Employee.empCount dall'interno della classe o dall'esterno della classe.
Il primo metodo __init __ () è un metodo speciale, chiamato costruttore di classi o metodo di inizializzazione che Python chiama quando crei una nuova istanza di questa classe.
Dichiari altri metodi di classe come funzioni normali con l'eccezione che il primo argomento di ogni metodo è self . Python aggiunge l' argomento self alla lista per te; non è necessario includerlo quando si chiamano i metodi.
Creazione di oggetti istanza
Per creare istanze di una classe, chiamate la classe utilizzando il nome della classe e passate qualsiasi argomento accetti il suo metodo __init__ .
"This would create first object of Employee class"
emp1 = Employee("Zara", 2000)
"This would create second object of Employee class"
emp2 = Employee("Manni", 5000)
Accesso agli attributi
Si accede agli attributi dell'oggetto utilizzando l'operatore punto con oggetto. È possibile accedere alla variabile di classe utilizzando il nome della classe come segue:
emp1.displayEmployee()
emp2.displayEmployee()
print "Total Employee %d" % Employee.empCount
Ora, mettendo insieme tutti i concetti -
#!/usr/bin/python
class Employee:
'Common base class for all employees'
empCount = 0
def __init__(self, name, salary):
self.name = name
self.salary = salary
Employee.empCount += 1
def displayCount(self):
print "Total Employee %d" % Employee.empCount
def displayEmployee(self):
print "Name : ", self.name, ", Salary: ", self.salary
"This would create first object of Employee class"
emp1 = Employee("Zara", 2000)
"This would create second object of Employee class"
emp2 = Employee("Manni", 5000)
emp1.displayEmployee()
emp2.displayEmployee()
print "Total Employee %d" % Employee.empCount
Quando il codice sopra viene eseguito, produce il seguente risultato:
Name : Zara ,Salary: 2000
Name : Manni ,Salary: 5000
Total Employee 2
Puoi aggiungere, rimuovere o modificare attributi di classi e oggetti in qualsiasi momento:
emp1.age = 7 # Add an 'age' attribute.
emp1.age = 8 # Modify 'age' attribute.
del emp1.age # Delete 'age' attribute.
Invece di utilizzare le normali istruzioni per accedere agli attributi, è possibile utilizzare le seguenti funzioni:
Il getattr(obj, name[, default]) - per accedere all'attributo dell'oggetto.
Il hasattr(obj,name) - per verificare se un attributo esiste o meno.
Il setattr(obj,name,value)- per impostare un attributo. Se l'attributo non esiste, verrebbe creato.
Il delattr(obj, name) - per eliminare un attributo.
hasattr(emp1, 'age') # Returns true if 'age' attribute exists
getattr(emp1, 'age') # Returns value of 'age' attribute
setattr(emp1, 'age', 8) # Set attribute 'age' at 8
delattr(empl, 'age') # Delete attribute 'age'
Attributi di classe incorporati
Ogni classe Python continua a seguire gli attributi incorporati ed è possibile accedervi utilizzando l'operatore punto come qualsiasi altro attributo -
__dict__ - Dizionario contenente lo spazio dei nomi della classe.
__doc__ - Stringa di documentazione della classe o nessuna, se non definita.
__name__ - Nome della classe.
__module__- Nome del modulo in cui è definita la classe. Questo attributo è "__main__" in modalità interattiva.
__bases__ - Una tupla possibilmente vuota contenente le classi di base, nell'ordine in cui sono presenti nell'elenco delle classi di base.
Per la classe precedente proviamo ad accedere a tutti questi attributi:
#!/usr/bin/python
class Employee:
'Common base class for all employees'
empCount = 0
def __init__(self, name, salary):
self.name = name
self.salary = salary
Employee.empCount += 1
def displayCount(self):
print "Total Employee %d" % Employee.empCount
def displayEmployee(self):
print "Name : ", self.name, ", Salary: ", self.salary
print "Employee.__doc__:", Employee.__doc__
print "Employee.__name__:", Employee.__name__
print "Employee.__module__:", Employee.__module__
print "Employee.__bases__:", Employee.__bases__
print "Employee.__dict__:", Employee.__dict__
Quando il codice sopra viene eseguito, produce il seguente risultato:
Employee.__doc__: Common base class for all employees
Employee.__name__: Employee
Employee.__module__: __main__
Employee.__bases__: ()
Employee.__dict__: {'__module__': '__main__', 'displayCount':
<function displayCount at 0xb7c84994>, 'empCount': 2,
'displayEmployee': <function displayEmployee at 0xb7c8441c>,
'__doc__': 'Common base class for all employees',
'__init__': <function __init__ at 0xb7c846bc>}
Distruzione di oggetti (raccolta dei rifiuti)
Python elimina automaticamente gli oggetti non necessari (tipi incorporati o istanze di classe) per liberare lo spazio di memoria. Il processo mediante il quale Python recupera periodicamente i blocchi di memoria che non sono più in uso è chiamato Garbage Collection.
Il garbage collector di Python viene eseguito durante l'esecuzione del programma e viene attivato quando il conteggio dei riferimenti di un oggetto raggiunge lo zero. Il conteggio dei riferimenti di un oggetto cambia al variare del numero di alias che puntano a esso.
Il conteggio dei riferimenti a un oggetto aumenta quando gli viene assegnato un nuovo nome o viene inserito in un contenitore (elenco, tupla o dizionario). Il conteggio dei riferimenti dell'oggetto diminuisce quando viene eliminato con del , il suo riferimento viene riassegnato o il suo riferimento esce dall'ambito. Quando il conteggio dei riferimenti di un oggetto raggiunge lo zero, Python lo raccoglie automaticamente.
a = 40 # Create object <40>
b = a # Increase ref. count of <40>
c = [b] # Increase ref. count of <40>
del a # Decrease ref. count of <40>
b = 100 # Decrease ref. count of <40>
c[0] = -1 # Decrease ref. count of <40>
Normalmente non noterai quando il garbage collector distrugge un'istanza orfana e ne recupera lo spazio. Ma una classe può implementare il metodo speciale __del __ () , chiamato distruttore, che viene invocato quando l'istanza sta per essere distrutta. Questo metodo può essere utilizzato per pulire le risorse non di memoria utilizzate da un'istanza.
Esempio
Questo distruttore __del __ () stampa il nome della classe di un'istanza che sta per essere distrutta -
#!/usr/bin/python
class Point:
def __init__( self, x=0, y=0):
self.x = x
self.y = y
def __del__(self):
class_name = self.__class__.__name__
print class_name, "destroyed"
pt1 = Point()
pt2 = pt1
pt3 = pt1
print id(pt1), id(pt2), id(pt3) # prints the ids of the obejcts
del pt1
del pt2
del pt3
Quando il codice sopra viene eseguito, produce il seguente risultato:
3083401324 3083401324 3083401324
Point destroyed
Note- Idealmente, dovresti definire le tue classi in un file separato, quindi dovresti importarle nel tuo file di programma principale usando l' istruzione import .
Eredità di classe
Invece di iniziare da zero, puoi creare una classe derivandola da una classe preesistente elencando la classe genitore tra parentesi dopo il nome della nuova classe.
La classe figlia eredita gli attributi della sua classe genitore e puoi usare quegli attributi come se fossero definiti nella classe figlia. Una classe figlia può anche sovrascrivere membri di dati e metodi dal genitore.
Sintassi
Le classi derivate sono dichiarate in modo molto simile alla loro classe genitore; tuttavia, dopo il nome della classe viene fornito un elenco di classi base da cui ereditare:
class SubClassName (ParentClass1[, ParentClass2, ...]):
'Optional class documentation string'
class_suite
Esempio
#!/usr/bin/python
class Parent: # define parent class
parentAttr = 100
def __init__(self):
print "Calling parent constructor"
def parentMethod(self):
print 'Calling parent method'
def setAttr(self, attr):
Parent.parentAttr = attr
def getAttr(self):
print "Parent attribute :", Parent.parentAttr
class Child(Parent): # define child class
def __init__(self):
print "Calling child constructor"
def childMethod(self):
print 'Calling child method'
c = Child() # instance of child
c.childMethod() # child calls its method
c.parentMethod() # calls parent's method
c.setAttr(200) # again call parent's method
c.getAttr() # again call parent's method
Quando il codice sopra viene eseguito, produce il seguente risultato:
Calling child constructor
Calling child method
Calling parent method
Parent attribute : 200
In modo simile, puoi guidare una classe da più classi genitore come segue:
class A: # define your class A
.....
class B: # define your class B
.....
class C(A, B): # subclass of A and B
.....
È possibile utilizzare le funzioni issubclass () o isinstance () per controllare le relazioni di due classi e istanze.
Il issubclass(sub, sup) La funzione booleana restituisce true se la sottoclasse data sub è effettivamente una sottoclasse della superclasse sup.
Il isinstance(obj, Class)La funzione booleana restituisce true se obj è un'istanza della classe Class o è un'istanza di una sottoclasse di Class
Metodi di sostituzione
Puoi sempre sovrascrivere i metodi della tua classe genitore. Uno dei motivi per sovrascrivere i metodi del genitore è perché potresti volere funzionalità speciali o diverse nella tua sottoclasse.
Esempio
#!/usr/bin/python
class Parent: # define parent class
def myMethod(self):
print 'Calling parent method'
class Child(Parent): # define child class
def myMethod(self):
print 'Calling child method'
c = Child() # instance of child
c.myMethod() # child calls overridden method
Quando il codice sopra viene eseguito, produce il seguente risultato:
Calling child method
Metodi di sovraccarico di base
La tabella seguente elenca alcune funzionalità generiche che puoi sovrascrivere nelle tue classi:
Sr.No. | Metodo, descrizione e chiamata di esempio |
---|---|
1 | __init__ ( self [,args...] ) Costruttore (con eventuali argomenti opzionali) Chiamata di esempio: obj = className (args) |
2 | __del__( self ) Distruttore, elimina un oggetto Chiamata di esempio: del obj |
3 | __repr__( self ) Rappresentazione di stringa valutabile Chiamata di esempio: repr (obj) |
4 | __str__( self ) Rappresentazione di stringa stampabile Chiamata di esempio: str (obj) |
5 | __cmp__ ( self, x ) Confronto di oggetti Chiamata di esempio: cmp (obj, x) |
Operatori di sovraccarico
Supponi di aver creato una classe Vector per rappresentare vettori bidimensionali, cosa succede quando usi l'operatore più per aggiungerli? Molto probabilmente Python ti sgriderà.
Tuttavia, potresti definire il metodo __add__ nella tua classe per eseguire l'addizione vettoriale e quindi l'operatore più si comporterebbe come previsto -
Esempio
#!/usr/bin/python
class Vector:
def __init__(self, a, b):
self.a = a
self.b = b
def __str__(self):
return 'Vector (%d, %d)' % (self.a, self.b)
def __add__(self,other):
return Vector(self.a + other.a, self.b + other.b)
v1 = Vector(2,10)
v2 = Vector(5,-2)
print v1 + v2
Quando il codice sopra viene eseguito, produce il seguente risultato:
Vector(7,8)
Dati nascosti
Gli attributi di un oggetto possono o meno essere visibili al di fuori della definizione della classe. È necessario denominare gli attributi con un doppio carattere di sottolineatura e questi attributi non saranno quindi direttamente visibili agli estranei.
Esempio
#!/usr/bin/python
class JustCounter:
__secretCount = 0
def count(self):
self.__secretCount += 1
print self.__secretCount
counter = JustCounter()
counter.count()
counter.count()
print counter.__secretCount
Quando il codice sopra viene eseguito, produce il seguente risultato:
1
2
Traceback (most recent call last):
File "test.py", line 12, in <module>
print counter.__secretCount
AttributeError: JustCounter instance has no attribute '__secretCount'
Python protegge questi membri modificando internamente il nome per includere il nome della classe. È possibile accedere ad attributi come object._className__attrName . Se sostituisci l'ultima riga come segue, allora funziona per te -
.........................
print counter._JustCounter__secretCount
Quando il codice sopra viene eseguito, produce il seguente risultato:
1
2
2