Использование дескрипторов Python: как лучше написать общий метод получения-проверки
И самое лучшее, я имею в виду как Python, так и с как можно меньшим количеством строк кода.
Я привык определять классы с атрибутами в Python, вот так.
class MyClass(object):
def __init__(self):
self.value = 5
Однако часто мне бывает необходимо обеспечить self.value
может принимать только определенный тип значения. В прошлом (исходя из фона PHP OOP) я делал это с помощью метода getter-setter:
def value(self, new_value = -1)
if new_value == -1:
return self.value
if isinstance(new_value, int) and new_value > -1:
self.value = new_value
else:
raise TypeError
Я тогда доступ self.value
с self.value()
и установите его с self.value(67)
, Обычно я буду называть по имени self.value
в self.__value
препятствовать его прямому доступу.
Я недавно обнаружил (или нахожусь в процессе обнаружения) дескрипторов. После внимательного обсуждения этой темы я написал что-то похожее на это:
class ValidatedAttribute(object):
def __init__(self, key, kind):
self.key = key
self.kind = kind
def __get__(self, instance, owner):
if self.key not in instance.__dict__:
raise AttributeError, self.key
return instance.__dict__[self.key]
def __set__(self, instance, value):
if not isinstance(value, self.kind):
raise TypeError, self.kind
instance.__dict__[self.key] = value
class MyUsefulClass(object):
a = ValidatedAttribute("a", int)
Но у меня есть несколько вопросов!
Во-первых, что именно MyUsefulClass().a
? Является ли это экземпляром класса ValidatedAttribute, каким-то образом связанным с экземпляром MyUsefulClass
учебный класс?
А во-вторых, то, что я действительно хотел бы, это что-то вроде
class MyUsefulClass(object):
def __init__(self):
self.value = Checker(int, 99)
def Checker(self, kind, default):
# Code for verifying that associated attribute is of type kind.
и каким-то образом связать атрибут доступа к какому-либо методу перехвата, без необходимости использовать целый другой класс дескриптора.
Извините, если у меня нет понимания - я все еще начинаю понимать классы Python нового стиля. Любая помощь или указатели в правильном направлении будет высоко ценится.
5 ответов
Во-первых, что такое MyUsefulClass(). A? Является ли экземпляр класса ValidatedAttribute каким-либо образом связанным с экземпляром класса MyUsefulClass?
Да, именно так.
И во-вторых, что мне действительно нравится, так это что-то вроде [...] и каким-то образом привязывать доступ к атрибуту к какому-либо методу перехвата, без необходимости использовать целый другой класс дескриптора.
Ну, вы могли бы перезаписать __getattribute__
, поскольку он перехватывает каждый поиск атрибутов, но это намного сложнее, чем использование дескрипторов.
Дескрипторы работают именно для того, что вы хотите сделать (автоматическая проверка). Единственное улучшение, которое вы можете сделать, - это также использовать метакласс, чтобы автоматически устанавливать ключ для вас, и тогда ваш код класса будет выглядеть так:
class MyUsefulClass(object):
a = ValidatedAttribute(int)
Но сначала освоитесь с дескрипторами, метаклассы сложны и могут быть непростыми.
О, и одно маленькое изменение, которое я хотел бы сделать для вас ValidatedAttribute
:
def __init__(self, key, default):
self.key = key
self.kind = type(default)
self.default = default
def __get__(self, instance, owner):
if self.key not in instance.__dict__:
return self.default
return instance.__dict__[self.key]
Вместо того, чтобы писать этот материал самостоятельно, используйте библиотеку признаков: https://pypi.python.org/pypi/traits. Использование библиотек - это питонный подход к большинству проблем.
Однако, как правило, вы должны написать достаточно общий код, чтобы такая проверка не требовалась. Это легко сделать в Python.
Пожалуйста, пожалуйста, пожалуйста, не делайте isinstance(new_value, int)
,
Вы могли бы сойти с рук
from numbers import Integral
isinstance(new_value, Integral)
но это совершенно анафема для утки.
Во-первых, что такое MyUsefulClass(). A? Является ли экземпляр класса ValidatedAttribute каким-либо образом связанным с экземпляром класса MyUsefulClass?
Чтобы найти MyUsefulClass().a
Питон спрашивает класс. Класс сообщает ему, что у него есть getter-setter для атрибута и говорит Python запросить атрибут. Атрибут говорит, что находит его, а затем сообщает классу, что это за атрибут. Атрибут не является установщиком-получателем.
MyUsefulClass.a
является геттер-установщик (свойство), а не MyUsefulClass().a
,
И во-вторых, что мне действительно нравится, так это что-то вроде [материала]
AFAIK, вы можете устанавливать свойства только для класса, а не для экземпляров, потому что как вы узнаете, хотите ли вы получить свойство или элемент, за которым скрывается свойство?
Я думаю, что вы ищете свойства Python. Правильный подход к проверке атрибутов экземпляра класса, кажется, имеет отношение к вашей проблеме. Вы также можете посмотреть на Python дескриптор против свойства
Похоже, вы хотите использовать свойства
class MyUsefulClass(object):
def __init__(self):
self._value = 0
@property
def value(self):
return self._value
@value.setter
def value(self, value):
self._value = self.Checker(value, int, 99)
def Checker(self, value, kind, default):
return value if isinstance(value, kind) else default