Поиск…


Простой дескриптор

Существует два разных типа дескрипторов. Дескрипторы данных определяются как объекты, которые определяют как метод __get__() и __set__() , тогда как дескрипторы без данных определяют только метод __get__() . Это различие важно при рассмотрении переопределений и пространства имен словаря экземпляра. Если дескриптор данных и запись в словаре экземпляра имеют одно и то же имя, дескриптор данных будет иметь приоритет. Однако, если вместо этого дескриптор не данных и запись в словаре экземпляра имеют одно и то же имя, запись словаря экземпляра будет иметь приоритет.

Чтобы создать дескриптор данных только для чтения, определите как get (), так и set () с помощью set (), вызывающего AttributeError при вызове. Определить метод set () с помощью заполнителя исключения, достаточного для того, чтобы сделать его дескриптором данных.

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

Приведенный пример:

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

Двусторонние конверсии

Объекты дескриптора могут позволить связанным объектным атрибутам реагировать на изменения автоматически.

Предположим, мы хотим смоделировать осциллятор с заданной частотой (в герцах) и периоде (в секундах). Когда мы обновляем частоту, которую хотим обновить, и когда мы обновляем период, мы хотим, чтобы частота обновлялась:

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

Мы выбираем одно из значений (частота, в Hertz) как «якорь», то есть тот, который можно установить без преобразования, и написать для него класс дескриптора:

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

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

«Другое» значение (период, в секундах) определяется в терминах якоря. Мы пишем класс дескриптора, который делает наши преобразования:

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)

Теперь мы можем написать класс осциллятора:

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
Лицензировано согласно CC BY-SA 3.0
Не связан с Stack Overflow