Recherche…


Descripteur simple

Il existe deux types de descripteurs différents. Les descripteurs de données sont définis comme des objets définissant à la fois une __get__() et une __set__() , tandis que les descripteurs autres que des données définissent uniquement une __get__() . Cette distinction est importante lorsque l’on considère les substitutions et l’espace de noms du dictionnaire d’une instance. Si un descripteur de données et une entrée du dictionnaire d'une instance partagent le même nom, le descripteur de données aura la priorité. Cependant, si à la place un descripteur de non-données et une entrée du dictionnaire d'une instance partagent le même nom, l'entrée du dictionnaire d'instance est prioritaire.

Pour créer un descripteur de données en lecture seule, définissez à la fois get () et set () avec set () en levant un AttributeError lorsqu'il est appelé. Définir la méthode set () avec un espace réservé de levée d'exception suffit pour en faire un descripteur de données.

descr.__get__(self, obj, type=None) --> value
descr.__set__(self, obj, value) --> None
descr.__delete__(self, obj) --> None

Un exemple implémenté:

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

Conversions bidirectionnelles

Les objets descripteurs peuvent permettre aux attributs d'objet associés de réagir automatiquement aux modifications.

Supposons que nous voulions modéliser un oscillateur avec une fréquence donnée (en Hertz) et une période (en secondes). Lorsque nous mettons à jour la fréquence à laquelle nous souhaitons mettre à jour la période et que nous mettons à jour la période, nous voulons que la fréquence soit mise à jour:

 >>> 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

Nous sélectionnons l'une des valeurs (fréquence, en Hertz) comme "ancre", c'est-à-dire celle qui peut être définie sans conversion, et écrivons une classe de descripteur pour cela:

class Hertz(object):
    def __get__(self, instance, owner):
        return self.value

    def __set__(self, instance, value):
        self.value = float(value)

La valeur "autre" (point, en secondes) est définie en fonction de l'ancre. Nous écrivons une classe de descripteur qui effectue nos conversions:

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)

Nous pouvons maintenant écrire 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


Modified text is an extract of the original Stack Overflow Documentation
Sous licence CC BY-SA 3.0
Non affilié à Stack Overflow