Дескрипторы в Python
Простой дескриптор
Есть два разных типа дескрипторов. Дескрипторы данных определяются как объекты , которые определяют одновременно __get__()
и __set__()
метод, тогда как дескрипторы без данных определить только __get__()
метод. Это различие важно при рассмотрении переопределений и пространства имен словаря экземпляра. Если дескриптор данных и запись в словаре экземпляра имеют одно и то же имя, дескриптор данных будет иметь приоритет. Однако если вместо дескриптора не данных и записи в словаре экземпляра используется одно и то же имя, запись словаря экземпляра будет иметь приоритет.
Для того, чтобы дескриптор данных только для чтения, определяют как получить () и установить () с множеством () поднимая AttributeError при вызове. Определение метода набора () с исключением повышающего заполнителем достаточно , чтобы сделать его дескриптор данных.
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
Мы выбираем одно из значений (частота в герцах) в качестве «якоря», то есть того, которое можно установить без преобразования, и записываем для него класс дескриптора:
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)
Теперь мы можем написать класс 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