Python Language
descrittore
Ricerca…
Descrittore semplice
Esistono due diversi tipi di descrittori. I descrittori di dati sono definiti come oggetti che definiscono sia un __get__()
sia un __set__()
, mentre i descrittori non di dati definiscono solo un __get__()
. Questa distinzione è importante quando si considerano le sostituzioni e lo spazio dei nomi del dizionario di un'istanza. Se un descrittore di dati e una voce nel dizionario di un'istanza condividono lo stesso nome, il descrittore di dati avrà la precedenza. Tuttavia, se invece un descrittore non di dati e una voce nel dizionario di un'istanza condividono lo stesso nome, la voce del dizionario dell'istanza avrà la precedenza.
Per creare un descrittore di dati di sola lettura, definisci sia get () che set () con set () generando un AttributeError quando chiamato. Definire il metodo set () con un segnaposto che genera un'eccezione è sufficiente per renderlo un descrittore di dati.
descr.__get__(self, obj, type=None) --> value
descr.__set__(self, obj, value) --> None
descr.__delete__(self, obj) --> None
Un esempio implementato:
class DescPrinter(object):
"""A data descriptor that logs activity."""
_val = 7
def __get__(self, obj, objtype=None):
print('Getting ...')
return self._val
def __set__(self, obj, val):
print('Setting', val)
self._val = val
def __delete__(self, obj):
print('Deleting ...')
del self._val
class Foo():
x = DescPrinter()
i = Foo()
i.x
# Getting ...
# 7
i.x = 100
# Setting 100
i.x
# Getting ...
# 100
del i.x
# Deleting ...
i.x
# Getting ...
# 7
Conversioni bidirezionali
Gli oggetti descrittori possono consentire agli attributi degli oggetti correlati di reagire automaticamente alle modifiche.
Supponiamo di voler modellare un oscillatore con una determinata frequenza (in Hertz) e un periodo (in secondi). Quando aggiorniamo la frequenza, vogliamo che il periodo si aggiorni e quando aggiorniamo il periodo vogliamo che la frequenza si aggiorni:
>>> oscillator = Oscillator(freq=100.0) # Set frequency to 100.0 Hz
>>> oscillator.period # Period is 1 / frequency, i.e. 0.01 seconds
0.01
>>> oscillator.period = 0.02 # Set period to 0.02 seconds
>>> oscillator.freq # The frequency is automatically adjusted
50.0
>>> oscillator.freq = 200.0 # Set the frequency to 200.0 Hz
>>> oscillator.period # The period is automatically adjusted
0.005
Selezioniamo uno dei valori (frequenza, in Hertz) come "ancora", cioè quello che può essere impostato senza conversione e scrivere per esso una classe descrittore:
class Hertz(object):
def __get__(self, instance, owner):
return self.value
def __set__(self, instance, value):
self.value = float(value)
Il valore "altro" (periodo, in secondi) è definito in termini di ancoraggio. Scriviamo una classe descrittore che esegue le nostre conversioni:
class Second(object):
def __get__(self, instance, owner):
# When reading period, convert from frequency
return 1 / instance.freq
def __set__(self, instance, value):
# When setting period, update the frequency
instance.freq = 1 / float(value)
Ora possiamo scrivere la classe Oscillator:
class Oscillator(object):
period = Second() # Set the other value as a class attribute
def __init__(self, freq):
self.freq = Hertz() # Set the anchor value as an instance attribute
self.freq = freq # Assign the passed value - self.period will be adjusted