English | 简体中文 | 繁體中文 | Русский язык | Français | Español | Português | Deutsch | 日本語 | 한국어 | Italiano | بالعربية

Tutorial di base di Python

Controllo dei flussi in Python

Funzione in Python

Tipi di dati in Python

Operazioni di file in Python

Oggetti e classi in Python

Date e ora in Python

Conoscenze avanzate di Python

Manuale di Python

@property in Python

Python ha un grande concetto chiamato proprietà, che rende la vita dei programmatori orientati agli oggetti più semplice.

Prima di definire e comprendere in dettaglio cosa sia @property, diamo un'occhiata al motivo per cui è necessario utilizzarlo in primo luogo.

Un'istanza inizia

Supponiamo che tu decidaCreare unUna classe per memorizzare la temperatura in gradi Celsius. Implementerà anche un metodo per convertire la temperatura in gradi Fahrenheit. Un esempio di questo è il seguente.

class Celsius:
    def __init__(self, temperature = 0):
        self.temperature = temperature
    def to_fahrenheit(self):
        return (self.temperature * 1.8) + 32

Possiamo creare oggetti da questa classe e manipolare le proprietà temperature come necessario. Prova questi con lo shell di Python.

>>> # Creare un nuovo oggetto
>>> man = Celsius()
>>> # Impostare la temperatura
>>> man.temperature = 37
>>> # Ottenere la temperatura
>>> man.temperature
37
>>> # Ottenere gradi Fahrenheit
>>> man.to_fahrenheit()
98.60000000000001

Quando si converte in gradi Fahrenheit, i decimali in eccesso sono dovuti a errori di calcolo a virgola mobile (provare 1.1 + 2.2 nell'interprete Python).

Come mostrato sopra, ogni volta che assegniamo o recuperiamo qualsiasi attributo dell'oggetto (cometemperature) Python cercherà sempre nel dizionario __dict__ dell'oggetto.

>>> man.__dict__
{'temperature': 37}

Di conseguenza, man.temperature diventa man.__dict__['temperature'].

Ora, supponiamo ulteriormente che il nostro corso sia molto popolare tra i clienti e che loro iniziino a utilizzarlo nei loro programmi. Li hanno assegnati in vari modi.

Un giorno, un cliente fidato ci è venuto a trovare e ha suggerito che la temperatura non può essere inferiore a -273 gradi Celsius (gli studenti di scienze termiche potrebbero dire che è in realtà -273.15 gradi Celsius), conosciuta anche come zero assoluto. Ha ulteriormente richiesto che implementassimo questa restrizione di valore. Come azienda che si impegna a soddisfare i clienti, siamo stati lieti di ascoltare questo suggerimento e abbiamo rilasciato la versione 1.01 (aggiornamento delle classi esistenti).

Utilizzo di getter e setter

Un metodo ovvio per risolvere queste restrizioni è nascondere l'attributo temperature (impostandolo come privato) e definire nuovi getter e setter per manipolarlo. Questo può essere fatto come segue.

class Celsius:
    def __init__(self, temperature = 0):
        self.set_temperature(temperature)
    def to_fahrenheit(self):
        return (self.get_temperature() * 1.8) + 32
    # new update
    def get_temperature(self):
        return self._temperature
    def set_temperature(self, value):
        if value < -273:
            raise ValueError("-273 gradi è impossibile")
        self._temperature = value

Sulla pagina precedente possiamo vedere che get_temperature() e set_temperature() hanno definito nuovi metodi, in aggiunta, _temperature ha sostituito temperature. La sottolineatura (_) all'inizio indica una variabile privata in Python.

>>> c = Celsius(-277)
Traceback (chiamata più recente in basso):
...
ValueError: Impossibile impostare la temperatura sotto -273
>>> c = Celsius(37)
>>> c.get_temperature()
37
>>> c.set_temperature(10)
>>> c.set_temperature(-300)
Traceback (chiamata più recente in basso):
...
ValueError: Impossibile impostare la temperatura sotto -273

Questa aggiornamento ha implementato con successo la nuova limitazione. Non siamo più autorizzati a impostare la temperatura sotto -273.

Attenzione, le variabili private non esistono in Python. Basta seguire alcune convenzioni. Il linguaggio stesso non ha alcuna limitazione.

>>> c._temperature = -300
>>> c.get_temperature()
-300

Ma non è un grande problema. Il problema più grande di questo aggiornamento è che tutti i client che hanno implementato la classe precedente devono modificare il loro codice da obj.temperature a obj.get_temperature() e tutte le assegnazioni (ad esempio obj.temperature val viene modificato in obj.set_temperature(val).

Questa ristrutturazione potrebbe causare problemi a decine di migliaia di linee di codice per i clienti.

In sintesi, la nostra nuova aggiornamento non è retrocompatibile. Questo è il punto in cui @property entra in gioco.

La forza di @property

Python gestisce questo problema utilizzando property. Possiamo realizzarlo in questo modo.

class Celsius:
    def __init__(self, temperature = 0):
        self.temperature = temperature
    def to_fahrenheit(self):
        return (self.temperature * 1.8) + 32
    def get_temperature(self):
        print("Valore ottenuto")
        return self._temperature
    def set_temperature(self, value):
        if value < -273:
            raise ValueError("-273 gradi sotto zero non è possibile")
        print("Valore impostato")
        self._temperature = value
    temperature = property(get_temperature, set_temperature)

E una volta eseguito, nel shell viene emesso il seguente codice.

>>> c = Celsius()

Abbiamo aggiunto la funzione print() in get temperature() e set temperature() per osservare chiaramente la loro esecuzione.

L'ultima riga del codice crea un oggetto property chiamato temperature. In breve, l'attributo aggiunge alcune funzioni di codice (get_temperature e set_temperature) all'accesso alle proprietà membro (temperature).

任何检索温度值的代码都将自动调用get_temperature()而不是字典(__dict__)查找。 同样,任何为温度分配值的代码都会自动调用set_temperature()。 这是Python中的一项很酷的功能。

我们可以在上面看到即使创建对象时也会调用set_temperature()。

你能猜出为什么吗?

原因是创建对象时,将调用__init__()方法。 此方法的行是self.temperature = temperature。 此分配自动称为set_temperature()。

>>> c.temperature
Getting value
0

同样,任何访问如c.temperature都会自动调用get_temperature()。 这就是属性的作用。 这里还有一些实例。

>>> c.temperature = 37
Setting value
>>> c.to_fahrenheit()
Getting value
98.60000000000001

通过使用属性,我们可以看到,我们修改了类并实现了值约束,而无需更改客户端代码。因此,我们的实现是向后兼容的。

最后请注意,实际温度值存储在私有变量_temperature中。 temperature属性是一个属性对象,它提供了与此私有变量的接口。

深入了解property

在Python中,property()是一个内置函数,用于创建并返回属性对象。该函数的签名是

property(fget=None, fset=None, fdel=None, doc=None)

其中,fget为获取属性值的函数,fset为设置属性值的函数,fdel为删除属性的函数,doc为字符串(如注释)。从实现中可以看出,这些函数参数是可选的。因此,可以简单地按照以下方式创建属性对象。

>>> property()
<property object at 0x0000000003239B38>

属性对象有三个方法,getter()、setter()和deleter(),用于稍后指定fget、fset和fdel。这意味着

temperature = property(get_temperature, set_temperature)

Può anche essere decomposto

# Crea attributo vuoto
temperature = property()
# Imposta fget
temperature = temperature.getter(get_temperature)
# Imposta fset
temperature = temperature.setter(set_temperature)

Questi due segmenti di codice sono equivalenti.

ConoscenzaDecoratori in PythonI programmatori possono riconoscere che i costrutti sopra descritti possono essere implementati come decoratori.

Possiamo andare oltre, senza definire i nomi get_temperature, set_temperature, poiché non sono necessari e influenzano lo spazio dei nomi della classe. Per questo, abbiamo riutilizzato il nome temperature nella definizione delle funzioni getter e setter. È possibile farlo.

class Celsius:
    def __init__(self, temperature = 0):
        self._temperature = temperature
    def to_fahrenheit(self):
        return (self.temperature * 1.8) + 32
    @property
    def temperature(self):
        print("Valore ottenuto")
        return self._temperature
    @temperature.setter
    def temperature(self, value):
        if value < -273:
            raise ValueError("-273 gradi sotto zero non è possibile")
        print("Valore impostato")
        self._temperature = value

Il metodo di implementazione sopra descritto è un metodo semplice e raccomandato per creare attributi. Durante la ricerca di attributi in Python, è probabile che si incontrino costrutti di questo tipo.

Bene, è tutto per oggi.