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


Modified text is an extract of the original Stack Overflow Documentation
Licenciado bajo CC BY-SA 3.0
No afiliado a Stack Overflow