Использование дескрипторов 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
Другие вопросы по тегам