Python Language
Descriptor
Buscar..
Descriptor simple
Hay dos tipos diferentes de descriptores. Los descriptores de datos se definen como objetos que definen tanto un __get__()
como un __set__()
, mientras que los descriptores que no son de datos solo definen un __get__()
. Esta distinción es importante cuando se consideran las sustituciones y el espacio de nombres del diccionario de una instancia. Si un descriptor de datos y una entrada en el diccionario de una instancia comparten el mismo nombre, el descriptor de datos tendrá prioridad. Sin embargo, si en cambio un descriptor que no es de datos y una entrada en el diccionario de una instancia comparten el mismo nombre, la entrada del diccionario de la instancia tendrá prioridad.
Para hacer un descriptor de datos de solo lectura, defina tanto get () como set () con el conjunto () generando un AttributeError cuando se le llama. Definir el método set () con un marcador de posición de aumento de excepción es suficiente para convertirlo en un descriptor de datos.
descr.__get__(self, obj, type=None) --> value
descr.__set__(self, obj, value) --> None
descr.__delete__(self, obj) --> None
Un ejemplo implementado:
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
Conversiones bidireccionales
Los objetos descriptores pueden permitir que los atributos de los objetos relacionados reaccionen a los cambios automáticamente.
Supongamos que queremos modelar un oscilador con una frecuencia determinada (en hercios) y un período (en segundos). Cuando actualizamos la frecuencia, queremos que el período se actualice, y cuando actualizamos el período, queremos que la frecuencia se actualice:
>>> 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
Seleccionamos uno de los valores (frecuencia, en Hertz) como el "ancla", es decir, el que se puede establecer sin conversión, y escribimos una clase de descriptor para él:
class Hertz(object):
def __get__(self, instance, owner):
return self.value
def __set__(self, instance, value):
self.value = float(value)
El "otro" valor (período, en segundos) se define en términos del ancla. Escribimos una clase de descriptor que hace nuestras conversiones:
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)
Ahora podemos escribir la clase Oscilador:
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