Python orientato agli oggetti - Building Blocks

In questo capitolo, discuteremo in dettaglio i termini orientati agli oggetti e i concetti di programmazione. Class è solo una fabbrica per un'istanza. Questa fabbrica contiene il progetto che descrive come creare le istanze. Una istanza o un oggetto vengono costruiti dalla classe. Nella maggior parte dei casi, possiamo avere più di un'istanza di una classe. Ogni istanza ha un insieme di attributi e questi attributi sono definiti in una classe, quindi ogni istanza di una particolare classe dovrebbe avere gli stessi attributi.

Pacchetti di classi: comportamento e stato

Una classe ti permetterà di raggruppare insieme il comportamento e lo stato di un oggetto. Osservare il diagramma seguente per una migliore comprensione:

I seguenti punti sono degni di nota quando si discute di pacchetti di classi:

  • La parola behavior è identico a function - è un pezzo di codice che fa qualcosa (o implementa un comportamento)

  • La parola state è identico a variables - è un luogo in cui memorizzare i valori all'interno di una classe.

  • Quando affermiamo insieme un comportamento e uno stato di una classe, significa che una classe impacchetta funzioni e variabili.

Le classi hanno metodi e attributi

In Python, la creazione di un metodo definisce un comportamento di classe. La parola metodo è il nome OOP assegnato a una funzione definita all'interno di una classe. Per riassumere -

  • Class functions - è sinonimo di methods

  • Class variables - è sinonimo di name attributes.

  • Class - un progetto per un'istanza con un comportamento esatto.

  • Object - una delle istanze della classe, esegue la funzionalità definita nella classe.

  • Type - indica la classe a cui appartiene l'istanza

  • Attribute - Qualsiasi valore oggetto: object.attribute

  • Method - un “attributo richiamabile” definito nella classe

Ad esempio, osservare la seguente parte di codice:

var = “Hello, John”
print( type (var)) # ‘str’> or <class 'str'>
print(var.upper()) # upper() method is called, HELLO, JOHN

Creazione e istanziazione

Il codice seguente mostra come creare la nostra prima classe e poi la sua istanza.

class MyClass(object):
   pass
# Create first instance of MyClass
this_obj = MyClass()
print(this_obj)
# Another instance of MyClass
that_obj = MyClass()
print (that_obj)

Qui abbiamo creato una classe chiamata MyClasse che non svolge alcun compito. L'argomentoobject in MyClass class implica l'ereditarietà delle classi e sarà discusso nei capitoli successivi. pass nel codice sopra indica che questo blocco è vuoto, cioè è una definizione di classe vuota.

Creiamo un'istanza this_obj di MyClass() class e stampalo come mostrato -

<__main__.MyClass object at 0x03B08E10>
<__main__.MyClass object at 0x0369D390>

Qui abbiamo creato un'istanza di MyClass.Il codice esadecimale si riferisce all'indirizzo in cui viene archiviato l'oggetto. Un'altra istanza punta a un altro indirizzo.

Ora definiamo una variabile all'interno della classe MyClass() e ottieni la variabile dall'istanza di quella classe come mostrato nel codice seguente:

class MyClass(object):
   var = 9

# Create first instance of MyClass
this_obj = MyClass()
print(this_obj.var)

# Another instance of MyClass

that_obj = MyClass()
print (that_obj.var)

Produzione

È possibile osservare il seguente output quando si esegue il codice sopra riportato:

9
9

Poiché l'istanza sa da quale classe viene istanziata, quindi quando viene richiesto un attributo da un'istanza, l'istanza cerca l'attributo e la classe. Questo è chiamatoattribute lookup.

Metodi di istanza

Una funzione definita in una classe è chiamata a method.Un metodo di istanza richiede un'istanza per poterlo chiamare e non richiede un decoratore. Quando si crea un metodo di istanza, il primo parametro è sempreself. Sebbene possiamo chiamarlo (self) con qualsiasi altro nome, si consiglia di utilizzare self, poiché è una convenzione di denominazione.

class MyClass(object):
   var = 9
   def firstM(self):
      print("hello, World")
obj = MyClass()
print(obj.var)
obj.firstM()

Produzione

È possibile osservare il seguente output quando si esegue il codice sopra riportato:

9
hello, World

Si noti che nel programma precedente, abbiamo definito un metodo con self come argomento. Ma non possiamo chiamare il metodo perché non abbiamo dichiarato alcun argomento.

class MyClass(object):
   def firstM(self):
      print("hello, World")
      print(self)
obj = MyClass()
obj.firstM()
print(obj)

Produzione

È possibile osservare il seguente output quando si esegue il codice sopra riportato:

hello, World
<__main__.MyClass object at 0x036A8E10>
<__main__.MyClass object at 0x036A8E10>

Incapsulamento

L'incapsulamento è uno dei fondamenti dell'OOP. L'OOP ci consente di nascondere la complessità del funzionamento interno dell'oggetto che è vantaggioso per lo sviluppatore nei seguenti modi:

  • Semplifica e rende facile capire l'utilizzo di un oggetto senza conoscerne gli interni.

  • Qualsiasi cambiamento può essere facilmente gestibile.

La programmazione orientata agli oggetti si basa molto sull'incapsulamento. I termini incapsulamento e astrazione (chiamati anche dati nascosti) sono spesso usati come sinonimi. Sono quasi sinonimi, poiché l'astrazione si ottiene attraverso l'incapsulamento.

L'incapsulamento ci fornisce il meccanismo per limitare l'accesso ad alcuni dei componenti dell'oggetto, questo significa che la rappresentazione interna di un oggetto non può essere vista dall'esterno della definizione dell'oggetto. L'accesso a questi dati viene in genere ottenuto tramite metodi speciali:Getters e Setters.

Questi dati vengono archiviati negli attributi dell'istanza e possono essere manipolati da qualsiasi punto all'esterno della classe. Per proteggerlo, è necessario accedere a tali dati solo utilizzando metodi di istanza. L'accesso diretto non dovrebbe essere consentito.

class MyClass(object):
   def setAge(self, num):
      self.age = num

   def getAge(self):
      return self.age

zack = MyClass()
zack.setAge(45)
print(zack.getAge())

zack.setAge("Fourty Five")
print(zack.getAge())

Produzione

È possibile osservare il seguente output quando si esegue il codice sopra riportato:

45
Fourty Five

I dati devono essere memorizzati solo se sono corretti e validi, utilizzando i costrutti di gestione delle eccezioni. Come possiamo vedere sopra, non ci sono restrizioni sull'input dell'utente al metodo setAge (). Potrebbe essere una stringa, un numero o un elenco. Quindi dobbiamo controllare il codice sopra per garantire la correttezza della memorizzazione.

class MyClass(object):
   def setAge(self, num):
      self.age = num

   def getAge(self):
      return self.age
zack = MyClass()
zack.setAge(45)
print(zack.getAge())
zack.setAge("Fourty Five")
print(zack.getAge())

Init Constructor

Il __initIl metodo __ viene chiamato implicitamente non appena viene istanziato un oggetto di una classe. Questo inizializzerà l'oggetto.

x = MyClass()

La riga di codice mostrata sopra creerà una nuova istanza e assegna questo oggetto alla variabile locale x.

L'operazione di istanziazione, cioè calling a class object, crea un oggetto vuoto. A molte classi piace creare oggetti con istanze personalizzate per uno stato iniziale specifico. Pertanto, una classe può definire un metodo speciale chiamato '__init __ ()' come mostrato -

def __init__(self):
   self.data = []

Python chiama __init__ durante l'istanziazione per definire un attributo aggiuntivo che dovrebbe verificarsi quando viene istanziata una classe che potrebbe impostare alcuni valori iniziali per quell'oggetto o eseguire una routine richiesta all'istanza. Quindi in questo esempio, una nuova istanza inizializzata può essere ottenuta da:

x = MyClass()

Il metodo __init __ () può avere argomenti singoli o multipli per una maggiore flessibilità. L'init sta per inizializzazione, poiché inizializza gli attributi dell'istanza. È chiamato il costruttore di una classe.

class myclass(object):
   def __init__(self,aaa, bbb):
      self.a = aaa
      self.b = bbb

x = myclass(4.5, 3)
print(x.a, x.b)

Produzione

4.5 3

Attributi di classe

L'attributo definito nella classe è chiamato "attributi di classe" e gli attributi definiti nella funzione sono chiamati "attributi di istanza". Durante la definizione, questi attributi non sono prefissati da self, poiché sono proprietà della classe e non di un'istanza particolare.

È possibile accedere agli attributi della classe dalla classe stessa (className.attributeName) e dalle istanze della classe (inst.attributeName). Quindi, le istanze hanno accesso sia all'attributo di istanza che agli attributi di classe.

>>> class myclass():
   age = 21
>>> myclass.age
21
>>> x = myclass()
>>> x.age
21
>>>

Un attributo di classe può essere sovrascritto in un'istanza, anche se non è un buon metodo per interrompere l'incapsulamento.

C'è un percorso di ricerca per gli attributi in Python. Il primo è il metodo definito all'interno della classe, quindi la classe sopra di essa.

>>> class myclass(object):
   classy = 'class value'
>>> dd = myclass()
>>> print (dd.classy) # This should return the string 'class value'
class value
>>>
>>> dd.classy = "Instance Value"
>>> print(dd.classy) # Return the string "Instance Value"
Instance Value
>>>
>>> # This will delete the value set for 'dd.classy' in the instance.
>>> del dd.classy
>>> >>> # Since the overriding attribute was deleted, this will print 'class
value'.

>>> print(dd.classy)
class value
>>>

Stiamo sovrascrivendo l'attributo di classe "classy" nell'istanza dd. Quando è sovrascritto, l'interprete Python legge il valore sovrascritto. Ma una volta che il nuovo valore è stato cancellato con "del", il valore sovrascritto non è più presente nell'istanza, e quindi la ricerca va di un livello superiore e lo ottiene dalla classe.

Lavorare con dati di classi e istanze

In questa sezione, vediamo come i dati della classe si relazionano ai dati dell'istanza. Possiamo memorizzare i dati in una classe o in un'istanza. Quando progettiamo una classe, decidiamo quali dati appartengono all'istanza e quali dati devono essere archiviati nella classe complessiva.

Un'istanza può accedere ai dati della classe. Se creiamo più istanze, queste istanze possono accedere ai valori degli attributi individuali e ai dati della classe complessiva.

Pertanto, i dati di una classe sono i dati condivisi tra tutte le istanze. Rispettare il codice riportato di seguito per una migliore comprensione -

class InstanceCounter(object):
   count = 0 # class attribute, will be accessible to all instances
   def __init__(self, val):
      self.val = val
      InstanceCounter.count +=1 # Increment the value of class attribute, accessible through class name
# In above line, class ('InstanceCounter') act as an object
   def set_val(self, newval):
      self.val = newval

   def get_val(self):
      return self.val

   def get_count(self):
      return InstanceCounter.count
a = InstanceCounter(9)
b = InstanceCounter(18)
c = InstanceCounter(27)

for obj in (a, b, c):
   print ('val of obj: %s' %(obj.get_val())) # Initialized value ( 9, 18, 27)
   print ('count: %s' %(obj.get_count())) # always 3

Produzione

val of obj: 9
count: 3
val of obj: 18
count: 3
val of obj: 27
count: 3

In breve, gli attributi di classe sono gli stessi per tutte le istanze di classe mentre gli attributi di istanza sono particolari per ogni istanza. Per due istanze diverse, avremo due attributi di istanza diversi.

class myClass:
   class_attribute = 99

   def class_method(self):
      self.instance_attribute = 'I am instance attribute'

print (myClass.__dict__)

Produzione

È possibile osservare il seguente output quando si esegue il codice sopra riportato:

{'__module__': '__main__', 'class_attribute': 99, 'class_method': 
      
       , '__dict__': 
       
        , '__weakref__': 
        
         , '__doc__': None} 
        
       
      

The instance attribute myClass.__dict__ as shown −

>>> a = myClass()
>>> a.class_method()
>>> print(a.__dict__)
{'instance_attribute': 'I am instance attribute'}