Python - Modificatori di Accesso

Ciao a tutti, aspiranti programmatori Python! Oggi ci imbarqueremo in un viaggio avventuroso nel mondo dei Modificatori di Accesso in Python. Non preoccupatevi se siete nuovi nella programmazione; vi guiderò attraverso questo concetto passo per passo, con un sacco di esempi e spiegazioni lungo il percorso. Allora, immergiamoci!

Python - Access Modifiers

Modificatori di Accesso in Python

Nella programmazione orientata agli oggetti, i modificatori di accesso vengono utilizzati per controllare la visibilità e l'accessibilità dei membri della classe (attributi e metodi). Mentre molti linguaggi di programmazione hanno modificatori di accesso rigorosi come public, private e protected, Python adotta un approccio più rilassato. Segue una filosofia spesso definita come "Siamo tutti adulti consenzienti qui".

In Python, abbiamo tre tipi di modificatori di accesso:

  1. Pubblico
  2. Protetto
  3. Privato

Esploriamo ciascuno di questi con degli esempi.

Membri Pubblici

In Python, tutti i membri sono pubblici per impostazione predefinita. Questo significa che possono essere accessibili dall'esterno della classe. Ecco un esempio:

class Student:
def __init__(self, name, age):
self.name = name  # Attributo pubblico
self.age = age    # Attributo pubblico

def display_info(self):  # Metodo pubblico
print(f"Name: {self.name}, Age: {self.age}")

# Creazione di un'istanza di Student
student1 = Student("Alice", 20)

# Accesso ai membri pubblici
print(student1.name)  # Output: Alice
student1.display_info()  # Output: Name: Alice, Age: 20

In questo esempio, name, age e display_info() sono tutti membri pubblici. Possiamo accedervi direttamente dall'esterno della classe.

Membri Protetti

I membri protetti sono denotati prefissando il nome del membro con un singolo underscore (_). Non sono veramente privati e possono ancora essere accessibili dall'esterno della classe, ma è una convenzione trattarli solo per uso interno.

class Employee:
def __init__(self, name, salary):
self._name = name      # Attributo protetto
self._salary = salary  # Attributo protetto

def _display_salary(self):  # Metodo protetto
print(f"{self._name}'s salary is ${self._salary}")

# Creazione di un'istanza di Employee
emp1 = Employee("Bob", 50000)

# Accesso ai membri protetti (Nota: Questo è possibile ma non raccomandato)
print(emp1._name)  # Output: Bob
emp1._display_salary()  # Output: Bob's salary is $50000

Anche se possiamo accedere a _name, _salary e _display_salary(), generalmente non è consigliabile farlo dall'esterno della classe o delle sue sottoclassi.

Membri Privati

I membri privati sono denotati prefissando il nome con doppie underscores (__). Python esegue il name mangling per questi membri, rendendoli più difficili (ma non impossibili) da accedere dall'esterno della classe.

class BankAccount:
def __init__(self, account_number, balance):
self.__account_number = account_number  # Attributo privato
self.__balance = balance                # Attributo privato

def __display_balance(self):  # Metodo privato
print(f"Balance: ${self.__balance}")

def public_display(self):
self.__display_balance()

# Creazione di un'istanza di BankAccount
account1 = BankAccount("123456", 1000)

# Tentativo di accesso ai membri privati
# print(account1.__account_number)  # Questo solleva un AttributeError
# account1.__display_balance()      # Questo solleva anche un AttributeError

# Accesso al metodo privato attraverso un metodo pubblico
account1.public_display()  # Output: Balance: $1000

In questo esempio, __account_number, __balance e __display_balance() sono membri privati. Tentare di accedervi direttamente dall'esterno della classe solleverà un AttributeError.

Name Mangling

Ricordate quando ho menzionato che i membri privati in Python non sono veramente privati? Questo è a causa di un meccanismo chiamato name mangling. Quando create un membro privato utilizzando doppie underscores, Python cambia il suo nome internamente per rendere più difficile l'accesso accidentale.

Ecco come funziona:

class NameManglingDemo:
def __init__(self):
self.__private_var = "Sono privato!"

demo = NameManglingDemo()
print(dir(demo))
# Output: [..., '_NameManglingDemo__private_var', ...]

# Accesso alla variabile privata utilizzando il nome mangled
print(demo._NameManglingDemo__private_var)  # Output: Sono privato!

Come vedete, Python rinomina __private_var in _NameManglingDemo__private_var. Questo è il name mangling in azione!

Oggetto Proprietà di Python

La funzione property() in Python è una funzione integrata che crea e restituisce un oggetto proprietà. È un modo per aggiungere metodi getter, setter e deleter agli attributi della classe.

Ecco un esempio:

class Temperature:
def __init__(self, celsius):
self._celsius = celsius

def get_fahrenheit(self):
return (self._celsius * 9/5) + 32

def set_fahrenheit(self, fahrenheit):
self._celsius = (fahrenheit - 32) * 5/9

fahrenheit = property(get_fahrenheit, set_fahrenheit)

# Utilizzo della proprietà
temp = Temperature(25)
print(temp.fahrenheit)  # Output: 77.0

temp.fahrenheit = 86
print(temp._celsius)  # Output: 30.0

In questo esempio, fahrenheit è una proprietà che ci consente di ottenere e impostare la temperatura in Fahrenheit memorizzandola internamente in Celsius.

Metodi Getter e Setter

I metodi getter e setter sono metodi utilizzati per ottenere e impostare i valori degli attributi della classe. Forniscono un modo per accedere e modificare gli attributi privati mantenendo l'incapsulamento.

Ecco un esempio utilizzando il decoratore @property, che è un modo più pythonesco di implementare i getter e i setter:

class Person:
def __init__(self, name, age):
self._name = name
self._age = age

@property
def name(self):
return self._name

@name.setter
def name(self, value):
if not isinstance(value, str):
raise ValueError("Il nome deve essere una stringa")
self._name = value

@property
def age(self):
return self._age

@age.setter
def age(self, value):
if not isinstance(value, int) or value < 0:
raise ValueError("L'età deve essere un intero positivo")
self._age = value

# Utilizzo di getter e setter
person = Person("Charlie", 30)
print(person.name)  # Output: Charlie

person.name = "David"
print(person.name)  # Output: David

try:
person.age = -5
except ValueError as e:
print(e)  # Output: L'età deve essere un intero positivo

In questo esempio, abbiamo creato metodi getter e setter per name e age. I metodi setter includono la validazione per assicurarsi che i valori impostati soddisfino determinati criteri.

Per riassumere i metodi discussi, ecco una tabella in formato Markdown:

Metodo Descrizione Esempio
Pubblico Accessibile da ovunque self.name
Protetto Accessibile all'interno della classe e delle sottoclassi (per convenzione) self._name
Privato Nome mangled per limitare l'accesso self.__name
Proprietà Crea un oggetto proprietà property(get_method, set_method)
Getter Metodo per ottenere il valore di un attributo @property
Setter Metodo per impostare il valore di un attributo @attribute.setter

Eccoci! Abbiamo coperto i modificatori di accesso in Python, il name mangling, gli oggetti proprietà e i metodi getter e setter. Ricordate, l'approccio di Python al controllo dell'accesso è più sulla convenzione e la fiducia che su regole rigorose. Mentre continuate il vostro viaggio con Python, scoprirete che questa flessibilità permette di scrivere codice pulito e leggibile mentre fornisce ancora modi per implementare l'incapsulamento quando necessario.

Seguite a praticare, restate curiosi e buon coding!

Credits: Image by storyset